Boost.XpressiveとBoost.Spiritの速度比べ
ちょっとした構文解析なら Xpressive でも充分だけど、ちゃんとした(?)構文解析器の Spirit と比べてどれだけ性能の差があるのか調べてみた。
どうせ比較するなら実用的なもので、というわけで RFC 2616 やらを参考に HTTP のリクエストヘッダをパースさせてみた。
本当はパースしたヘッダの内容を取り込ませて比較したかったけど、時間の都合上、正しいヘッダかどうかの判断だけにした。
パースしたヘッダと、それぞれ 1 万回ずつパースしたときの結果は以下の通り。
計測に使用した PC のスペックは CPU: Core i5 750@2.67GHz、メモリ: 2GB、gcc: 4.4.3。
// 標準的なヘッダ std::string str("GET / HTTP/1.1\r\n" "Host:www.google.co.jp\r\n" "User-Agent:Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.3\r\n" "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" "Accept-Language:ja,en-us;q=0.7,en;q=0.3\r\n" "Accept-Encoding:gzip,deflate\r\n" "Accept-Charset:Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n" "Keep-Alive:115\r\n" "Connection:keep-alive\r\n" "Cookie:PREF=ID=2bdd9aa98503:TM=114720:LM=614720:S=dLlh6FV3n-Z9; NID=37=m9UB21ePGXjT0IalYJC1tiCxPCbi-DyromZQhesAx7HJaL9XjffosMP1wSNDNayh28seAwJfGi4PduwQbyxG9yDYoblORavoS4IWIfyGdP9ii\r\n" "\r\n"); // 無駄に長いヘッダ std::string str2("GET /aaa/bbb/cccc HTTP/1.1\r\n" "Allow: GET, HEAD, PUT\r\n" "Content-Encoding: gzip,deflate\r\n" "Content-Language: a,ab,abc,abcd,abcde,abcdef,abcdefg,abcdefgh\r\n" "Content-Length:3495\r\n" "Content-Location:http://www.w3.org/hypertext/DataSources/Overview.html\r\n" "Content-MD5:22CharsConsitOfAlnum+/==\r\n" "Content-Range:bytes 123-456/12345\r\n" "Content-Type:text/html;charset=ISO-8859-4\r\n" "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" "Last-Modified:Sat, 29 Oct 1994 19:43:31 GMT\r\n" "Accept: */*, type/type;attr=value;attr=\"value\";q=0.000;token;token=token;token=\"token\"\r\n" "Accept-Charset: *, **, iso-8859-5, unicode-1-1;q=0.8\r\n" "Accept-Encoding: gzip;q=1.0, identity;q=0.5, *;q=0\r\n" "Accept-Language: a,ab,abc,abcd,abcde,abcdef,abcdefg,abcdefgh\r\n" "Authorization:aaa bbb=\"ccc\r\n ,\r\n ddd=eee\"\r\n" "Expect:100-continue,token,token=token,token=\"token\",token=\"token\";token,token=\"token\";token=token,token=\"token\";token=\"token\"\r\n" "From:name<@doman,@domain:some.addr@subdomain.subdomain>\r\n" "Host:www.w3.org\r\n" "If-Match:\"tag\", W/\"tag\"\r\n" "If-Modified-Since:Tuesday, 15-Nov-94 08:12:31 GMT\r\n" "If-None-Match:\"tag\", W/\"tag\"\r\n" "If-Range:Tuesday, 15-Nov-94 08:12:31 GMT\r\n" "If-Unmodified-Since:Tuesday, 15-Nov-94 08:12:31 GMT\r\n" "Max-Forwards:123\r\n" "Proxy-Authorization:aaa bbb=\"ccc\r\n ,\r\n ddd=eee\"\r\n" "Range:bytes=123-, 123-456, -456\r\n" "Referer:scheme::/first/segment/can/include/colons?query\r\n" "TE: deflate;param=value, deflate;param=value;param=\"value\", deflate;param=value;q=0.123;token;token=value;token=\"value\"\r\n" "User-Agent: CERN-LineMode/2.15 libwww/2.17b3\r\n" "Cache-Control: public, private=\"field,field\", no-cache=\"field, field\", no-store, no-transform, must-revalidate, proxy-revalidate, s-max-age=12345\r\n" "Connection: close, token\r\n" "Date:Tuesday, 15-Nov-94 08:12:31 GMT\r\n" "Pragma: no-cache, extension-pragma, extension-pragma=token, extension-pragma=\"value\"\r\n" "Trailer: field-name, field-name\r\n" "Transfer-Encoding: chunked, transfer-extension, transfer-extension;attr=value, transfer-extension;attr=\"value\";attr=\"value\"\r\n" "Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11\r\n" "Via: protocol-version host, protocol-name/protocol-version host:1234 (somecomment)\r\n" "Warning: 123 some-host \"warn-text\", 000 some-host:1234 \"warn-text: warn\" \"Sun, 06 Nov 1994 08:49:37 GMT\"\r\n" "\r\n"); // 不正なヘッダ std::string str3(1024, 'a');
// 標準的なヘッダ spirit: 0.31 sec xpressive: 3.11 sec // 無駄に長いヘッダ spirit: 0.89 sec xpressive: 8.91 sec // 不正なヘッダ spirit: 0.1 sec xpressive: 1.51 sec
厳密な計測ではないけど、やはりちゃんとした構文解析器なだけあって Spirit の方が速い。
ただ、Spirit は文法の定義にちょっとした工夫が必要だったりする*1ので、必ずしも Spirit を使うのが正解とはいえないかもしれない。
今回検証に使用したコードは Github に上げておいたので参考にどうぞ。
オマケ: コンパイル時間
Boost.Xpressive real 0m43.009s user 0m42.559s sys 0m0.428s Boost.Spirit real 1m20.814s user 1m20.373s sys 0m0.748s
これは Xpressive に軍配が上がったとはいえ、どちらも再コンパイルは極力避けた方が良さそうだ。
*1:Spirit の場合、qi::rule