🏎️

DeepStream vs Triton Inference Server

2024/06/26に公開

引き続き,Triton Inference Server の動作検証シリーズ.今回は,同じく NVIDIA からリリースされているストリームデータの処理に特化されたフレームワーク DeepStream との動作比較を行った. (注: 今回の記事には,Triton と DeepStream の Latency & Throughput の比較は含まれていません.)

今回の検証課題

ML パイプラインを構築できる NVIDIA の推論用フレームワークは大きく,「Triton Inference Server」と「DeepStream SDK」の 2 種類ある.Triton は,CPU/GPU 環境の両方で動作するなど非常に汎用的な設計になっている.一方で,DeepStream は GPU 環境のみで使用できるという制約があるが,ハードウェアアクセラレータ (NVDEC) を使った入力のビデオストリームの直接 GPU メモリへの読み込みや,各種処理のマルチプロセス化などを,簡単に高速な ML パイプラインを構築することができる.

今回は,この二つのフレームワークについて,GPU の使用方法の観点に着目して,比較をしていく.ML パイプラインとして,(Latency ではなく) Throuput を高めるためには GPU のアイドルタイムを減らし,使用率を常に高く保つ必要がる.プロファイラを用いて,GPU の使用状態を比較する.

NVIDIA DeepStream SDK

本題に進む前に,簡単に DeepStream の特徴をまとめる.詳細は本家ドキュメントを読むことをお勧めする.

  • ストリーミングデータの解析ツール
  • メディア処理用ツールである,GStreamer をベースに,NVIDIA GPU を扱うためのプラグインや,ML Pipeline を構築するためのプラグインを追加したものである.
  • 入力の動画はハードウェアアクセラレータ NVDEC を使用して,直接 GPU メモリ上にデコードされる.(ホストマシンのメモリからデータを GPU に転送不要)
  • ML パイプラインは,追加された DeepStream のコンポーネントを組み込んで,GStreamer のパイプラインとして構築する.
  • DeepStream (個人的) 主要コンポーネント
    • Gst-nvinfer: Tensor RT でモデルの推論を行う GStreamer プラグイン.
    • Gst-nvinferserver: Triton Inference Server で推論を行う GSTreamer プラグイン
    • Gst-nvstreammux New: GStreamer は基本フレームごとの処理を想定しているが,ML のパイプラインでは複数フレームをバッチとして扱うことが処理能力の観点から必要.nvstreammux は複数のフレームを束ねて,バッチを作成する.以後に配置される nvinfer などの DeepStream のプラグインは,基本 Batch を入力としている.

https://developer.nvidia.com/deepstream-sdk

DeepSteqam Arch
DeepStream Overview[1]

検証方法

今回は 3 つのモデルを含むパイプラインにおいて,プロファイラを使って GPU の利用状況を比較する.

今回も,過去の Triton 検証記事 と同じ,Conv2d 1 層・2 層・3 層の 3 種のモデルを直列に繋いだパイプラインを用いる.このパイプラインを DeepStream と Triton の両方で構築し,プロファイラを使って GPU の利用状況を比較する.

実装

Triton 側の実装は 以前の記事 と同じ物を用いた.

https://github.com/getty708/triton-sandbox/tree/feat/getty708/model-concurrency-deepstream/benchmarks/concurrent_model_exec

DeepStream 側は新たに実装し直した.最新の DeepStream 7.0 でパイプラインを動かすまでにそれなりの苦労があった (サンプルやドキュメントが,部分的にしか更新されていないなど...).それについては,また別記事で紹介したい.まだ,最適化しきれていない挙動が残っているのはご容赦いただきたい.

https://github.com/getty708/triton-sandbox/tree/feat/getty708/model-concurrency-deepstream/deepstream/concurrent_model_exec

(note: 後日リファクタリングを行い実行方法などを追加します.現時点ではこれでご容赦ください.)

結果

Triton Pipeline (Ensemble Models)

以前の記事と同じ画像を再び下に載せる.濃い青 (void から始まる帯) が Conv2d の処理で,それに続く水色の帯は計算結果のテンソルを GPU 上の別領域にコピーする処理 (Memcpy DtoD) である[2].クライアント側で,前処理済みの入力データを GPU メモリにセットしているため,入力データのコピー発生しない.

3 つのモデルの計算がほぼ隙間なく GPU 上で実行されていることがわかる.よく観察すると,モデル間 (Mmcpy DtoD と 畳み込み演算 CUDA カーネルタスク) の間に 450 μs 程度のギャップが存在している.これは,Ensembe Models のスケジューラが,モデルの出力を次のモデルに繋いでいる処理かと推測される.今回はモデルを直列に繋いでいるが,モデルの並列実行を行うと,逆に 10 μs 程度のオーバーラップが発生している.計測の方法によるものかと思われるが,並列実行できるモデルがあるなら,GPU は隙間なく無駄なく使用されていると言える.

Triton Pipeline
Profiling: Triton Pipeline

DeepStream Pipeline

DeepStream のプロファイリング結果を以下に示す.Triton とは大きく違い,GPU 上で何も実行されていない部分が目立つ.スクショ下側 GstNvinfer の帯を見ると,モデル自体は隙間なく実行されているようだが,1 モデルの実行時間が 300ms 近くと,Triton の 5ms ~ 15ms と比べてとても長い.CNN の GPU 上での処理自体は最初の方に終了しているが,その後に長い街状態が発生している.追加調査は必要であるが,gstreamer 側のプロセス管理サイクルに起因するものだと思われる.このバッファを減らして latency を下げる方法は,別途調査していきたい.

DeepStream Pipeline
Profiling: DeepStream Pipeline (Video: 1FPS)

ちなみに,動画の再生速度を 1FPS から 5FPS に変更した結果は以下のとおりである.異なるバッチに対する処理が並列実行され,挙動が非常に複雑になる.詳細を見ると,各モデルのプロセス実行時間はむしろ伸びるが,自動的に各モデルが 3 インスタンス程度用意され並列実行されているように見える.前処理・後処理などの部分が並列実行され,モデルが全体として並列実行されているように見えると思われる.また,プロセスの帯が各モデル (GUID) で一つずつしか記録されていないことから,Triton の マルチインスタンス実行とは異なり,TensorRT のモデルは GPU 上に 1 つしか展開されていないと推測される.

DeepStream Pipleine (5FPS)
Profiling: DeepStream Pipeline 2 (Video: 5FPS)

最後に DeepStream によるパイプラインの GPU 処理の部分をクローズアップしてみる.Conv2d 1 層モデルの実行状況を以下に示す. Triton は,ほぼ 1 つのカーネルタスク + GPU デバイス上でのメモリーコピーによって構成されていたが,DeepStream の場合は Conv2D に対応するカーネルに加えて,他に 2 つカーネルタスクのブロックがあり,最後には Device to Host のメモリーコピーが発生する.

畳込み演算の前の 2 つのブロックは,Gst-Nvinfer の前処理に対応する.DeepStream はフレームを NV12 という独自のコーデックで GPU メモリに保存しているため,推論前には RGB 画像に変換し,さらにモデルの入力となる Float 型のデータに変換する必要がある.この二つの処理が,各ブロックに対応している.これらの前処理はフレームごとに実行され,それぞれ 40 μs 程度かかっている.画像の枚数が増えると,比例的に増加するため,入力のストリームが多い場合には注意が必要かもれしれない.

また,畳込み演算後のメモリのコピーは,Triton とは違い,GPU からホストへのコピーとなっている.これは,Gst-Nvinfer で出力にテンソルを指定していることが関係していると思われる.メタデータは CPU 側において C の構造体として GStreamer のメタデータに格納されるため,データを CPU 側に移動させなければいけないということだと思われる.

ちなみに,畳込み演算 (1 層分) の実行速度は,ONNX Runtime で実行している Trito は 5ms 程度であるのに対し,Tensor RT で実行している DeepStream は,3.5 ms と速い.Tensor RT の最適化が効いていることが確認できた.

DeepStream Pipeline (Zoom)
Profiling: DeepStream Pipeline 3 (GPU Process)

まとめ

DeepStream は,簡単に高スループットな ML パイプラインを構築できるが,GPU の挙動からすると,Triton に比べてムダが多いように思われる.Titon の場合,画像を Float のテンソルに変換するといった共通の前処理は,一回実行すればその結果を使い回すことができるが,DeepStream ではそれはできなく毎度前処理が実行される.よって,この記事の結論としては,Triton の方が GPU を効率的に活用し,高いスループットを出せる可能性が高い.

ただし,Triton でパイプラインを実装する場合,クライアントがパイプラインのボトルネックになりうる.ビデオストリームを受け取って GPU メモリにセットする処理を,高速かつ多くのストリームに対して安定して実行させることは至難の業である.DeepStream は,その点をカバーしてくれるので,ビデオストリームを入力とする ML パイプラインを実装する上では有力な候補である.

脚注
  1. https://developer.download.nvidia.com/images/deepstream/metropolis-deepstream-vision-ai-edge.jpg ↩︎

  2. 計算結果をモデルのワークスペースから対比させる処理だと思われる.予想が正しいか,調査を続けていく. ↩︎

Discussion