VisualStudio C++ ONNXRUNTIME GPU（CUDA）でONNX初期化エラーが出る時

## 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でした。