つい昨日(日本時間では今日)、TechEmpowerが行っているWebアプリケーションのベンチマーク結果が更新されました。
なんと、Round 18ではRustのフレームワーク「actix-web」が2位以下に大差をつけて圧勝していたにも関わらず、Round 19ではさらにそれを上回るライブラリが出現しました。
その名もdrogon!
名前からして素晴らしいフレームワークの予感がしますが、Rustのactix-webとはどのような点で異なり、またどのようにしてこの速度を達成したのか気になったので調べてみました。
フルスタックのライブラリ
drogonはフルスタックのWebアプリケーションフレームワークとして実装されています。つまり、(actix-webとは違い)データベースとのやり取りを行うAPIやJSON, ORM, HMRなどさまざまな機能が盛り込まれています。
一方actix-webではWebサーバーを実装するための最低限の機能に関するAPIしか用意されておらず、データベースとのやり取りなどを行う際には外部ライブラリを読み込み、ライブラリ間の橋渡しは自分で実装しなければなりません。これは個人的な推測ですが、actix-webを用いた場合、データベースとのやり取りを行う際に外部ライブラリのAPIとの橋渡しを行うため、Query Builderの事前最適化ができなかったり、あるいはメモリを動的に確保・コピーを行う必要が出てくるために、パフォーマンスの劣化を招いているのではないかと考えられます。
ただしフルスタックのライブラリの場合は、個々の機能に関するAPIが限られているため、少し込み入った処理を行おうとした際に泥沼にハマるケースもあったりするので、何でもかんでもフルスタックにすべきという考えも危険なのではないかと思われます。
コンパイル時ORM最適化
drogon_ctlというコマンドを使うと、事前にデータベースのテーブルの構造やテンプレートファイルなどを読み込んで、それに対応したC++のソースコードを出力することができます。これにより、テーブルの構造やテンプレートファイルの中身に対応した最適化を事前に行うことが可能となります。
例えばdrogon_ctl create model
というコマンドを使用すると、データベースの中のテーブルの構造をすべて読み込み、それぞれのテーブルに対応したModel Classが記述されたC++ソースを出力します。それぞれのモデルクラスは各テーブルの構造に合わせて最適化されているため、プロジェクトの中にこのソースを組み込んでコンパイルすれば、優れたパフォーマンスを達成することができるということになります。
FastDBClient
ごめんなさい、これは説明読んでもよく分かりませんでした。ただ、データベースのクライアントとNetwork IOでイベントループを共有することによって、毎回無駄なロックを省略することができ、パフォーマンスが10〜20%ほど向上するそうです。ほんとかなあ。
感想とか
コンパイル時にデータベースを読み込んでテーブルごとに最適化してしまうという発想は面白いと思いました。実際私が経験してきた現場では、サーバが動いている最中にテーブルの構造を変えるということがなかったので、このアイデアは様々な場面で有効なのではないかと思います。