はじめに
これは、OpenCV Advent Calendar 2018 3日目の記事です。関連記事は目次にまとめられています。なお、本記事は筆者個人の意見であり、筆者の所属組織とは無関係です。
OpenCVの挙動を変える
OpenCVの挙動は基本的にビルド時に決定されます。挙動というのはCUDA、OpenCL、Eigen、IPPやPLCなどの外部連携ライブラリを使う/使わない、SIMD命令を使う/使わない、と言った類の挙動です。
またビルド時に連携するライブラリのパスも、場合によっては一々指定する必要があります。
その時に、毎回手動でパスを指定したり、挙動を変えるためにビルドをやり直すのも、なかなかイケてない状況です。そこで、一部限られた挙動に限り、環境変数で挙動を変えることが可能です。
具体例も交えて紹介していきます。
実行時の挙動を変える
実行時の挙動の一つとして、OpenCLのデバイスが挙げられます。OpenCLはご存知のようにアクセラレータで動作するカーネルを、単一の言語で記述することができます。ここで言う「アクセラレータ」はGeforeceやRadeonなどのGPUが一般的ですが、Intel HD GraphicsなどのCPU内蔵のGPU、FPGAやCPU自身でも実行することが1できます。
例えば、筆者の私物のラップトップは、以下のような構成です
Device | |
---|---|
CPU | Intel Core i7 8650U |
iGPU | Intel HD Graphics 620 |
dGPU | NVIDIA Geforce GTX 1060 |
ここに記述されているCPU、iGPU、dGPUはそれぞれOpenCLに対応しているので、OpenCLでカーネルを記述すると、勝手にベクタライズ/並列化されたプログラムをそれぞれのプラットフォームで走らせることができます。ただし、基本的にはOpenCLが列挙した1番目のデバイスが選ばれます。他のプラットフォームで走らせたい場合はどうすれば良いのでしょうか?
ここで、環境変数OPENCV_OPENCL_DEVICE
を指定すると、任意のプラットフォームを指定できます
環境変数の指定方法はコントロールパネルからしたり、Visual Studioでデバッグしてる場合は、Debugの項目からも指定できます。Visual Studioでデバッグ実行する際に環境変数を指定する方法は結構便利なので、皆さん覚えてくださいね。
便利は便利なのですが、本記事ではGit bashコマンドラインで直接指定して、OpenCVのテストプログラムを実行してみます。
以下が実行したときのログです
$ ./opencv_test_imgproc --gtest_filter=*CvtColor.BGR2YCrCb/0
CTEST_FULL_OUTPUT
OpenCV version: 4.0.0
OpenCV VCS version: 4.0.0
Build type: N/A
WARNING: build value differs from runtime: Release
Compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/x86_amd64/cl.exe (ver 19.0.24215.1)
Parallel framework: ms-concurrency
CPU features: SSE SSE2 SSE3 *SSE4.1 *SSE4.2 *FP16 *AVX *AVX2
Intel(R) IPP version: ippIP AVX2 (l9) 2017.0.3 (-) Jul 31 2017
OpenCL Platforms:
NVIDIA CUDA
dGPU: GeForce GTX 1060 (OpenCL 1.2 CUDA)
Intel(R) OpenCL
iGPU: Intel(R) UHD Graphics 620 (OpenCL 2.1 )
CPU: Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz (OpenCL 2.1 (Build 10))
Current OpenCL device:
Type = dGPU
Name = GeForce GTX 1060
Version = OpenCL 1.2 CUDA
Driver version = 411.63
Address bits = 64
Compute units = 10
Max work group size = 1024
Local memory size = 48 KB
Max memory allocation size = 1 GB 512 MB
Double support = Yes
Host unified memory = No
Device extensions:
cl_khr_global_int32_base_atomics
cl_khr_global_int32_extended_atomics
cl_khr_local_int32_base_atomics
cl_khr_local_int32_extended_atomics
cl_khr_fp64
cl_khr_byte_addressable_store
cl_khr_icd
cl_khr_gl_sharing
cl_nv_compiler_options
cl_nv_device_attribute_query
cl_nv_pragma_unroll
cl_nv_d3d10_sharing
cl_khr_d3d10_sharing
cl_nv_d3d11_sharing
cl_nv_copy_opts
cl_nv_create_buffer
Has AMD Blas = No
Has AMD Fft = No
Preferred vector width char = 1
Preferred vector width short = 1
Preferred vector width int = 1
Preferred vector width long = 1
Preferred vector width float = 1
Preferred vector width double = 1
Note: Google Test filter = *CvtColor.BGR2YCrCb/0
解説
この出力を全部読むと日が暮れるので、かいつまんで説明すると、OpenCVをロードした時点でデバイスの列挙が行われます
OpenCL Platforms:
NVIDIA CUDA
dGPU: GeForce GTX 1060 (OpenCL 1.2 CUDA)
Intel(R) OpenCL
iGPU: Intel(R) UHD Graphics 620 (OpenCL 2.1 )
CPU: Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz (OpenCL 2.1 (Build 10))
前述の通り、GeForce GTX 1060、Intel UHD Graphics 620とIntel Core i7-8650Uの3種類が列挙されています。その後読み解くと、以下のような出力があります。
Current OpenCL device:
Type = dGPU
Name = GeForce GTX 1060
Version = OpenCL 1.2 CUDA
dGPU
とは"Discrete GPU"の略で、いわゆるGPUのことです。広義のGPUには、CPU内蔵のIntel HD Graphicsも含みます。明示的に区別するためにCPU内蔵のGPUをiGPU
、独立したGPUをdGPU
と指定します。
これがデフォルトの挙動です。では、環境変数を指定してデバイスを切り替えてみましょう。
iGPU (Intel HD Graphics 620)を使う
起動時に変数名=値
というペアをコマンドの前に指定すると、環境変数を上書きできます。以下では:igpu
を環境変数に指定しています。(コロンを忘れないように)
$ OPENCV_OPENCL_DEVICE=:igpu ./opencv_test_imgproc --gtest_filter=*CvtColor.BGR2YCrCb/0
(中略)
Current OpenCL device:
Type = iGPU
Name = Intel(R) UHD Graphics 620
Version = OpenCL 2.1
先程と出力が代わり、iGPUを使用するように切り替わっていることがわかります。OpenCLの対応バージョンも変わっています。この様に、OpenCLのカーネルを実行するデバイスを、実行時(プログラム起動時)に切り替えられていることがわかります。
CPU (Intel Core i7 8650U)を使う
最近のCore i7 シリーズは、CPUでもOpenCLを実行できます。並列化したプログラムを、わざわざ書き換えなくても、一度書いたOpenCLのカーネルを再利用できます。
$ OPENCV_OPENCL_DEVICE=:cpu ./opencv_test_imgproc --gtest_filter=*CvtColor.BGR2YCrCb/0
(中略)
Current OpenCL device:
Type = CPU
Name = Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz
Version = OpenCL 2.1 (Build 10)
今度はちゃんとCPU上で実行されました。
その他実行時に参照される環境変数
書きなぐるだけなのも何なので、筆者が使ってる変数を抜粋した上で解説を書き加えておきます
OPENCV_CPU_DISABLE
- OpenCV 3.3 から導入されたDispatchの機能を使うことで、一部のCPUの特殊命令を使用せずに画像処理を行います。例えば、デフォルトのコンフィグだとSSE4、AVX、AVX2あたりの命令は実行時にチェックがおこなれます。このとき、
OPENCV_CPU_DISABLE=AVX
と設定することでAVX命令を使う部分を迂回して画像処理が行われます。これにより、実行結果が遅くなりますが、この差から、AVX版のパフォーマンス向上がどれ位なのか知ることができます。実行時に命令無効化が行われるので、ビルドし直す必要がありません。 - 当然ながらDispatchを利用せず、Baselineだけで指定した場合はこの環境変数は無視されます。
OPENCV_DUMP_CONFIG
- OpenCVのデバッグ情報を出力します。OpenCVをロードした時点で、DLL内部にあるビルド情報が吐き出されます。
OpenCV build configuration is:
General configuration for OpenCV 4.0.0 =====================================
Version control: 4.0.0
Platform:
Timestamp: 2018-11-20T14:48:49Z
Host: Windows 10.0.17134 AMD64
CMake: 3.11.0
CMake generator: Visual Studio 14 2015 Win64
CMake build tool: C:/Program Files (x86)/MSBuild/14.0/bin/MSBuild.exe
MSVC: 1900
CPU/HW features:
Baseline: SSE SSE2 SSE3
requested: SSE3
Dispatched code generation: SSE4_1 SSE4_2 FP16 AVX AVX2
requested: SSE4_1 SSE4_2 AVX FP16 AVX2 AVX512_SKX
SSE4_1 (5 files): + SSSE3 SSE4_1
SSE4_2 (2 files): + SSSE3 SSE4_1 POPCNT SSE4_2
FP16 (1 files): + SSSE3 SSE4_1 POPCNT SSE4_2 FP16 AVX
AVX (6 files): + SSSE3 SSE4_1 POPCNT SSE4_2 AVX
AVX2 (11 files): + SSSE3 SSE4_1 POPCNT SSE4_2 FP16 FMA3 AVX AVX2
(中略)
Install to: C:/work/opencv-fork/build/install
-----------------------------------------------------------------
- 内部では
getBuildInformation()
を呼んでるだけです。空文字以外を指定するとビルド情報が出力されます2。
if (getenv("OPENCV_DUMP_CONFIG"))
{
fprintf(stderr, "\nOpenCV build configuration is:\n%s\n",
cv::getBuildInformation().c_str());
}
OPENCV_FOR_THREADS_NUM
-
CPU上で並列実行の並列数を指定します。
imgproc
モジュールに代表されるような、全画素をなめるようなループをCPU実行する場合、OpenCVは内部でparallell_for
で実行します。 -
デフォルトではCPUの論理スレッド数が指定されます。(ただし、Android版では2スレッドがデフォルト値)この値を変える事によって、コア数の違いでパフォーマンスがスケールするか実験できます。また、OpenCV内部をデバッグする場合、並列に処理される部分はトレースが著しく面倒くさいのですが、この変数を1に設定することで並列化を一時的に無効化できます。OpenCVビルド職人がデバッグする際に重宝する環境変数です。
-
無指定の場合
$ opencv_perf_imgproc --gtest_filter=Size_CvtMode_cvtColor8u.cvtColor8u/123
Time compensation is 0
CTEST_FULL_OUTPUT
(中略)
[ RUN ] Size_CvtMode_cvtColor8u.cvtColor8u/123, where GetParam() = (640x480, COLOR_BGR2GRAY)
[ PERFSTAT ] (samples=100 mean=0.20 median=0.19 min=0.19 stddev=0.01 (4.4%))
[ OK ] Size_CvtMode_cvtColor8u.cvtColor8u/123 (106 ms)
- スレッド数を1に固定した場合
$ OPENCV_FOR_THREADS_NUM=1 opencv_perf_imgproc --gtest_filter=Size_CvtMode_cvtColor8u.cvtColor8u/123
Time compensation is 0
CTEST_FULL_OUTPUT
(中略)
[ RUN ] Size_CvtMode_cvtColor8u.cvtColor8u/123, where GetParam() = (640x480, COLOR_BGR2GRAY)
[ PERFSTAT ] (samples=100 mean=0.42 median=0.40 min=0.38 stddev=0.04 (8.7%))
[ OK ] Size_CvtMode_cvtColor8u.cvtColor8u/123 (223 ms)
- 100回計測した中央値が
median
に表示されてますが、倍遅くなってることが分かります3。
OPENCV_IPP
- IPPライブラリが実行するモジュールを実行時に選択できる
OPENCV_IPP=disabled|sse42|avx2|avx512
- disabledにするとOpenCV独自の実装が呼ばれる。またavx512は64bit環境下でのみ選べる。
- 複数の項目は指定できないし、一つ選択すると、下位の命令セットも自動的に有効になる。(avx2を指定するとsse42も有効になる)
- IPP 自体の設定はもっと細かいのだが、OpenCVでまとめられて、指定できるのは前述の3つ+disabledのみ
OPENCV_LEGACY_WAITKEY
- 特殊キーを拾う挙動が3.2から変わりました4。その挙動を3.2より前に戻すためにはこの環境変数を使用します。空文字以外を指定すると3.2より前の挙動に戻ります。
一方で、 cv::waitKey は、特殊キーが押されても反応するが、キーコードは0が返される。(矢印キーとかファンクションキーとか)。この挙動は、環境変数 OPENCV_LEGACY_WAITKEY に1を設定することで、3.2.0 より前の挙動と一致する。
— Tomoaki Teshima (@tomoaki_teshima) 2018年5月21日
OPENCV_OPENCL_DEVICE
- 前述の通り
OPENCV_OPENCL_DEVICE=disabled|platform:deviceTypes:deviceName
-
disabled
は問答無用に無効化します。 - deviceTypesは現状以下の中から選びます
gpu
dgpu
igpu
cpu
accelerator
all
- deviceNameは
- 1桁の数字の場合は列挙されたn番目のデバイスを選ぶ
- 2桁以上の場合は名前の中に含まれた数字にマッチするかどうか
- ex: 1080 -> Geforce GTX1080
- ex: 580 -> Radeon RX 580, Geforce GTX 580
- ex: 620 -> Intel(R) UHD Graphics 620
OPENCV_OPENCL_RUNTIME
- OpenCL.dllもしくはlibOpenCL.soをロードするためのパスを環境変数で指定できる
- disabledを指定すると、libOpenCL.soのロードを諦める(
OPENCV_OPENCL_DEVICE=disabled
と等価)
OPENCV_TEST_DATA_PATH
- opencv_extraリポジトリのパス。テスト実行時にこのパスが参照され、リポジトリ内にあるテストデータやリファレンスの結果が参照され、テストが実行される。
OPENCV_TRACE
-
TRACE
に関してはdandelion1124先生に聞いて下さい。
OPENCV_VIDEOCAPTURE_DEBUG
- OpenCVのビデオ読み込み周りのバックグラウンドエンジンからのデバッグ情報を表示する。
ビルド時に参照される環境変数一覧
特定のライブラリと、OpenCVを連携することができます。このとき、連携させるライブラリがどこにインストールされているか、環境変数に登録するライブラリが多いです。5
この環境変数が設定されており、かつ環境変数が示す先に実体が存在するとき、OpenCVは指定されたライブラリと連携したビルドを試します。
- ANT_DIR
- Atlas_ROOT_DIR
- DESTDIR
- EIGEN_ROOT
- GSTREAMER_DIR
- HOME
- IPP_ASYNC_ROOT
- MFX_HOME
- MKLROOT
- NVSDKCOMPUTE_ROOT
- OPEN_NI_INSTALL_PATH
- OPEN_NI_INSTALL_PATH64
- OPEN_NI_LIB
- OPEN_NI_LIB64
- OpenBLAS_HOME
- OpenBLAS
- OPENCV_DOWNLOAD_PATH
- OPENCV_IPP_PATH
- OPENNI2_INCLUDE
- OPENNI2_INCLUDE64
- OPENNI2_LIB
- OPENNI2_LIB64
- OPENNI2_REDIST
- PCSDK_DIR
- ProgramFiles
- SystemDrive
- TBBROOT
- VA_INTEL_IOCL_ROOT
- VA_INTEL_MSDK_ROOT
終わりに
OpenCV を再ビルドするの、疲れますよね。(疲れないけれど)
筆者は以下の環境で確認しました
- OpenCV 4.0.0
- Windows 10 64bit
- Visual Studio Community 2015 (Update 3)
- CMake 3.11.0
- CPU Intel Core i7 8650U
- iGPU Intel HD Graphics 620
- dGPU NVIDIA Geforce GTX 1060
明日も私の予定で、なんかネタを考えます。