一般的な最適化¶
ここでは、データ・パイプラインや前処理の高速化などを改善するため、非同期実行などアプリケーション・レベルの最適化手法について説明します。手法 (前処理など) はエンドユーザー・アプリケーションに固有のものである可能性がありますが、関連するパフォーマンスの向上は一般的なものであり、レイテンシーとスループットの両方のあらゆるターゲットシナリオを改善します。
OpenVINO による入力の前処理¶
多くの場合、ネットワークは前処理された画像を期待します。コード内で必要のない手順を実行しないことをお勧めします。
モデル変換 API は、平均値と正規化 (スケール) 値をモデル (例えば、最初の畳み込みの重み) に効率良く組み込むことができます。詳細については、関連するモデル変換 API のコマンドライン・パラメーターを参照してください。
OpenVINO で画像の前処理と変換を加速させます。
すでに “デバイス上の” メモリーにあるデータは、GPU プラグインのリモート tensor API を使用して直接入力できます。
OpenVINO 非同期 API¶
推論要求の API は、同期および非同期に実行できます。ov::InferRequest::infer()
は本質的に同期構造であり、直ちに実行されます (現在のアプリケーション・スレッドで実行フローをシリアル化します)。Async は infer()
を ov::InferRequest::start_async()
と ov::InferRequest::wait()
に “分割” します。詳細については、API の例を参照してください。
ov::InferRequest::infer()
の一般的な使用例は、入力ソース (カメラなど) ごとに専用のアプリケーション・スレッドを実行することで、すべてのステップ (フレーム・キャプチャー、処理、結果の解析、および関連するロジック) がスレッド内で逐次化されます。対照的に、ov::InferRequest::start_async()
および ov::InferRequest::wait()
を使用すると、アプリケーションはアクティビティーを継続し、必要なときに推論の完了をポーリングまたは待機できます。非同期コードを使用する理由の 1 つは “効率” です。
注
同期 API のほうが使い方は容易ですが、運用目的のコードでは非同期 (以下、コールバック・ベース) API を使用することを推奨します。それは、考えられる数の要求 (レイテンシーとスループットの両方のシナリオ) に対してフロー制御を実装する一般的でスケーラブルな方法であるためです。
非同期アプローチの利点は、デバイスが推論でビジーなときに、アプリケーションが現在の推論が最初に完了するのを待機することなく、他の処理 (入力の設定や他の要求のスケジュール設定など) を並行して実行できることです。
以下の例では、ビデオデコードに推論が適用されます。2 つの並列推論要求を保持することが可能であり、現在の推論要求が処理されている間に、次の推論要求の入力フレームがキャプチャーされます。これにより、キャプチャーのレイテンシーが隠蔽されるため、全体のフレームレートはステージの合計ではなく、パイプラインの最も遅い部分 (デコードと推論) によってのみ決定されます。
以下は、通常のアプローチと非同期ベースのアプローチのコード例です。
-
通常、フレームは OpenCV でキャプチャーされ、直ちに処理されます。
while(true) { // capture frame // populate CURRENT InferRequest // Infer CURRENT InferRequest //this call is synchronous // display CURRENT result }
-
“真の” 非同期モードでは、
CURRENT
要求が処理されている間に、NEXT
要求がメイン (アプリケーション) スレッドに設定されます。while(true) { // capture frame // populate NEXT InferRequest // start NEXT InferRequest //this call is async and returns immediately // wait for the CURRENT InferRequest // display CURRENT result // swap CURRENT and NEXT InferRequests }
この手法では、利用可能なあらゆる並列スラックに一般化できます。例えば、推論を実行しながら、結果のフレームまたは前のフレームを同時にエンコードしたり、顔検出結果に加えて感情検出を行うなど、追加の推論を実行できます。非同期 API の完全な例については、オブジェクト検出 C++ デモ、オブジェクト検出 Python デモ (レイテンシー指向の非同期 API ショーケース)、およびベンチマーク・アプリのサンプルを参照してください。
注
スループット重視のシナリオでは、非同期 API が必須です。
コールバックに関する注意事項¶
Async API の ov::InferRequest::wait()
は、特定の要求のみを待機することに注意してください。ただし、複数の推論要求を並行して実行しても、完了する順序は保証されません。これにより、ov::InferRequest::wait
にベースのロジックが複雑になる可能性があります。最もスケーラブルなアプローチは、要求の完了時に実行されるコールバック (ov::InferRequest::set_callback
によって設定) を使用する方法です。コールバック関数は、結果 (またはエラー) を通知するため OpenVINO ランタイムによって使用されます。これは、よりイベント駆動型のアプローチです。
コールバックには、いくつかの重要な点があります。
コールバック関数がスレッドセーフであることを確認するのは、アプリケーションの役割です。
専用のスレッドによって非同期に実行されますが、コールバックには負荷の高い操作 (I/O など) やブロック呼び出しが含まれてはいけません。コールバックで実行されるワークは最小限に抑える必要があります。
‘get_tensor’ イディオム¶
OpenVINO 内の各デバイスには、中間テンソルのメモリーパディング、アライメントなどに関して異なる内部要件がある場合があります。入出力テンソルにはアプリケーション・コードからもアクセスできます。すべての ov::InferRequest
は ov::CompiledModel
の特定のインスタンス (すでにデバイス固有) によって作成されるため、要件が尊重され、要求の入出力テンソルは引き続きデバイスに適しています。要約すると次のようになります。
-
get_tensor
(テンソルの内容へのシステム・メモリー・ポインターを取得するdata()
メソッドを提供する) は、ホストメモリーとの間で推論入力を設定する (および出力を読み戻す) のに推奨される方法です。例えば、GPU デバイスでは、入出力テンソルは
get_tensor
が使用される場合にのみホストにマッピングされます (高速です)。一方、set_tensor
の場合は内部 GPU 構造へコピーが発生する可能性があります。
対照的に、入力テンソルがすでにデバイスのメモリーにある場合 (ビデオデコードの結果など)、ゼロコピーの方法には
set_tensor
を優先します。詳細については、GPU デバイス・リモート・テンソル API を参照してください。
get_tensor
と set_tensor
の API の例を考えてみましょう。