We’re sorry we missed you at re:Invent, but we can still meet!

衝撃的なほどシンプル: MomentoのPythonキャッシュクライアントのチューニング

PythonのgRPCクライアントをどのように最適化したか

Chris Price headshot
Chris Price
Author

Share

この記事は、Momentoが開発者の体験をどのように考えているかを詳しく説明するシリーズの一部です。Momentoのサーバーレスキャッシュを使い始めるのは驚くほど簡単ですが、それは物語の半分に過ぎません。

楽しいユーザー・エクスペリエンスには、キャッシュとやりとりするコードを書いて実行することも含まれると私たちは考えており、私たちのクライアント・ライブラリを驚くほどシンプルにするには、意図的な努力が必要です。別の言い方をすれば、私たちのキャッシュ・クライアントは第一級の関心事なのです。

開発者エクスペリエンスに関する私たちの哲学の概要については、シリーズの最初の投稿をご覧ください: 驚くほどシンプル: キャッシュクライアントがあなたのために大変な仕事をします。続いて、JavaScriptクライアントのチューニングについて深く掘り下げてみました。今日は、Pythonキャッシュクライアントのパフォーマンスについて同じように掘り下げてみます。

この投稿は、p999のレイテンシーとgRPCチャンネルについて、私と一緒にオタクになりたい人のためのものです。それ以外の人は、Momentoを使い始めることに興味があればOKですまた、GitHub orgからお好きな言語のMomentoクライアントをダウンロードすることもできます!

前回までの Shockingly Simple

Pythonキャッシュクライアントのパフォーマンスの調査は、JavaScript(Node.js)クライアントで行った作業と多くの点で似ている。前回のブログ記事から関連するハイライトをいくつか紹介します:

・Momentoクライアントは、Googleが作成したリモートプロシージャコールのための高性能フレームワークであるgRPCを使用して構築されています。
・そのため、チューニングの多くはgRPCの設定に集中します。
・Node.jsのように、デフォルトのPythonランタイムはシングルCPUコアでしかユーザーコードを実行しないので、CPUが最初の、そして主要なパフォーマンスのボトルネックになると思われます。
・クライアントに対して実行される同時リクエスト数が多ければ多いほど、そのすべてのネットワーク・コールバックを処理するために、より多くのCPUが必要になります。
・したがって、CPUのボトルネックがクライアント側のレイテンシーに影響を与え始める前に、処理できる同時リクエストの最大数を特定することが重要です。
・gRPCの “チャネル “は、クライアントとサーバ間の接続である。ほとんどの gRPC サーバは、チャネルあたりの最大同時リクエスト数を 100 に制限するように設定されています。
・したがって、100 を超える同時リクエストを実行する場合、クライアントとサーバ間に複数の gRPC チャネルを作成することが役立つことがあります。

Momento Python gRPCクライアントのチューニング

javascript のチューニングで行ったように、ラップトップから実行される 5,000 の同時リクエスト(prod よりもレイテンシが高い dev スタイルの環境)でのパフォーマンスを調査することから始めました。サーバはチャネルあたり100の同時ストリームしか許可しないので、1つのgRPCチャネルで、常に4,900のリクエストがバックログに滞留することになります。そこで、さまざまな数のチャネルをテストして、パフォーマンスにどのような影響があるかを調べました。JavaScriptでは、5チャンネルに増やすことで非常に大幅なパフォーマンスの改善が見られたので、Pythonでも同じことが見られると期待しました。以下がその結果です:

JavaScriptのチューニングとは異なり、これらの結果は、チャンネルを追加しても改善されないことを示しています。実際、結果のばらつきと p999 レイテンシは、単一のチャネルのみを使用した場合の方が実際に優れています。例えば、Python の実装は大部分が C++ で書かれているのに対し、grpc-js ライブラリは純粋に JavaScript で書かれています。したがって、ネットワーク・リソースへの低レベルのアクセスに関して、これらは異なる能力を持っています。

(JavaScriptのチューニングで、grpc.use_local_subchannel_poolという設定を見つけて、複数のチャンネルを使用するときのパフォーマンスに大きな影響を与えたことも思い出してください。PythonのgRPC実装では、この設定は針を動かしません)。

これでシングル・チャンネルで十分だとわかりました。しかし、これらのレイテンシーはまだ許容できないほど高いので、チューニングを続ける必要があります。

同時リクエスト数を変える

単一の gRPC チャネルで最良の結果が得られることがわかったので、次に許容する最大同時リクエスト数の理想的な値の調査に移ります。JavaScriptのチューニングでは、レイテンシとスループットの良いバランスを達成するための最良の値は50-100の範囲であることがわかりました。以下がその結果です:

予想通り、最良の値は50から100の範囲にあります。50から100に移行するとレイテンシーはわずかに増加するが、スループットはかなり増加するため、さらなるテストの出発点としては100が良さそうです。

ノートパソコンからクラウドへ

さて、開発環境でパフォーマンスがどのように見えるかを把握したところで、キャッシュ・サービスと同じリージョンで動作するAWSのEC2インスタンスでテストしてみましょう。これにより、ネットワークレイテンシーがほとんどなくなるので、クライアントサイドのレイテンシーの数値はサーバーサイドの数値にかなり近くなると予想されます。(c6i.4xlargeインスタンスタイプを使用するのは、このクラスでは小さいインスタンスよりも安定したネットワークパフォーマンスを観測しているからです)

チューニングの目標

JavaScriptクライアントをチューニングするときに使ったのと同じレイテンシー・ターゲットを使う:

クライアント側のp999レイテンシは20ms: これは、計算コストが非常に高いデータ(複雑なリレーショナル・データベース・スキーマの大きなJOINクエリなど)をキャッシュするアプリケーションや、アプリケーション自身のレイテンシ要件が甘い場合に妥当な目標値です。
クライアント側のp999レイテンシは5ms: これは、独自のレイテンシ要件が絶対的に重要なアプリケーションのための、より良いターゲットです。

asyncioとuvloop

単一のPythonプロセスで最大のパフォーマンスを発揮するようにチューニングしており、ボトルネックがCPUであることは分かっていたので、CPU使用量を減らす機会を探していました。ロード・ジェネレーター・プログラムのオーバーヘッドの多くは、Pythonのasyncioライブラリによって駆動されます。この投稿では、これまでデフォルトのエンジンを使ってきました。

私たちはuvloopエンジンを使って実験することにしました。具体的には、node.jsのイベントループで使われているのと同じlibuv Cライブラリを使います。パフォーマンスにどのような影響があったかを見てみましょう。uvloop を使用した場合と使用しなかった場合で、50 リクエストと 100 リクエストのパフォーマンスを比較します:

上のデータからわかるように、uvloopに切り替えると10%程度のパフォーマンス向上が見られます。2行のコード変更にしては悪くありません!これで、JavaScriptクライアントのパフォーマンスとほぼ同等になりました。

同時リクエスト数の再検討

EC2インスタンス上でuvloopを使用し、同時リクエスト数を変えて実行した場合のクライアント側のレイテンシーとスループットのデータです:

これらのチャートから、50の同時リクエストで最初のレイテンシー目標(p999レイテンシー20ms以下)を達成できていることがわかる。

レイテンシ要件がより厳しいアプリケーションでは、5つの同時リクエストでp999レイテンシ5ms以下という目標を達成することができました。これはスループットを毎秒9100リクエストから約3500リクエストに減少させますが、トレードオフは特定のアプリケーションにとって正しい選択でしょう。

Pythonプロセスの複数のインスタンスを実行することで、マシン全体のスループットを向上させるオプションもあります。今日は、単一のPythonプロセスを最適化することに焦点を当てましたが、将来の投稿では、これらのオプションで生じるパフォーマンスのトレードオフを探るために、また深く掘り下げるかもしれません。

結論

これらの結果から、開発環境とプロダクツ環境用のビルド済みコンフィギュレーションを定義するための強固な基盤ができました。今後のリリースでは、Configurations.Laptop、Configurations.InRegion、Configurations.InRegion.LowLatencyとProdEnvironmentConfigのようなビルド済みの設定から選択できるようになります。もちろん、適切な設定を手動で調整することもできますが、私たちの目標は90%のケースをカバーすることです!

次は C#

次回は、C#クライアントのチューニングを紹介します。今回は、マルチCPUコアの使用をサポートする言語を初めて取り上げます。。最初の2回ほどCPUがボトルネックになることはないので、面白い展開になるはずです。ご期待ください!

あるいは…しない!Momentoでは、このようなことを心配する必要はないというのが私たちの考えです。Momentoを使えば、このような心配をする必要はないというのが私たちの考えです。私たちのスタートアップガイドをチェックし、わずか数分で無料でキャッシュを作成することができます。

そして、この

Share