高度なスループット・オプション: ストリームとバッチ処理

OpenVINO ストリーム

一般的な最適化セクションで説明したように、複数の推論要求を非同期で実行することは、通常アプリケーションの効率にとって重要です。内部的には、すべてのデバイスはバッファーとして機能するキューを実装しており、デバイスが独自のタイミングで取得するまで推論要求を保存します。デバイスは、デバイスの使用率と全体のスループットを向上させるため、複数の推論要求を並行して処理する場合があります。このデバイス側の並列処理の構成方法は、一般にストリームと呼ばれます。

ストリームは要求を並行して実行しますが、(バッチ処理のように) ロックステップでは並行して実行しないことに注意してください。これにより、ストリームは動的形状の入力と完全に互換性が得られ、個々の要求は異なる形状を持つことができます。

ほとんどの OpenVINO デバイス (CPU および GPU を含む) はストリームをサポートしますが、ストリームの最適数は異なる方法で推定されます。このトピックの詳細については、以下のセクションを参照してください。

いくつかの一般的な考慮事項:

  • ストリームを使用すると、個々の要求のレイテンシーが増加します。

    • ストリーム数が指定されていない場合、デフォルトではレイテンシーを重視するため、デバイスは最小限のストリーム (通常は 1 つだけ) を作成します。

    • ストリームの最適な数を決定するには、以下のヒントを参照してください。

  • すべてのストリームが中間バッファーを複製して残りのストリームと並行して推論を行うため、ストリームは大量のメモリーを消費します。

    • 重みメモリーがストリーム間で共有されると、メモリー消費が削減されるため、同じモデルに対して複数の ov:Compiled_Model インスタンスを作成するよりもストリームを優先してください。

  • ストリームによってモデルのロード (コンパイル) 時間も長くなることにも注意してください。

効率良い非同期実行のため、ストリームはスレッドの特別なプール (ストリームごとに 1 つのスレッド) を使用して推論を処理します。推論要求 (異なるアプリケーション・スレッドからの可能性もあります) を開始するたびに、要求は特定の ov:Compiled_Model の推論キューに多重化されます。空のストリームがある場合は、キューから要求をプルし、デバイス上での実行に移行します。内部セクションには、CPU などのデバイスに固有の詳細がさらに示されます。

バッチ処理

GPU などのハードウェア・アクセラレーターは、大規模な並列計算処理向けに最適化されているため、バッチ処理によりデバイスが飽和状態になり、スループットが向上します。ストリーム (前のセクションで説明) は、通信のオーバーヘッドやスケジューリングのバブルを隠匿するのに役立ちますが、複数の OpenCL カーネルを同時に実行すると、複数の入力でカーネルを同時に呼び出すのに比べて GPU 効率が低くなります。次のセクションで説明するように、GPU で最大のスループットを引き出すにはバッチ処理が必須です。

バッチ処理によりアプリケーションのパフォーマンスを向上するには、いくつかの方法があります。

  • アプリケーション側で入力を明示的に収集し、バッチ要求を OpenVINO に送信します

    • これにより、バッチ処理に柔軟性が与えられますが、このアプローチはアプリケーション・ロジックの再設計が必要になります。

  • 個々の要求を送信しながら、要求をバッチで自動的に収集して推論するように OpenVINO を構成します。

どちらの場合も、最適なバッチサイズはデバイスによって異なります。以下で説明するように、最適なバッチサイズはモデル、推論精度、その他の要因によっても異なります。

ストリーム数/バッチサイズの選択

推論パフォーマンスの予測は難しく、最適な実行パラメーターを見つけるには、測定による実験と検証が必要です。開発段階でパフォーマンス・テストを実行し、アプリケーション全体の (エンドツーエンドの) パフォーマンスを必ず検証します。

デバイスが異なれば、バッチサイズに応じて動作も異なります。最適なバッチサイズは、モデル、推論精度、その他の要因によって異なります。同様に、デバイスが異なれば、飽和に必要な実行ストリーム数も異なります。場合によっては、スループットを最大化するためストリームとバッチ処理の組み合わせが必要になります。

スループット最適化戦略の候補の 1 つは、レイテンシーの上限を設定し、そのテール・レイテンシーに達するまで (またはスループットが増加しなくなるまで) バッチサイズやストリーム数を増やすことです

動的に形成された入力を使用する場合、異なる形状の個々の要求を許容するため、ストリームのみを使用します (バッチ処理は使用しません)。

ハイレベルのパフォーマンス・ヒントは、移植可能で将来性のある代替オプションであり、OpenVINO は特定のシナリオとモデルに最適なストリームとバッチの組み合わせを見つけることができます。

ストリーム数の考慮事項

  • アプリケーションが同時に実行できる要求数以下のストリーム数を選択します。

  • リソースの無駄を避けるため、ストリーム数は、ピーク負荷ではなく平均並列スラックを満たすのに十分である必要があります。

  • さらに移植性の高いオプションとして ov::streams::AUTO が使用できます (ベースとなるハードウェア構成も考慮されます)。

  • できるだけ多くの推論要求を実行して、ストリームをビジー状態に保つことが重要です (例えば、新しく到着した入力をすぐに開始します)。

  • デバイスのストリームの最大数 (モデルごと) は、ov::range_for_streams で照会できます。

バッチサイズの考慮事項

  • アプリケーションが同時に実行できる要求数と等しいバッチサイズを選択します。

    • それ以外の場合 (または “利用可能な” 要求の数が変動する場合)、ネットワークの複数のインスタンスを保持し (異なるバッチサイズに再形成し)、それに応じてランタイムで適切なサイズのインスタンスを選択する必要がある場合があります。

  • 独自のヒューリスティックを内部で実装する OpenVINO デバイスの場合、ov::optimal_batch_size は、モデルの推奨バッチサイズを照会するデバイス・プロパティー (実際のモデルをパラメーターとして受け入れる) です。

デバイス固有の詳細

  • GPU の場合:

    • 並列スラックが小さい、例えば同時に実行される要求が 2 ~ 4 つだけである場合、GPU のストリームを使用するだけで十分な場合があります。

      • GPU はストリームごとに 2 つの要求を実行できるため、2 つのストリームで 4 つの要求を処理できます。

      • あるいは、単一のストリームに 2 つの要求 (それぞれの 2 などの小さなバッチサイズ) が含まれていると考えてください。これにより、実行中に同じ 4 つの入力が行われることになります。

    • 通常、4 つ以上の要求では、バッチ処理によりスループットが向上します。

    • バッチサイズは、“並行して実行される推論要求の数” を “ストリームが消費する要求の数” で割ったものとして計算できます。

      • 例えば、2 つの GPU ストリーム (それぞれ 2 つの要求を処理できる) で 16 台のカメラ (同時に推論される 16 の要求) を処理する場合、要求あたりのバッチサイズは 16/(2*2)=4 になります。

  • CPU では、常に最初にストリームを使用してください:

    • ハイエンド CPU では、ストリームの最大数に加えて適度な (2 ~ 8) バッチサイズを使用すると、パフォーマンスがさらに向上する可能性があります。