はじめに
- 今日はWindows + ARM64 + Visual Studioという組み合わせでビルドに挑戦します。
- 小ネタですが完走のためにOpenCV Advent Calendar 2023 14日目の記事として投稿します
- 他の記事は目次からご覧ください
準備編
-
まずはVisual Studio をARM64用に追加セットアップします。手元のVisual Studio はCommunity Edition 2022でした。ビルド用マシンが
x86_64
、ターゲットのシステムがARM64
です。 -
ARM64
環境がセットアップされてる場合はスキップして問題ありません。 -
Visual Studio Installer (VisualStudioSetup.exe) を起動し、インストールされてるVisual Studioを表示します。
-
緑色でハイライトしたIndivisual Componentsでコンポーネントを出し、
ARM64
で検索してみましょう。ARM64、Aarch64、Armv8や64bit Armなど表記は色々あるのですが、Visual StudioではARM64表記で統一しているようです。
-
リストの中から
MSVC v143 - VS 2022 C++ ARM64 build tools (Latest)
とMSVC v143 - VS 2022 C++ ARM64 Spectre-mitigated libs (Latest)
を選択し、Modify
を選択して順次インストールします。 -
ITMediaさんの記事によるとVisual Studio 2017 から対応していたそうです。
-
新規インストールする場合も同様のメニューからコンポーネントの追加が可能です。
ビルド(1回目)、コケる
- 続いて、CMakeでアーキテクチャを指定します。CMakeを起動して、buildディレクトリを適当に指定し、OpenCVのソースディレクトリを指定しましょう。なお本記事はgit版のソースコード
6ee71fee88d26df529eaad7209dbdcd3baf86577
を利用しました。4.8.1と4.9.0のリリースの間の開発版です。 - Configure を選んだときにコンパイラはVisual Studio、アーキテクチャは
ARM64
を選択します
-
Finish
をクリックします
Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx64/arm64/cl.exe - skipped
Detecting CXX compile features
Detecting CXX compile features - done
Detecting C compiler ABI info
Detecting C compiler ABI info - done
Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx64/arm64/cl.exe - skipped
Detecting C compile features
Detecting C compile features - done
Detected processor: AMD64 ←おや?
Performing Test HAVE_CPU_SSE3_SUPPORT (check file: cmake/checks/cpu_sse3.cpp)
Performing Test HAVE_CPU_SSE3_SUPPORT - Failed ←おや?
SSE3 is not supported by C++ compiler
- Configureは完走するのですが、前述のログを覗くと
AMD64
とかSSE
とか、なんかおかしなキーワードが出てきます。 - これについてググると色々出てくるのですが、OpenCVの中の人の一人、mshabuninさんが公開してるスクリプトが助けてくれます。
cmake -G"Visual Studio 16 2019" \
-A ARM64 \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_SYSTEM_VERSION=10.0 \
-DCMAKE_SYSTEM_PROCESSOR=ARM64 \
-DWITH_IPP=OFF \
-DWITH_FFMPEG=OFF \
../opencv
- どうやらコンパイラとアーキテクチャを指定するだけでなく、
-
CMAKE_SYSTEM_NAME=Windows
と -
CMAKE_SYSTEM_PROCESSOR=ARM64
を設定する必要があるみたいです。 - また他にも環境に応じてビルド対象のモジュールを
ON/OFF
する必要があるようです。
-
- では早速設定してリトライしてみましょう。なお、肝となる
CMAKE_SYSTEM_NAME
オプションとCMAKE_SYSTEM_PROCESSOR
オプションはConfigure前に指定する必要があり、筆者の知る限りGUI版のCMakeからこのオプションを指定することはできません。ですので、ここではGit For Windowsに同梱されてるbashで作業を続けます
ビルド(2回目)、CMakeの一部でコケる
- コマンド
$ cmake -G"Visual Studio 17 2022" \
-A ARM64 \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_SYSTEM_PROCESSOR=ARM64 \
-DPYTHON2_EXECUTABLE= \
..
- ここでオプションを適切に選んで実行すると、以下のようにConfigureが完走します。が、、、
-- Performing Test HAVE_CPU_NEON_SUPPORT (check file: cmake/checks/cpu_neon.cpp)
-- Performing Test HAVE_CPU_NEON_SUPPORT - Success
-- Performing Test HAVE_CPU_FP16_SUPPORT (check file: cmake/checks/cpu_fp16.cpp)
-- Performing Test HAVE_CPU_FP16_SUPPORT - Failed
-- FP16 is not supported by C++ compiler
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+DOTPROD (check file: cmake/checks/cpu_neon_dotprod.cpp)
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+DOTPROD - Failed
-- NEON_DOTPROD is not supported by C++ compiler
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16 (check file: cmake/checks/cpu_neon_fp16.cpp)
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16 - Failed
-- NEON_FP16 is not supported by C++ compiler
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16+BF16 (check file: cmake/checks/cpu_neon_bf16.cpp)
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16+BF16 - Failed
-- NEON_BF16 is not supported by C++ compiler
-- Optimization FP16 is not available, skipped
-- Dispatch optimization NEON_FP16 is not available, skipped
-- Dispatch optimization NEON_BF16 is not available, skipped
-- Dispatch optimization NEON_DOTPROD is not available, skipped
-- CPU/HW features:
-- Baseline: NEON
-- requested: NEON FP16
-- Dispatched code generation:
-- requested: NEON_FP16 NEON_BF16 NEON_DOTPROD
- mashabuninさんに感謝しつつcmakeを走らせると、無事完走してソリューションファイルが作られるものの、途中のログに不穏なメッセージが出ています。
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+DOTPROD - Failed
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16 - Failed
-- Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16+BF16 - Failed
- この抜粋した3行は、CMakeによるコンパイラがオプションに対応しているかどうかをチェックしている部分です。
- 具体的には次に示す表の用に、「コンパイラオプション」から自動的にメッセージが生成さて、CMakeの変数としてコンパイルの可否が保存されます。賢いですね!
メッセージ | コンパイラオプション | 結果 |
---|---|---|
Performing Test HAVE_CXX_MARCH_ARMV8_2_A+DOTPROD - Failed | -march=armv8.2-a+dotprod | Failed |
Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16 - Failed | -march=armv8.2-a+fp16 | Failed |
Performing Test HAVE_CXX_MARCH_ARMV8_2_A+FP16+BF16 - Failed | -march=armv8.2-a+fp16+BF16 | Failed |
- 不穏なメッセージと呼んだのは、「コンパイラオプション」の列で、こちらは見てわかるようにGCC/Clang用のオプションがテストされています。奇しくも12日目の記事で筆者が触れた通りの状況にぶち当たりました
(引用) 現状のOpenCVのコードは、Aarch64用のコードをVisual Studioでビルドする想定に全くなってない
- 以下該当する部分の一部抜粋とコメント追記
elseif(ARM OR AARCH64) # 32bit Arm及び64bit Arm用設定
ocv_update(CPU_NEON_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon.cpp")
ocv_update(CPU_FP16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_fp16.cpp")
ocv_update(CPU_NEON_FP16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon_fp16.cpp")
ocv_update(CPU_NEON_BF16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon_bf16.cpp")
ocv_update(CPU_NEON_DOTPROD_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon_dotprod.cpp")
if(NOT AARCH64)
# 32bit Arm用設定
else()
# 64bit Arm用設定
ocv_update(CPU_KNOWN_OPTIMIZATIONS "NEON;FP16;NEON_DOTPROD;NEON_FP16;NEON_BF16")
ocv_update(CPU_NEON_FLAGS_ON "")
ocv_update(CPU_FP16_IMPLIES "NEON")
ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "-march=armv8.2-a+dotprod")
ocv_update(CPU_NEON_DOTPROD_IMPLIES "NEON")
ocv_update(CPU_NEON_FP16_FLAGS_ON "-march=armv8.2-a+fp16")
ocv_update(CPU_NEON_FP16_IMPLIES "NEON")
ocv_update(CPU_NEON_BF16_FLAGS_ON "-march=armv8.2-a+fp16+bf16")
ocv_update(CPU_NEON_BF16_IMPLIES "NEON")
set(CPU_BASELINE "NEON;FP16" CACHE STRING "${HELP_CPU_BASELINE}")
set(CPU_DISPATCH "NEON_FP16;NEON_BF16;NEON_DOTPROD" CACHE STRING "${HELP_CPU_DISPATCH}")
endif()
elseif(MIPS)
- これだけ見ると特に違和感がないのですが、実は
x86_64
部分と見比べると、Arm
部分の最適化のオプションはGCC/Clangしか想定していないことが伺い知れます
if(X86 OR X86_64)
ocv_update(CPU_KNOWN_OPTIMIZATIONS "SSE;SSE2;SSE3;SSSE3;SSE4_1;POPCNT;SSE4_2;FP16;FMA3;AVX;AVX2;AVX_512F;AVX512_COMMON;AVX512_KNL;AVX512_KNM;AVX512_SKX;AVX512_CNL;AVX512_CLX;AVX512_ICL")
# 以下既存のAVX命令一覧を羅列
ocv_update(CPU_SSE_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_sse.cpp")
# 以下各命令チェック用のファイル一覧を羅列
if(CV_ICC OR CV_ICX)
# ICC Intel C++ Compiler 用の設定
elseif(CV_GCC OR CV_CLANG OR CV_ICX)
# GCC, Clang, Intel LLVM 用の設定
elseif(MSVC)
# Visual Studio 用の設定
else()
message(WARNING "TODO: Unsupported compiler")
endif()
# 一部省略
elseif(ARM OR AARCH64)
- この用に、
x86_64
では各コンパイラごとにセクション分けされているものの、現状ARM64
はGCC or Clang でLinux用にビルドしか想定されてないように見えます1 - ですので、まずはGCC用のコンパイルオプションをVisual Studioに渡さないように修正します
if(NOT AARCH64)
# 中略
else()
ocv_update(CPU_KNOWN_OPTIMIZATIONS "NEON;FP16;NEON_DOTPROD;NEON_FP16;NEON_BF16")
ocv_update(CPU_NEON_FLAGS_ON "")
ocv_update(CPU_FP16_IMPLIES "NEON")
+ if(NOT MSVC)
ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "-march=armv8.2-a+dotprod")
ocv_update(CPU_NEON_DOTPROD_IMPLIES "NEON")
ocv_update(CPU_NEON_FP16_FLAGS_ON "-march=armv8.2-a+fp16")
ocv_update(CPU_NEON_FP16_IMPLIES "NEON")
ocv_update(CPU_NEON_BF16_FLAGS_ON "-march=armv8.2-a+fp16+bf16")
ocv_update(CPU_NEON_BF16_IMPLIES "NEON")
+ else()
+ ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "")
+ ocv_update(CPU_NEON_DOTPROD_IMPLIES "NEON")
+ ocv_update(CPU_NEON_FP16_FLAGS_ON "")
+ ocv_update(CPU_NEON_FP16_IMPLIES "NEON")
+ ocv_update(CPU_NEON_BF16_FLAGS_ON "")
+ ocv_update(CPU_NEON_BF16_IMPLIES "NEON")
+ endif()
- 色々試したのですが、Visual Studio 2022 だと、
NEON
、FP16
、NEON_DOTPROD
、NEON_FP16
あたりは特にオプションを指定せずともコンパイルできるようです
ビルド(3回目)、CMakeの一部でコケる(2回目)
- キャッシュをクリアして再度cmakeを走らせましょう。
$ rm -rf *
$ cmake -G"Visual Studio 17 2022" \
-A ARM64 \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_SYSTEM_PROCESSOR=ARM64 \
-DPYTHON2_EXECUTABLE= \
..
<中略>
-- Performing Test HAVE_CPU_NEON_SUPPORT (check file: cmake/checks/cpu_neon.cpp)
-- Performing Test HAVE_CPU_NEON_SUPPORT - Success
-- Performing Test HAVE_CPU_FP16_SUPPORT (check file: cmake/checks/cpu_fp16.cpp)
-- Performing Test HAVE_CPU_FP16_SUPPORT - Failed
-- FP16 is not supported by C++ compiler
-- Performing Test HAVE_CPU_NEON_DOTPROD_SUPPORT (check file: cmake/checks/cpu_neon_dotprod.cpp)
-- Performing Test HAVE_CPU_NEON_DOTPROD_SUPPORT - Failed
-- NEON_DOTPROD is not supported by C++ compiler
-- Performing Test HAVE_CPU_NEON_FP16_SUPPORT (check file: cmake/checks/cpu_neon_fp16.cpp)
-- Performing Test HAVE_CPU_NEON_FP16_SUPPORT - Failed
-- NEON_FP16 is not supported by C++ compiler
-- Performing Test HAVE_CPU_NEON_BF16_SUPPORT (check file: cmake/checks/cpu_neon_bf16.cpp)
-- Performing Test HAVE_CPU_NEON_BF16_SUPPORT - Failed
-- NEON_BF16 is not supported by C++ compiler
-- Optimization FP16 is not available, skipped
-- Dispatch optimization NEON_FP16 is not available, skipped
-- Dispatch optimization NEON_BF16 is not available, skipped
-- Dispatch optimization NEON_DOTPROD is not available, skipped
<中略>
-- CPU/HW features:
-- Baseline: NEON
-- requested: NEON FP16
-- Dispatched code generation:
-- requested: NEON_FP16 NEON_BF16 NEON_DOTPROD
- とりあえずCMakeは完走するのですが、今度は
FP16
が対応していないと主張します。
-- Baseline: NEON
-- requested: NEON FP16
- 前述のリストが表すのは
- OpenCVとしてはARM64では
NEON
とFP16
命令は無条件で使えると仮定している - が、
FP16
命令を使ったコンパイルに失敗したのでFP16
命令は使わない
- OpenCVとしてはARM64では
- と判断された、ということです。とはいえ、
FP16
命令は実質変換の2命令しかないのに、何故コケるのでしょうか?もう少し覗いてみましょう。
OpenCVのビルドの裏側を支えるCMake
- GCCやClangは様々なプラットフォーム用にコンパイルすることを想定しているため、ターゲットのシステムに応じてコンパイルオプションが細々と設定されております。
- コンパイラがそもそも当該オプションを受け付けるか?というのをチェックするため、OpenCVにはチェック用のコードが書かれた小さいファイルたちが存在します。
- 前述のVisual Studio用に編集したCMakeの少し前を見てみましょう。
elseif(ARM OR AARCH64)
ocv_update(CPU_NEON_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon.cpp")
ocv_update(CPU_FP16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_fp16.cpp")
ocv_update(CPU_NEON_FP16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon_fp16.cpp")
ocv_update(CPU_NEON_BF16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon_bf16.cpp")
ocv_update(CPU_NEON_DOTPROD_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_neon_dotprod.cpp")
if(NOT AARCH64)
# 32bit Arm向けなので省略
else()
ocv_update(CPU_KNOWN_OPTIMIZATIONS "NEON;FP16;NEON_DOTPROD;NEON_FP16;NEON_BF16")
if(NOT MSVC)
# GCC/Clang用のコードなので省略
else()
ocv_update(CPU_NEON_FLAGS_ON "")
ocv_update(CPU_FP16_IMPLIES "NEON")
ocv_update(CPU_NEON_DOTPROD_FLAGS_ON "")
ocv_update(CPU_NEON_DOTPROD_IMPLIES "NEON")
ocv_update(CPU_NEON_FP16_FLAGS_ON "")
ocv_update(CPU_NEON_FP16_IMPLIES "NEON")
ocv_update(CPU_NEON_BF16_FLAGS_ON "")
ocv_update(CPU_NEON_BF16_IMPLIES "NEON")
endif()
- 何やら、
CPU_FP16_TEST_FILE
といった変数が設定され、ファイル名が設定されているのが見えます
ocv_update(CPU_FP16_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_fp16.cpp")
- 実際にOpenCVのソースコードを覗くと、当該ファイルが
cmake/checks
ディレクトリ以下に確認できます。実はOpenCVのCMakeステージの裏側ではこのテストファイルをコンパイラでコンパイルし、コンパイルに成功しなければ当該拡張命令を諦める、という仕事をします(CMakeが) - 今失敗している
FP16
命令をチェックするコードを見てみましょう
#include <stdio.h>
#if defined __F16C__ || (defined _MSC_VER && _MSC_VER >= 1700 && defined __AVX__) || (defined __INTEL_COMPILER && defined __AVX__)
#include <immintrin.h>
int test()
{
const float src[] = { 0.0f, 0.0f, 0.0f, 0.0f };
short dst[8];
__m128 v_src = _mm_load_ps(src);
__m128i v_dst = _mm_cvtps_ph(v_src, 0);
_mm_storel_epi64((__m128i*)dst, v_dst);
return (int)dst[0];
}
#elif defined __GNUC__ && (defined __arm__ || defined __aarch64__)
#include "arm_neon.h"
int test()
{
const float src[] = { 0.0f, 0.0f, 0.0f, 0.0f };
short dst[8];
float32x4_t v_src = *(float32x4_t*)src;
float16x4_t v_dst = vcvt_f16_f32(v_src);
*(float16x4_t*)dst = v_dst;
return (int)dst[0];
}
#else
#error "FP16 is not supported"
#endif
int main()
{
printf("%d\n", test());
return 0;
}
- このコードを眺めると途中で
vcvt_f16_f32
命令を呼んでいます。この命令はfloat
のベクトルを受け取り、各要素をhalf
に変換して返す命令です。
float16x4_t v_dst = vcvt_f16_f32(v_src);
- ところが、プリプロセッサを読み解くと、ARM64用の設定箇所にVisual Studioでは到達できません。
#if defined __F16C__ || (defined _MSC_VER && _MSC_VER >= 1700 && defined __AVX__) || (defined __INTEL_COMPILER && defined __AVX__)
// ここはx86_64 + (GCC/Clang/Visual Studio/Intel Compiler) 用設定
#elif defined __GNUC__ && (defined __arm__ || defined __aarch64__)
// ここは(ARM 32bit / ARM64) + GCC 向け設定
#else
#error "FP16 is not supported" ←今回はここにたどり着き、コンパイルに失敗する
#endif
- x86_64用設定があるのは、全く同じサブセットがたまたま存在していたからなのですが、Visual Studio でのビルドはx86_64しか想定されていません。ここはプリプロセッサを書き換えてあげましょう。コンパイラは
_MSC_VER
でVisual Studioと分かるし、Visual StudioでARM64向けにビルドするときは_M_ARM64
が定義されます
(引用) _M_ARM64 ARM64 を対象とするコンパイルでは 1 として定義されます。 それ以外の場合は、定義されません。
-
他にも、内積命令、bfloat16用命令などのテストコードがありますが、どれもプリプロセッサがVisual Studioを想定していないので、合計4ファイル修正しましょう。
-
cmake/checks/cpu_fp16.cpp
-#elif defined __GNUC__ && (defined __arm__ || defined __aarch64__)
+#elif (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64)
-
cmake/checks/cpu_neon_{dotprod,fp16,bf16}.cpp
いずれも同様
-#if defined __GNUC__ && (defined __arm__ || defined __aarch64__)
+#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64)
- なお、筆者の試した範囲だとVisual Studdio 2022 だと
bf16
用拡張命令だけまだ対応していないようです。有効にするオプションがどこか別にあったりするんでしょうか?それともVisual Studioのバージョンを上げると対応する?
ビルド(4回目)、CMake通る、コンパイルできたか!?(できてない)
-- Performing Test HAVE_CPU_NEON_SUPPORT (check file: cmake/checks/cpu_neon.cpp)
-- Performing Test HAVE_CPU_NEON_SUPPORT - Success
-- Performing Test HAVE_CPU_FP16_SUPPORT (check file: cmake/checks/cpu_fp16.cpp)
-- Performing Test HAVE_CPU_FP16_SUPPORT - Success
-- Performing Test HAVE_CPU_NEON_DOTPROD_SUPPORT (check file: cmake/checks/cpu_neon_dotprod.cpp)
-- Performing Test HAVE_CPU_NEON_DOTPROD_SUPPORT - Success
-- Performing Test HAVE_CPU_NEON_FP16_SUPPORT (check file: cmake/checks/cpu_neon_fp16.cpp)
-- Performing Test HAVE_CPU_NEON_FP16_SUPPORT - Success
-- Performing Test HAVE_CPU_NEON_BF16_SUPPORT (check file: cmake/checks/cpu_neon_bf16.cpp)
-- Performing Test HAVE_CPU_NEON_BF16_SUPPORT - Failed
-- NEON_BF16 is not supported by C++ compiler
-- Dispatch optimization NEON_BF16 is not available, skipped
-- CPU/HW features:
-- Baseline: NEON FP16
-- Dispatched code generation: NEON_DOTPROD NEON_FP16
-- requested: NEON_FP16 NEON_BF16 NEON_DOTPROD
-- NEON_DOTPROD (1 files): + NEON_DOTPROD
-- NEON_FP16 (2 files): + NEON_FP16
--
- CMakeも無事完走しますので、ビルドしましょう。前節で触れた通り、
NEON_BF16
はコンパイラが対応していないと判断されて当該拡張命令がオフにされます。とりあえず今はビルドできるか試してみましょう。
zlib.vcxproj -> C:\opencv\cross_build\3rdparty\lib\Release\zlib.lib
------ Build started: Project: opencv_core_NEON_DOTPROD, Configuration: Release ARM64 ------
Building Custom Rule C:/opencv/modules/core/CMakeLists.txt
matmul.neon_dotprod.cpp
C:\opencv\modules\core\include\opencv2/core/cvdef.h(904,9): error C2065: '__m128': undeclared identifier
C:\opencv\modules\core\include\opencv2/core/cvdef.h(904,16): error C2146: syntax error: missing ';' before identifier 'v'
C:\opencv\modules\core\include\opencv2/core/cvdef.h(904,16): error C2065: 'v': undeclared identifier
C:\opencv\modules\core\include\opencv2/core/cvdef.h(904,20): error C3861: '_mm_load_ss': identifier not found
C:\opencv\modules\core\include\opencv2/core/cvdef.h(905,52): error C2065: 'v': undeclared identifier
C:\opencv\modules\core\include\opencv2/core/cvdef.h(905,39): error C3861: '_mm_cvtps_ph': identifier not found
C:\opencv\modules\core\include\opencv2/core/cvdef.h(905,21): error C3861: '_mm_cvtsi128_si32': identifier not found
C:\opencv\modules\core\include\opencv2/core/cvdef.h(936,39): error C3861: '_mm_cvtsi32_si128': identifier not found
C:\opencv\modules\core\include\opencv2/core/cvdef.h(936,26): error C3861: '_mm_cvtph_ps': identifier not found
C:\opencv\modules\core\include\opencv2/core/cvdef.h(936,9): error C3861: '_mm_store_ss': identifier not found
- おや、大量のビルドエラーが出ます。
cvdef.h
内でコンパイルエラーが起きている模様です。少し覗いてみましょう。
#if defined __ARM_FP16_FORMAT_IEEE \
&& !defined __CUDACC__
# define CV_FP16_TYPE 1
#else
# define CV_FP16_TYPE 0
#endif
namespace cv
{
class float16_t
{
public:
#if CV_FP16_TYPE
// CV_FP16_TYPE は0と定義されていた
#else
float16_t() : w(0) {}
explicit float16_t(float x)
{
#if CV_FP16
__m128 v = _mm_load_ss(&x); // ←ここでコンパイルにこける
w = (ushort)_mm_cvtsi128_si32(_mm_cvtps_ph(v, 0));
-
_mm_load_ss
!!!まさかのx86_64
命令に突き当たります!ドウシテコウナッタ。。。。 - コンパイルは
cv::float16_t
クラスのコンパイルでコケています。- これは
ARM/ARM64
向けのGCCではfloat16_t
という型がすでにネイティブにサポートされています。 - しかし
x86_64
向けのコンパイラ、及びARM/ARM64
向けでもVisual Studioではネイティブにサポートされていない型ですので、代わりとしてラッパークラスのcv::float16_t
が定義されています。 - ラッパークラスが定義されますが、基本
ARM64
向けコンパイラはネイティブにfloat16_t
型をサポートするだろう、つまりfloat16_t
をサポートしないコンパイラはx86_64
向けであろう というロジックに基づき、x86_64
用命令に突き当たります
- これは
一応の迂回策
- 本記事執筆にあたりあーだこーだ色々試してみたのですが、前述のCMakeの修正を施した上で、以下のコンフィグをするとビルドは完了します
$ cmake -G"Visual Studio 17 2022" \
-A ARM64 \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_SYSTEM_PROCESSOR=ARM64 \
-DPYTHON2_EXECUTABLE= \
-DCPU_BASELINE=NEON
..
- ただし筆者がARM64が動くWindowマシンを持ってないため、実際にロジックが正しいか、テストがPASSするかどうかは確認が取れていません。あしからず
おわりに
- やはりARM64向け+Visual Studioのビルドはあまり想定されていないように見える
-
cmake
ディレクトリにある以下のファイルたちは修正が必要OpenCVCompilerOptimizations.cmake
checks/cpu_fp16.cpp
checks/cpu_neon_bf16.cpp
checks/cpu_neon_fp16.cpp
checks/cpu_neon_dotprod.cpp
- それに加え、CMakeのオプションをコマンドラインから以下のように叩く
$ cmake -G"Visual Studio 17 2022" \
-A ARM64 \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_SYSTEM_PROCESSOR=ARM64 \
-DPYTHON2_EXECUTABLE= \
-DCPU_BASELINE=NEON \
..
- 誰か検証用に
ARM64
で動くWindowsの実機を下さい。 - 明日はfukushima1981先生の予定で、記事執筆時点でのタイトルは「bfloat16を対応していないCPUで使う方法(OpenCVはMatしか使わない)」です。みんな大好き
bfloat16
!次回もサービスサービスゥ!
再現環境
- OS: Windows 11
- Compiler: Visual Studio 17 2022 Community Edition (17.4.3)
- CMake: 3.25.1 2
- OpenCV: 4.8.0-dev (commit
6ee71fee88d26df529eaad7209dbdcd3baf86577
4.8.1と4.9.0の間の開発版)
ビルド完走したときのCMake Configuration
-- General configuration for OpenCV 4.8.0-dev =====================================
-- Version control: 4.8.0-496-g6ee71fee88-dirty
--
-- Platform:
-- Timestamp: 2023-12-13T14:00:20Z
-- Host: Windows 10.0.22621 AMD64
-- Target: Windows ARM64
-- CMake: 3.25.1
-- CMake generator: Visual Studio 17 2022
-- CMake build tool: C:/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/amd64/MSBuild.exe
-- MSVC: 1934
-- Configuration: Debug Release
--
-- CPU/HW features:
-- Baseline: NEON
-- Dispatched code generation: NEON_DOTPROD NEON_FP16
-- requested: NEON_FP16 NEON_BF16 NEON_DOTPROD
-- NEON_DOTPROD (1 files): + NEON_DOTPROD
-- NEON_FP16 (2 files): + NEON_FP16
--
-- C/C++:
-- Built as dynamic libs?: YES
-- C++ standard: 11
-- C++ Compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx64/arm64/cl.exe (ver 19.34.31937.0)
-- C++ flags (Release): /DWIN32 /D_WINDOWS /W4 /GR /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE /D _SCL_SECURE_NO_WARNINGS /Gy /bigobj /D _ARM64_DISTINCT_NEON_TYPES /Oi /fp:precise /EHa /wd4127 /wd4251 /wd4324 /wd4275 /wd4512 /wd4589 /wd4819 /MP /MD /O2 /Ob2 /DNDEBUG
-- C++ flags (Debug): /DWIN32 /D_WINDOWS /W4 /GR /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE /D _SCL_SECURE_NO_WARNINGS /Gy /bigobj /D _ARM64_DISTINCT_NEON_TYPES /Oi /fp:precise /EHa /wd4127 /wd4251 /wd4324 /wd4275 /wd4512 /wd4589 /wd4819 /MP /MDd /Zi /Ob0 /Od /RTC1
-- C Compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx64/arm64/cl.exe
-- C flags (Release): /DWIN32 /D_WINDOWS /W3 /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE /D _SCL_SECURE_NO_WARNINGS /Gy /bigobj /D _ARM64_DISTINCT_NEON_TYPES /Oi /fp:precise /MP /MD /O2 /Ob2 /DNDEBUG
-- C flags (Debug): /DWIN32 /D_WINDOWS /W3 /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE /D _SCL_SECURE_NO_WARNINGS /Gy /bigobj /D _ARM64_DISTINCT_NEON_TYPES /Oi /fp:precise /MP /MDd /Zi /Ob0 /Od /RTC1
-- Linker flags (Release): /machine:ARM64 /INCREMENTAL:NO
-- Linker flags (Debug): /machine:ARM64 /debug /INCREMENTAL
-- ccache: NO
-- Precompiled headers: YES
-- Extra dependencies:
-- 3rdparty dependencies:
--
-- OpenCV modules:
-- To be built: calib3d core dnn features2d flann gapi highgui imgcodecs imgproc ml objdetect photo stitching ts video videoio
-- Disabled: world
-- Disabled by dependency: -
-- Unavailable: java python2 python3
-- Applications: tests perf_tests apps
-- Documentation: NO
-- Non-free algorithms: NO
--
-- Windows RT support: NO
--
-- GUI: WIN32UI
-- Win32 UI: YES
--
-- Media I/O:
-- ZLib: build (ver 1.2.13)
-- JPEG: build-libjpeg-turbo (ver 2.1.3-62)
-- SIMD Support Request: YES
-- SIMD Support: YES
-- WEBP: build (ver encoder: 0x020f)
-- PNG: build (ver 1.6.37)
-- TIFF: build (ver 42 - 4.2.0)
-- JPEG 2000: build (ver 2.5.0)
-- OpenEXR: build (ver 2.3.0)
-- HDR: YES
-- SUNRASTER: YES
-- PXM: YES
-- PFM: YES
--
-- Video I/O:
-- DC1394: NO
-- FFMPEG: YES (prebuilt binaries)
-- avcodec: YES (58.134.100)
-- avformat: YES (58.76.100)
-- avutil: YES (56.70.100)
-- swscale: YES (5.9.100)
-- avresample: YES (4.0.0)
-- GStreamer: NO
-- DirectShow: YES
-- Media Foundation: YES
-- DXVA: YES
--
-- Parallel framework: Concurrency
--
-- Trace: YES (with Intel ITT)
--
-- Other third-party libraries:
-- Lapack: NO
-- Custom HAL: YES (carotene (ver 0.0.1))
-- Protobuf: build (3.19.1)
-- Flatbuffers: builtin/3rdparty (23.5.9)
--
-- OpenCL: YES (NVD3D11)
-- Include path: C:/opencv/3rdparty/include/opencl/1.2
-- Link libraries: Dynamic load
--
-- Install to: C:/opencv/cross_build/install
-- -----------------------------------------------------------------