NuGetで導入しているONNXRUNTIME-GPU(CUDA)を利用したいが、初期化でエラーが出てなんか遅い!
VisualStudio2022 C++でONNXRUNTIME(以下ORT)を利用したWindowsアプリ作ろうとしています。
VisualStudioでORTを利用するにはNuGetで導入すれば良さそうということはわかったのですが、CPUでの推論はともかく、CUDAがどうもちゃんと動いてくれません。
実行時、コンソールでは次のエラーが表示されていました。
Exception during initialization: D:\a\_work\1\s\onnxruntime\core\session\provider_bridge_ort.cc:1539 onnxruntime::ProviderLibrary::Get [ONNXRuntimeError] : 1 : FAIL : LoadLibrary failed with error 126 "" when trying to load "C:\Users\ユーザ名\source\repos\プロジェクト名\x64\Release\onnxruntime_providers_cuda.dll"
また、推論時間も合わせて評価していたのですが、1回目はともかく、2回目以降の推論もどうも早くなりませんでした。
session_run_time:1009
session_run_time:965
session_run_time:944
session_run_time:991
session_run_time:967
session_run_time:947
session_run_time:1032
session_run_time:952
session_run_time:960
session_run_time:964
session_run_time:952
create_session_time:341,load_image_preprocess_time:97,inference_time:1025,inference_times_10AVE:980
ONNXの初期化でエラーが出ているようですが、その後の推論は結果を見るとちゃんとできていそう。CPUでの推論よりは速いけど、CUDAの推論としては遅い、よね?せめて2回目以降はもっと早くなるんじゃないの・・・?
環境
Windows11 pro 23H2 64bit
Microsoft Visual Studio Community 2022 (64 ビット) - Current
Version 17.12.3
Microsoft.ML.OnnxRuntime 1.20.1(NuGet)
Microsoft.ML.OnnxRuntime.Gpu 1.20.1(NuGet)
CUDA 12.4 (参考:あまり関係ない)
cuDNN 9.1 (参考:あまり関係ない)
C++
結論:Build毎に、Microsoft.ML.OnnxRuntime.Gpuのdll、libをエラーのフォルダに上書きコピーする
VisualStudioはORT-CPUのお世話はせっせとしてくれますが、同じNuGetで取得しているORT-GPUのお世話は一切してくれないのです。
そのため、Buildが走る度にORT-CPUのdllはせっせとコピーしてくれますが、ORT-GPUは一切コピーしてくれません。
ORT-GPUで動作させたい時は、ORT-GPUのdllが必要ですが、buildすればORT-CPU版だけ上書きしてくれます。
そのため、onnxruntime.dll
、onnxruntime_providers_shared.dll
はビルドが走る度にCPU版に上書きされています。
ORT-GPUで動作させるのに必要なDLLは、例えば以下にあります。
C:\Users\ユーザ名\source\repos\プロジェクト名\packages\Microsoft.ML.OnnxRuntime.Gpu.1.20.1\runtimes\win-x64\native
ここにあるonnxruntime.dll
、onnxruntime_providers_shared.dll
、onnxruntime_providers_cuda.dll
、onnxruntime.lib
、onnxruntime_providers_shared.lib
、onnxruntime_providers_shared.lib
を、
C:\Users\ユーザ名\source\repos\プロジェクト名\x64\Release
にビルドの度に上書きコピーしましょう。
onnxruntime.dll
、onnxruntime_providers_shared.dll
は、同名ですが、CPUとGPUではファイルは別物です。
tensorrtはたぶんなくても大丈夫です。
GPUフォルダのdll、libで上書きコピーして実行した時の実行結果
エラーはなくなり、2回目以降の処理速度も速くなっています。(入力テンソルが1280,1280なのでやや遅い)
session_run_time:403
session_run_time:42
session_run_time:31
session_run_time:32
session_run_time:32
session_run_time:32
session_run_time:32
session_run_time:30
session_run_time:30
session_run_time:30
session_run_time:31
create_session_time:397,load_image_preprocess_time:78,inference_time:415,inference_times_10AVE:45
その他
- プロジェクトのプロパティから、ビルド後のイベントでファイルのコピーをすると手間が減るかもしれません
- またlibについては追加する依存ファイルに記載しておくと、コピーは不要かもしれません
- じゃあNuGetでORT-GPUだけ導入したらいいんじゃない?とも思いましたが、ORT-GPUには
onnxruntime_cxx_api.h
がないため、ORT-CPUもNuGetで導入が必要です - ORT-GPUでの事例ですが、ORT-DML(DirectML)などでも同じだと思います
まとめ
- VisualStudioにはNuGetでORT-CPU、ORT-GPUを導入できて便利ですが、肝心のDLLは手動での上書きコピーになります
- っていうかNugetでORT-GPUを導入しているなら、気を利かせてORT-GPUのフォルダからもコピーして欲しいところ(なぜここは手動なのか?
- もしかするとVisualStudioのユーザーでは当たり前のことかもしれませんが、検索してもあまりドンピシャな記事が見つからなかったので、書き残していくものです
後日談(2025/01/07 追記)
ONNX-CUDAが遅かったのでTensorRTにしようと考えているのですが、他のGPU対応も兼ねてONNXRUNTIME-DirectMLをNuGetで入れて動かしてみたところ、私の環境ではDirectMLの方がCUDAより若干速かったです。
1280*1280にリサイズした動画から物体検出をしていますが、DirectMLが17FPSで、CUDAが15FPSでした。