ふかうら王(やねうら王のMCTS+DNNバージョン)でMacのGPU, Neural Engineを使うことでMac上で高速動作できるか検証しました。しかし、今回のやり方ではうまくいきませんでした。
現象: onnxruntime上でCoreMLを利用する設定をしたが、onnxruntimeデフォルトのCPUプロバイダ利用時と速度が変わらない
失敗原因の仮説
- (今回実験した)Intel MacではCoreMLがCPUしか使わない
- dlshogiのモデルで使われているオペレータが、onnxruntimeがCoreMLを使う条件にヒットしない
実験手順メモ
今回は、ふかうら王で既にサポートされているonnxruntimeの内部で、CoreML Execution Providerを使うように改造します。
手順は以下の記事を参考にしましたが、やねうら王のバージョンアップで若干違いが出ました。
まずはCPUのみの動作を試し、その後CoreMLを使うように改造しました。
環境について
- MacBook Air (Retina, 13-inch, 2019)
- macOS Monterey 12.3.1
Intel Macなので、Neural Engineはついていません。
onnxruntimeのインストール
注意:これで入るものはCoreMLに対応していないようなので、後で削除します。
brew install onnxruntime
バージョン1.11.1が入りました。
やねうら王のclone
やねうら王のmaster (23a7f2a88f98bcbfcaf8e625fe1448ea8d612e2b)をclone
ソースの修正
先述の記事に従って、AVX2を使うように修正しました。他の修正点は最新バージョンのやねうら王では不要でした。
注:これが正しいのかよく理解していません。また、CoreMLとは関係ないのでこの実験においては修正しなくても良いと思います。
diff --git a/source/Makefile b/source/Makefile index 94795789..734b218c 100644 --- a/source/Makefile +++ b/source/Makefile @@ -548,7 +548,7 @@ else ifeq ($(TARGET_CPU),AVX512) CPPFLAGS += -DUSE_AVX512 -DUSE_BMI2 -march=skylake-avx512 else ifeq ($(TARGET_CPU),AVX2) - CPPFLAGS += -DUSE_AVX2 -DUSE_BMI2 -mbmi -mbmi2 -mavx2 -march=corei7-avx + CPPFLAGS += -DUSE_AVX2 -DUSE_BMI2 -mbmi -mbmi2 -mavx2 -march=core-avx2 else ifeq ($(TARGET_CPU),SSE42) CPPFLAGS += -DUSE_SSE42 -msse4.2 -march=corei7
ビルド
make -C source COMPILER=g++ EXTRA_CPPFLAGS="-I/usr/local/include/onnxruntime/core/session -I/usr/local/include/onnxruntime/core/providers/cpu -fexceptions" EXTRA_LDFLAGS="-L/usr/local/lib -lonnxruntime" YANEURAOU_EDITION=YANEURAOU_ENGINE_DEEP_ORT_CPU
実行ファイルが source/YaneuraOu-by-gcc
にできたので、将棋所にエンジン登録します。
モデルの準備
https://tadaoyamaoka.hatenablog.com/entry/2021/08/17/000710
からダウンロードして解凍
source/model-dr2_exhi/model-dr2_exhi.onnx
にモデルファイルが存在するようにする。
将棋所での設定
- EvalDir:
model-dr2_exhi
- DNN_Model1:
model-dr2_exhi.onnx
実行
この状態で将棋所でLesserkaiと対局することができました。CPUのみの利用で、40NPS程度でした。
改造する
やねうら王のソースは以下のように修正しました。CPUプロバイダはコメントアウトして、必ずCoreMLが使われるようにしています。
diff --git a/source/eval/deep/nn_onnx_runtime.cpp b/source/eval/deep/nn_onnx_runtime.cpp index 3f11de70..e5e89178 100644 --- a/source/eval/deep/nn_onnx_runtime.cpp +++ b/source/eval/deep/nn_onnx_runtime.cpp @@ -14,6 +14,7 @@ #include <tensorrt_provider_factory.h> #else #include <cpu_provider_factory.h> +#include <coreml_provider_factory.h> #endif #include "../../usi.h" @@ -28,6 +29,9 @@ namespace Eval::dlshogi Ort::SessionOptions session_options; session_options.DisableMemPattern(); session_options.SetExecutionMode(ORT_SEQUENTIAL); + Ort::Env env = Ort::Env{ORT_LOGGING_LEVEL_ERROR, "Default"}; + uint32_t coreml_flags = 0; + Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CoreML(session_options, coreml_flags)); #if defined(ORT_DML) Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_DML(session_options, gpu_id)); #elif defined(ORT_TRT) @@ -64,7 +68,7 @@ namespace Eval::dlshogi // Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_Tensorrt(session_options, gpu_id)); Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, gpu_id)); #else - Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CPU(session_options, true)); + //Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CPU(session_options, true)); #endif #if defined(_WIN32) // Windows環境ではwstringでファイル名を渡す必要があるようだが?
CoreMLに対応したonnxruntimeの入手
brewで入るonnxruntimeは、ヘッダにcoreml関係が入っておらず非対応のようでした。dylibファイルのサイズも小さいです。
干渉しないように一旦アンインストールしました。
brew uninstall onnxruntime
GithubのReleasesから、ビルド済みバイナリ(onnxruntime-osx-x86_64-1.11.1.tgz
)とソース(Source code (tar.gz)
)両方を入手します。ビルド済みバイナリの方には、coreml関係のヘッダがなかったためです。
https://github.com/microsoft/onnxruntime/releases/tag/v1.11.1
ビルド
make -C source COMPILER=g++ EXTRA_CPPFLAGS="-I/path/to/onnxruntime-1.11.1/include/onnxruntime/core/session -I/path/to/onnxruntime-1.11.1/include/onnxruntime/core/providers/cpu -I/path/to/onnxruntime-1.11.1/include/onnxruntime/core/providers/coreml -fexceptions" EXTRA_LDFLAGS="-L/path/to/onnxruntime-osx-x86_64-1.11.1/lib -lonnxruntime" YANEURAOU_EDITION=YANEURAOU_ENGINE_DEEP_ORT_CPU
強引ですが、インクルードファイルはソースコードを解凍したもの、ライブラリファイルはビルド済みバイナリを解凍したものを指します。
ライブラリを検索パスにコピー
sudo cp -a /path/to/onnxruntime-osx-x86_64-1.11.1/lib/libonnxruntime.1.11.1.dylib /usr/local/lib
セキュリティ許可
ビルドしたバイナリを初回実行するとセキュリティエラーが出るので、「セキュリティとプライバシー」で許可します。
実行結果
エラーは出ずに実行できましたが、速度は40NPS程度のままでした。アクティビティモニタでGPU使用率を確認しましたが、対局中にGPU使用率が上がるとは言えませんでした。
onnxruntimeのソースを軽く読んだ感じだと、モデルをCoreMLに丸投げするのではなく、onnxruntime側で解釈した上でCoreMLに処理を移譲出来る部分を個別に移譲するような仕組みのようです。以下のパターンが考えられますが、検証方法がまだわかりません。
- dlshogiモデルに含まれるオペレータがCoreMLへ変換できない
- オペレータをCoreMLに移譲しているが、CoreML側の判断でCPU上で実行されており、速度面ではonnxruntimeのオペレータ実装と違いがない
今後の課題
原因を切り分ける必要があります。onnxruntimeを使わずにCoreMLを直接呼び出すプログラムであれば、dlshogiモデルがGPU上で動作するかを検証したいと思います。