GPUモジュールの概要説明 ================================= .. highlight:: cpp 概要 ------ OpenCV の GPU モジュールは,GPU の計算能力を利用するためのクラスと関数の集合体です.NVidia CUDA ランタイム API を利用して実装されているので,NVidia の GPU のみがサポートされています.ここには,ユーティリティ機能や,低レベルなビジョンの基本機能だけでなく,高レベルなアルゴリズムも含まれています.このユーティリティ機能や低レベルな基本機能は,GPU の利点を生かした高速ビジョンアルゴリズムの開発のための重要な基盤となります.一方,高レベル機能には最先端のアルゴリズム(ステレオ対応点探索,顔・人検出など)が含まれており,アプリケーション開発者はすぐにこれを利用することができます GPU モジュールは,ホストレベル API として設計されています.つまり,ユーザが事前にコンパイルされた OpenCV GPU バイナリを持っていれば,Cuda Toolkit をインストールしたり,GPU を利用できるように追加コードを書いたりする必要はありません. GPU モジュールは,Cuda Toolkit および Nvidia Performance Primitives library (NPP) に依存しています.これらの最新バージョンを利用してください.どのサポート対象プラットフォームのものでも,NVidiaのサイトからダウンロードできます.OpenCV GPU モジュールをコンパイルするには,Cuda Runtime Toolkit と互換性のあるコンパイラが必要になります. OpenCV GPU モジュールは,簡単に使えるように設計されており,Cuda に関する知識を必要としません.しかし,その知識は,重要な場面や最高のパフォーマンスを欲する場合に確実に役立つでしょう.これは,様々な処理のコスト,GPU が何を行っているのか,より良いデータフォーマットは何か,などについて理解する助けになります.GPU モジュールは,GPUで高速化されたコンピュータビジョンアルゴリズムを手早く実装のための効率的な道具となります.しかし,アルゴリズムが多数の単純な処理を含んでおり,そこで最高のパフォーマンスを得るには,中間結果における余分な読み書き処理を避けるために,まだ,自分でカーネルを書く必要があるかもしれません. CUDA サポートを有効にするには,CMake で ``WITH_CUDA=ON`` にして OpenCV のコンフィグを行ってください.このフラグがセットされ,CUDAがインストール済みであれば,フル機能の OpenCV GPU モジュールがビルドされます.そうでない場合,このモジュールの関数はすべて,実行時に ``CV_GpuNotSupported`` の :func:`Exception` を投げます.ただし, :func:`gpu::getCudaEnabledDeviceCount()` は例外で,この場合は GPU 数0を返します.また,CUDA を無効にして OpenCV をビルドした場合は,デバイスコードのコンパイルを行いませんので,Cuda Toolkit も不要です.したがって,関数 :func:`gpu::getCudaEnabledDeviceCount()` を利用して,実行時に GPU の有無を検知し,それに応じて適切な実装(CPU または GPU)を選択する高レベルアルゴリズムを実装することが可能です. 異なる NVidia プラットフォーム用のコンパイル -------------------------------------------------------------- NVidia コンパイラによって,バイナリコード(cubin と fatbin),および中間コード(PTX)を生成することができます.バイナリコードは多くの場合,特定のGPUアーキテクチャや世代を暗示し,別のGPUとの互換性は保証されません.PTX は,性能や機能の集合によってのみ定義される仮想プラットフォームをターゲットにしています.実際のハードウェアがすべての機能をサポートしていたとしても,仮想プラットフォームの選択によっては,エミュレートされたり使えなかったりする命令も存在します. PTX コードは,最初のの呼び出しで,実行時の具体的な GPU プラットフォーム用に Just In Time (JIT) コンパイルされます.PTX コードには,より新しいプラットフォーム用にコンパイルできる(新しいものは,現在の機能セットもサポートするので)が,古いプラットフォーム用にはコンパイルできない(現在のPTXに,古いものがサポートしていない機能が含まれているかもしれないので),というルールがあります. デフォルトでは,OpenCV GPU モジュールは以下を含みます: * compute capabilities 1.3 および 2.0 用のバイナリ(CMake の ``CUDA_ARCH_GPU`` で制御されます) * compute capabilities 1.1 および 1.3 用の PTXコード(CMake の ``CUDA_ARCH_PTX`` で制御されます) これは,CC 1.3 および 2.0 のデバイスの場合,いつでもバイナリイメージを実行できることを意味します.それ以降のすべてのプラットフォームでは,CC 1.3 用のPTX コードがバイナリにイメージに JIT コンパイルされます.CC 1.1 および 1.2 のデバイスでは,PTX コードが JITコンパイルされます.CC 1.0 のデバイス用のコードは存在せず,関数は :func:`Exception` を投げます.JITコンパイルが実行されるプラットフォームでは,初回実行時は動作が遅いかもしれません. CC 1.0 の GPU だった場合でも,GPU モジュールはコンパイル可能で,その関数もカード上で問題なく動作します. ``CUDA_ARCH_BIN="1.0 1.3 2.0"`` という風に,"1.0" をバイナリの一覧に単に追加してください.CC 1.0 の GPU 上で動作しない関数は例外を投げます. OpenCV GPU のバイナリ(または,PTX コード)と,ユーザの GPU との間に互換性があるか否かを,実行時にいつでも決定できます.関数 :func:`gpu::DeviceInfo::isCompatible` は,互換状態(true/false)を返します. スレッディングとマルチスレッディング ------------------------------------------------------ OpenCV GPU モジュールは,マルチスレッドプログラミングに関しては,Cuda Runtime API の慣例に従っています.つまり,最初の API 呼び出し時に,暗に作成されたCuda コンテキストが現在の CPU スレッドにアタッチされ,それが,そのスレッドの「現在の」コンテキストになります.メモリ割り当てや GPU コードのコンパイルなどの,以降の処理はすべて,このコンテキストとスレッドに関連付けられます.他のスレッドはコンテキストにアタッチされていないので,最初のスレッドで割り当てられたメモリ(または,その他のリソース)にアクセスすることはできません.その代わり,Cudaは他のスレッド用に,それに関連付けられた別のコンテキストを作成します. しかし,Cuda ドライバ API (バージョン 3.1 以降)を利用すると,このような制限を解除できます.ドライバ API を使うと,あるスレッドのコンテキスト参照を取り出し,それを別のスレッドにアタッチして,それをスレッドの「現在の」コンテキストにすることができます.そうすることで,そのスレッドはメモリやその他のリソースを共有できます.また,GPUコードが呼ばれるよりも前に,明示的にコンテキストを作成し,リソースを共有したいすべてのスレッドにアタッチすることも可能です. マルチ GPU ------------- 現在のバージョンでは,OpenCV GPU アルゴリズムは,1つの GPU のみを利用します.複数の GPU を利用するには,ユーザがタスクを手動でGPUに割り振らなければいけません.ここでは,複数の GPU を利用する2つの方法を示します: * まず,同期関数のみを利用する場合,複数の CPU スレッド(各GPUに1つ)を作成します.そして, :func:`gpu::setDevice()` またはドライバ APIを利用して,各スレッド内で対応する GPU 用の CUDA コンテキストを作成します.これだけです. それぞれのスレッドは,関連付けられた GPU を利用します. * 非同期関数を利用する場合,1つの CPU スレッドにアタッチされつつ,異なる GPU に関連付けられた複数の Cuda コンテキストを作成することができます.これは,ドライバ API を利用することによってのみ可能になります.スレッド内で,対応するコンテキストを「現在の」コンテキストにすることで,1つのGPUから別のGPUに切り替えることができます.ブロックしない GPU 呼び出しの場合,管理アルゴリズムは明快です. 複数の GPU を利用するアルゴリズム開発において,データ受け渡しのオーバヘッドは重要な問題です.基本的な関数や小さい画像を利用する際に大きな影響を与え,複数 GPU を利用するアドバンテージを打ち消してしまう可能性があります.しかし,高レベルなアルゴリズムの場合は,マルチ GPU による高速化は適した方法かもしれません.例えば,以下のアルゴリズムを用いて,ステレオブロックマッチングアルゴリズムをうまく並列化する事ができました. * ステレオペアのそれぞれの画像を,重複領域を持つ2つの部分に水平分割します. * 各部分ペア(左画像と右画像の一部づつ)を,個別の Fermi GPU で処理します. * 結果は,1つの視差マップにマージされます. この方法により,デュアル GPU で 単一の Fermi GPU と比較して 180 % の性能向上が見られました.この例のソースコードは, https://code.ros.org/svn/opencv/trunk/opencv/examples/gpu/ にあります.