Quarkusは素晴らしいパフォーマンスを発揮します。その新たな証拠があります
要約
Quarkusにとってパフォーマンスは重要ですが、公開されていたパフォーマンスに関するグラフィックは古く、重要な情報(私たちが素晴らしいスループットを持っているという事実など)が欠けていました。これを解決するために、私たちは透明性があり、再現可能で、パフォーマンスの全体像を測定する新しいベンチマークを作成しました。私たちの実験では、QuarkusはSpring Bootよりも1秒あたり2.7倍多くのトランザクションを処理でき、2.3倍速く起動し、メモリは半分しか消費しません。
物語の始まり
もし quarkus.io サイトを訪れたことがあるなら(もちろん、ここにいるのだから訪れたことはあるでしょう)、フロントページの下部にあるパフォーマンスチャートに気づいたかもしれません。Quarkusチームは私たちのパフォーマンスを誇りに思っています。なぜなら、良いパフォーマンスは高いスケーラビリティ、低レイテンシー、低リソース使用量、低コスト、そしてより良い持続可能性を意味するからです。基本的には、パフォーマンスが重要です。以前に示していたのは次のとおりです。
しかし、パフォーマンスの重要性にもかかわらず、私たちのパフォーマンスチャートにはいくつかの問題がありました。第一に、数字が古いです。どれくらい古いか?それは、チャートに日付がないため、判断が難しいです。実際、その数字がどのように測定されたかについての情報は一切なく、ベンチマークのソースコードへのリンクもありません。これは、誰も再現できないことを意味し、誰も検証できないことを意味します。再現できなければ、信頼できません。
なぜベンチマークのソースコードにリンクしなかったのでしょうか?歴史的に、私たちは比較対象について意図的に曖昧にしてきました(マナーの問題!)。ソースコードを共有すれば、他のフレームワークが何であるかが完全に明らかになっていたでしょう。ベンチマークのソース共有を妨げるだけでなく、他のフレームワークを匿名化することにはそれ自体の問題がありました。フレームワーク名を共有しないことは丁寧ですが、読者に情報に基づいたフレームワークの選択をするための有用な情報を提供しないことになります。Quarkusの方が優れているのか?ああ、はい、間違いなく。何よりも優れているのか?シーッ、それは秘密です。
しかし、私の見解では、古いグラフィックにはさらに大きな問題があります。Quarkusが高速に起動し、フットプリントが小さいことを示していますが、スループットについては何も示していません。スループットとメモリフットプリントまたは起動時間の間に、典型的なパフォーマンスのトレードオフがあります。チャートを見た人々が、Quarkusがスループットの数値を示さないなら、それはひどいに違いないと考えるのは簡単でしょう?間違いです!Quarkusには トレードオフはありません 。Quarkusはあらゆる面で代替品よりも効率的です。(トレードオフがどこで発生するか知りたいですか? 読み進めてください。)
しかし、Quarkusのスループットが悪いという誤解はあちこちで発生しています。外部のブログで何度も出てきますし、お気に入りのAIサービスにQuarkusの利点について尋ねても、スループットに言及する可能性は低いです。その代わりに、起動時間やメモリに焦点を当てるでしょう。それらも重要ですが、スループットも同様に重要です。
私たちのチャートには、Quarkusに関する誤解の一因となっている、もう一つのより微妙な省略があります。私たちはQuarkusのネイティブモードでのパフォーマンスを示していますが、他のフレームワークのネイティブモードでのパフォーマンスは示していません。一部の人々は、これがQuarkusはネイティブモードがすべてであり、Quarkusを使用するならネイティブモードを使用すべきだと解釈します。Quarkusをネイティブモードで、他のフレームワークをJVMモードで比較するブログをよく見かけますが、これは単に愚かです。QuarkusをJVMモードで使う場合は他のフレームワークをJVMモードで使う場合と比較すべきであり、Quarkusをネイティブモードで使う場合は他のフレームワークをネイティブモードで使う場合と比較すべきです。Quarkusはネイティブモードに非常に優れていますが、ネイティブモードを使用するかどうかは、どのフレームワークを選択するかとは直交する選択肢です。
これらすべての問題のため、Quarkusコミュニティの何人かのメンバーは、会議での発表やデモで使用するために独自のベンチマークを作成してきました。実際、私もそうしました。同じチームのメンバーが似たような作業をやり直すのは無駄です。さらに、ベンチマークは難しいのです!数字を得ること自体は非常に簡単ですが、自分が測定していると思っているものを実際に測定している数字を得るのは難しいです。本当に正しく行うには、パフォーマンスの専門家が持っているようなスキルが必要です。
よし、だから私たちは間違いなく新しいベンチマークが必要でした
何かをする必要がありました。幸いなことに、私の同僚であるEric Deandreaは、この正確な問題に数年間取り組んでいました。Ericは、以前John O’Haraが作成したベンチマークに基づいて、制御された条件下でパフォーマンスラボで測定を実行できる一連の自動化を構築していました。
Ericと私は 新しいリポジトリ を立ち上げ、Ericのベンチマークコードをそこに移動させ、自動化を改良して、結果をラボのファイアウォールを越えてデータストアとして機能する 別のリポジトリ にプッシュするようにしました。私たちは数値を取得し、( Apache Batik を使用した少しの作業の後) 図も手に入れました!Quarkusのスループットがいかに素晴らしいかに注目してください。Quarkusは、Spring Bootと比較して、1秒あたり2.7倍(19255対7238 tps)多くのトランザクションを処理でき、起動も2.3倍(2.919秒対6.569秒)高速であり、すべて半分のメモリ(583MBではなく269MB)で実現します。
ここからが本当の作業の始まりでした。ベンチマークがオープンソース化されたことで、以前にはなかった方法で厳密な精査にさらされるようになりました。(もちろん、これがオープンソースの魔法の一部です。)Quarkusのパフォーマンス専門家の一人であるFrancesco Nigroは、測定をより堅牢にできるいくつかの領域を発見しました。例えば、私たちのセットアップでは、cgroupsとcpusetを使用してプロセスを特定のCPUコアに固定していました。これは正しいアプローチですが、テスト対象のアプリケーションと負荷ジェネレーターの両方が同じcgroupに集まり、同じコアを奪い合っていました。負荷ジェネレーターが16スレッドを使用するように設定されていたため、これが測定にかなりのノイズをもたらしました。 個別のコア割り当て に切り替えることでこれは解決しましたが、データベースが専用のコアを持たないことや、誤ってホスト上のすべてのプロセスに影響を与えていたキャッシュドロップのステップなど、他の干渉源は残っていました。適切な分離が確立されると、スループットの結果は微妙に変化しました。修正前は、負荷が低いことで他のプロセスにより多くの余裕が生まれていたため、遅いフレームワークも競争力があるように見えました。彼らは、自分たちが引き起こす競合が少ないという点で過大評価されていたのです。上記のチャートは、改善されたセットアップを反映しています。これこそが、ベンチマークにはスキルが必要だと私が先に言ったことです。これらの間違いは簡単に起こり、検出が微妙なのです。
しかし、最も大きな助けはQuarkusコミュニティの外から来ました。ベンチマークの意図は、通常のユーザーの体験を再現することであり、各フレームワークを極限までチューニングすることではありませんでした。(そのためには TechEmpower があります。)「初期設定(out of the box)」のパフォーマンスを測定することが最善の道だと考えました。その理由の一つは、ほとんどの人がデフォルトで経験するパフォーマンスだからであり、もう一つはそれが最も公平だったからです。公平性は重要な目標でした。そうでなければ、比較する意味がありません。私たちのチームはQuarkusアプリケーションを非常に鋭いパフォーマンスにチューニングするスキルを持っていますが、私たちのほとんどはSpringに対して同じスキルを持っていません。それは私たちの職務記述書にはないのです。QuarkusだけをチューニングしてSpringをチューニングしないのは明らかに公平 ではない でしょう。
しかし、最初の結果を公開し始めてから、Spring Boot を日常的に使用している人々から連絡がありました。彼らは、両フレームワークが open-session-in-view 設定と接続プールサイズの処理において違いがある と指摘しました。これらの違いは十分に大きく、初期設定の動作を比較することが、実際に 最も公平な選択肢であったか どうかを評価し始めました。open-session-in-view 設定と N+1 問題の修正は数値に大きな違いをもたらしませんでしたが、接続プールサイズの調整は違いをもたらしました。デフォルト設定では、Spring アプリケーションは深刻な接続エラーに悩まされていました。クライアントが接続できない場合、スループットはゼロになるため、エラーがスループットを低下させていました。Eric、Francesco、Sanne Grinovero は、Spring アプリケーションがエラーなしで負荷を処理できるように、ログを詳細に調査し、プロファイリングを行って設定の微調整を見つけ出すために多くの時間を費やしました。
私たちのSpringの友人たちは、このような調整は完全に標準的なものだと教えてくれました。私たちは依然として初期設定のパフォーマンスを測定したいと考えていたため、妥協案に落ち着きました。初期設定バージョンと、軽くチューニングされたバージョンの両方を測定しました。フロントページに表示しているグラフィックでは、チューニングされたバージョンを表示しています。以下に、同じコードレベルとスクリプトを使用した、上記の初期設定の結果に相当するチューニング版を示します。
チューニングの効果は、全体的にかなり控えめだったことがわかります。また、スループットとメモリフットプリントの間には多少のトレードオフがあったことにも注目してください。どちらのフレームワークでも、チューニングによる最適化は、速度向上のために一部のメモリを犠牲にすることになりました。
基本原則
チューニングの問題をどのように扱うかを決めようとする中で、私たちはこの取り組み全体の基本原則に立ち返ることになりました。それらは次のとおりです。
同等性
Spring版とQuarkus版のアプリケーションのコードは、同じ機能を実行するために可能な限り同等であるべきです。これは、ドメインモデルが同一であり、基盤となる永続化メカニズムが同一であるべきであることを意味します(私たちのケースでは、Hibernateを使用するJPA)。パフォーマンスの違いは、フレームワーク自体のアーキテクチャの違いとライブラリ統合の最適化から生じるべきです。アプリケーションのアーキテクチャを変更する変更(つまり、ブロッキングをリアクティブに移行する、仮想スレッドを使用するなど)が行われる場合、これらの変更はアプリケーションのすべてのバージョンに適用されるべきです。
パフォーマンスの探求
この作業の素晴らしい点の一つは、「Quarkusには素晴らしいパフォーマンスがあるのか?」という問いを超えて、パフォーマンスに関する様々な疑問を探求できるようになったことです。例えば、仮想スレッドで実行すると結果に影響があるでしょうか?はい!すべてのフレームワークで、仮想スレッドは約6,000トランザクション/秒を追加しました。
そして、他にも疑問がありました。Spring Boot 3とSpring Boot 4の間でパフォーマンスに違いはあったのでしょうか?いくつかの点で改善され、いくつかの点で悪化したことが判明しました。Spring 4はSpring 3よりも高いスループットを提供しますが、最初の応答までの時間が遅くなり、メモリフットプリントも増加するという犠牲を伴います。
Quarkusの新しい AOTパッケージング は起動時間にどのような影響を与えるでしょうか?(これについてはまだ作業中ですが、その答えを見るのが楽しみです。)
スループットと起動時間のトレードオフ、再訪
Quarkusでは、起動時間とパフォーマンスの間にトレードオフがないと述べました。他のフレームワークと比較した場合、これは確かに真実です。ビルド時の原則のようなQuarkusの内部効率は、起動時間 と スループットの両方を改善します。しかし、JVM上のQuarkusをネイティブのQuarkusと比較すると、トレードオフが再び現れます!GraalVMを使用してQuarkusアプリケーションをネイティブにコンパイルすると、Quarkusは電球よりも速く起動し、非常に小さなメモリフットプリントを持っています。しかし、スループットのペナルティ はあります 。ネイティブにするとスループットは半分になります。(Spring Bootの場合、ネイティブのペナルティは同様です。)ほとんどのアプリケーションにとって、このトレードオフは価値がありません。特に、より長いビルド時間やネイティブの追加の制約と組み合わせるとそうです。しかし、時にはトレードオフに価値がある場合もあります。アプリケーションが非常に頻繁に起動および停止される場合や、非常に低いワークロードの場合は、ネイティブを使用してください。
自宅でベンチマークを試してみたいですか?
可能です!すべてのソースコードとスクリプトは https://github.com/quarkusio/spring-quarkus-perf-comparison で利用できます。誰もが簡単に再現できることは、ベンチマークを設計する上で重要な基本原則でしたが、少々複雑になります。パフォーマンスの世界では、「厳密」と「簡単」は同じ文脈では使用されません。難しいことを常にアクセスしやすくしたい私としては、これは 本当に 歯がゆいです。結局、私たちは妥協点に落ち着きました。もし手元にパフォーマンスラボがあるなら、もちろん理想的です。すべてのスクリプトが自分のハードウェアでジョブを実行できるように利用可能です。そのようなセットアップがなくても、Linuxマシンがある場合は、私たちが使用する専門家が承認したスクリプトを使用できます。内部的には、それらはオーケストレーションに qDup のようなツールを使用し、協調的な省略のリスクなしに負荷を駆動し、プロセスの分離を確実にするために Hyperfoil を使用しています。
しかし、 本当に 簡単なものが欲しい場合はどうでしょう?この時点から、物事は難しくなります。私たちは、シンプルさを追求したスクリプトの第2バージョンを作成しました。これらは馴染みのあるツールのみを使用し、プロセス分離は試みません。そのため、MacとLinuxの両方、そして適切なターミナルがあればWindowsでも実行できます。ただし、結果は注意して扱う必要があります。ラップトップの電源管理はあらゆる種類の奇妙な影響を引き起こす可能性があり、負荷が多すぎたり少なすぎたりすると、QuarkusやSpringとは関係のないボトルネックを測定してしまう可能性があります。最も一般的な問題を回避する方法や、測定しているものが自分が考えているものかどうかを判断する方法について、別のブログ(または6つ!)を計画しています。