パフォーマンスに関するベストプラクティス

パフォーマンス向上のための、一般的および言語固有のベストプラクティスに関するユーザーガイドです。

パフォーマンスに関するベストプラクティス

パフォーマンス向上のための、一般的および言語固有のベストプラクティスに関するユーザーガイドです。

一般

  • 可能な限り常に**スタブとチャネルを再利用**してください。

  • 非アクティブ期間中にHTTP/2接続を維持するために**キープアライブピングを使用**し、初期RPCを遅延なしで迅速に行えるようにします(例:C++チャネル引数GRPC_ARG_KEEPALIVE_TIME_MS)。

  • クライアントからサーバーへ、サーバーからクライアントへ、または双方向の長期的なデータの流れを処理する際には、**ストリーミングRPCを使用**してください。 ストリームを使用すると、接続のロードバランシング(クライアント側)、トランスポート層での新しいHTTP/2リクエストの開始、サーバー側でのユーザー定義メソッドハンドラの呼び出しなど、継続的なRPC開始を回避できます。

    ただし、ストリームは開始されるとロードバランシングできず、ストリームの失敗のデバッグが困難になる可能性があります。また、小規模ではパフォーマンスが向上する可能性がありますが、ロードバランシングと複雑さのためにスケーラビリティが低下する可能性があるため、アプリケーションロジックに大幅なパフォーマンスまたは簡素化の利点をもたらす場合にのみ使用してください。アプリケーションを最適化するためにストリームを使用し、gRPCを最適化するためには使用しないでください。

    補足:これはPythonには適用されません(詳細はPythonセクションを参照)。

  • (特記事項)各gRPCチャネルは0個以上のHTTP/2接続を使用し、各接続は通常、同時ストリーム数に制限があります。接続上のアクティブなRPC数がこの制限に達すると、追加のRPCはクライアントでキューに入れられ、アクティブなRPCが終了するまで送信を待つ必要があります。高負荷または長寿命のストリーミングRPCを持つアプリケーションでは、このキューイングによってパフォーマンスの問題が発生する可能性があります。2つの解決策があります。

    1. アプリケーションの高負荷領域ごとに**個別のチャネルを作成**します。

    2. 複数の接続にRPCを分散するために**gRPCチャネルのプールを使用**します(チャネルの再利用を防ぐために、チャネル引数を異なるものにする必要があります。チャネル番号などの用途固有のチャネル引数を定義します)。

    補足:gRPCチームは、これらのパフォーマンスの問題を解決するための機能を追加する予定です(詳細についてはgrpc/grpc#21386を参照)。そのため、複数のチャネルを作成するソリューションは、最終的には不要になる一時的な回避策です。

C++

  • **パフォーマンスに敏感なサーバーでは同期APIを使用しないでください。** パフォーマンスやリソース消費が問題にならない場合は、実装が最も簡単な同期APIを使用してください(低QPSサービス向け)。

  • アプリケーションがすべてのブロッキング操作を回避できる場合、またはブロッキング操作を別のスレッドに移動できる場合は、ほとんどのRPCに対して**他のAPIよりもコールバックAPIを優先**してください。コールバックAPIは、完了キュー非同期APIよりも使いやすいためですが、非常に高いQPSのワークロードでは現在速度が遅くなっています。

  • 非同期完了キューAPIを使用する必要がある場合、**最適なスケーラビリティのトレードオフは、numcpu個のスレッドを持つこと**です。スレッド数に対する完了キューの理想的な数は、時間の経過とともに変化する可能性があります(gRPC C++が進化するにつれて)。しかし、gRPC 1.41(2021年9月)時点では、完了キューあたり2つのスレッドを使用すると、最良のパフォーマンスが得られるようです。

  • 非同期完了キューAPIの場合、サーバーが本質的にシリアルなリクエスト処理につながる遅いパスに継続的にスタックするのを防ぐために、**必要な並行性レベルに対して十分なサーバーリクエストを登録**してください。

  • (特記事項)gRPC::GenericStubは、プロトシリアライゼーションに高い競合/CPU時間が費やされている特定のケースで役立ちます。このクラスを使用すると、アプリケーションは**raw gRPC::ByteBufferをデータとして直接送信**でき、特定のプロトからシリアライズする必要がありません。同じデータが複数回送信される場合にも役立ちます。プロトからByteBufferへの明示的なシリアライゼーションを1回行い、それに続いてByteBufferの送信を複数回行うことができます。

Java

  • RPCを並列化するために**非ブロッキングスタブを使用**してください。

  • **ワークロードに基づいてスレッド数を制限するカスタムエグゼキュータを提供**してください(キャッシュ済み(デフォルト)、固定、forkjoinなど)。

Python

  • ストリーミングRPCは、メッセージの受信と送信のために追加のスレッドを作成するため、gRPC Pythonでは他のgRPC対応言語とは異なり、**ストリーミングRPCは単項RPCよりもはるかに遅くなります**。

  • **asyncioを使用する**とパフォーマンスが向上する可能性があります。

  • 同期スタックのFuture APIを使用すると、追加のスレッドが作成されます。可能な限り**Future APIを避けて**ください。

  • (実験的)SingleThreadedUnaryStreamチャネルオプションを介して、実験的な**シングルスレッド単項ストリーム実装**を使用できます。これにより、メッセージごとに最大7%のレイテンシを削減できます。