Day 14: fast-http
これは fukamachi products advent calendar 2016 の14日目の記事です。
今日はfast-httpについて話します。埋め込みツイートが多いですが手抜きではありません。
Common Lispは高速か
Common Lispは高速だという話を界隈ではよく聞きます。噂によればC++やCよりも高速なプログラムを書くことができるとさえ言われています。
Cで書くコードの方がCommon Lispで書くより速いって人がいたら、それは彼のCの技量が高すぎるってことだね。
“If you can't outperform C in CL, you're too good at C.” — Eric Naggum
昨日紹介したwebsocket-driverを書いたとき、好奇心からCommon Lispの実行速度というのはどれほどのものだろうと計測してみました。比較対象はNode.jsのwebsocketモジュールです。
勝負はあっけなく終わりました。書き上げてとりあえずベンチマークを取ってみようという段階で、僕が書いたCommon Lispの実装のほうが圧倒的に速かったのです。しかも、型宣言やoptimize宣言や関数のインライン化などもすればもっと速くなる余地がある状態でです。
これは当時の意気揚々としたツイートです。
Though I heard Node.js is fairly fast, My Common Lisp code is about 35 times faster without any declaims...
— fukamachi (@nitro_idiot) 2014年9月26日
高速に動くということの優位性
Common LispでWebアプリケーションが書けると言っても、じゃあ他と比べたときの優位性とはなんだろうという答えの一つがこれでした――Common Lispは速い。
これだけ抽象度の高い言語でありながらGoやC++と比べられるほどのパフォーマンスが出るのだから、これは強みになります。起業を考えていた自分はこの強みを活かして技術優位性としようと考えていました。
けれど、まだ道のりは始まったばかりです。
やっぱりCommon Lispは遅い?
河西くんとTwitterで会話するようになったのはこの頃でした。先程のツイートを見てリプライをくれています。
twitter.com@nitro_idiot Actually I tried to get benchmark s of Node.js HTTPserver and Wookie to get the result that Node.js is sadly faster.
— (rudolph-miller) (@Rudolph_Miller) 2014年9月26日
Wookie *1がNode.jsのhttpモジュールよりも遅い、というのです。まじかよ、と思って実際に計測してみると、Node.jsはWookieよりも2倍も高速でした。
twitter.comNode.js HTTP module is approximately 2 times faster than Clack and Wookie. https://t.co/ru1xkSwhRi
— fukamachi (@nitro_idiot) 2014年10月5日
Wookieを高速化する
Common Lisp自体はNode.jsより遅い言語ではないというのはwebsocket-driverで知れたことです。ということは、何か遅い原因があるはずです。僕はWookieのソースコードを見てどうにかNode.jsより高速にしてやろうと決めました。
Wookieをプロファイリングすると内部で使っているHTTPパーサーのhttp-parseがボトルネックとなっていることがわかりました。
twitter.comAlthough WOOKIE:READ-DATA is taking time most, it just 'funcall's a 'http-parse' parser. Functional style makes it harder to profile.
— fukamachi (@nitro_idiot) 2014年10月5日
中身を見てみると納得です。パーサー部分はナイーブに正規表現で行っていました。subseq
などの呼び出しも見られ、何度もメモリアロケーションがされているのがわかります。
このときの開発の状況はツイートによく残っています。http-parseのベンチマークを取って高速化してPull Requestを投げることを繰り返していると、じきにκeenも参加し始めました。
そして最終的にhttp-parseはWookieの最大のボトルネックではなくなりました。
でもまだ遅い
それでもWookieはまだNode.jsより少し遅い。そこで抜本的に改善するために一からHTTPパーサーを書きました。それが「fast-http」です。
twitter.comThough it's not completed yet, I published another library 'fast-http'. https://t.co/oyDBCeojG2 It's about 10 times faster than http-parse.
— fukamachi (@nitro_idiot) 2014年10月10日
リリース当時はhttp-parseの10倍高速でしたが、現在はさらに高速化されて120倍以上高速になっています。最終的にCで書かれたhttp-parser (Node.jsで使われているもの) よりも高速になったため、それをブログとしてまとめ、それなりの反響を得ました。
高速かつ抽象度の高いプログラム言語としてCommon Lispは十分に戦えそうだ、という実感を持った瞬間です。
上のエントリーからCommon Lisp界隈で「高速なプログラムを書こう」というムーブメントが起きたように思います。実際にCよりも速いコードが書けたぜ、っていうのはインパクトがあるのでしょうね。
でもまだまだまだまだ遅い
けれど、ここまでやってもWookieはNode.jsに勝てませんでした。
twitter.comHmm.. Wookie is still a little bit slower than Node.js. libevent2 might be a bottleneck of it?
— fukamachi (@nitro_idiot) 2014年10月10日
使っているイベントライブラリがlibevent2であることが問題なのだろうか?というツイートを残しており、Wookieの作者から「libuvバックエンド (Node.jsと同じもの) にするつもりだよ」とリプライがきています。
ここからまた面白くなるのですが、詳しくは明日のWooの回に取っておくとします。
おわりに
fast-httpはGitHubで公開されており、Starは252です。
明日のアドベントカレンダーは15日目のWooについてです。お楽しみに。
*1:Common Lispの非同期Webサーバー。当時はlibeventベース、現在はlibuvベース。