99// granted to it by virtue of its status as an Intergovernmental Organization
1010// or submit itself to any jurisdiction.
1111
12- // / Benchmark one dense ONNX input [batch, features] using CPU or AMD MIGraphX.
12+ // / Benchmark one dense ONNX input [batch, features] using CPU, AMD MIGraphX or NVIDIA CUDA .
1313#include < Framework/AnalysisTask.h>
1414#include < Framework/Configurable.h>
1515#include < Framework/ControlService.h>
3434
3535namespace
3636{
37- // Optional HIP telemetry without introducing a ROCm build dependency for CPU users .
37+ // Load GPU telemetry at runtime so CPU builds need no HIP or CUDA headers .
3838// Memory is device-wide, not process-local; samples are not a transient peak.
39- class HipMemory
39+ class GpuMemory
4040{
4141 public:
42- explicit HipMemory (int device)
42+ explicit GpuMemory (int device, bool cuda )
4343 {
44- library = dlopen (" libamdhip64.so" , RTLD_LAZY | RTLD_LOCAL );
44+ const std::vector<const char *> libraries = cuda
45+ ? std::vector<const char *>{" libcudart.so" , " libcudart.so.13" , " libcudart.so.12" , " libcudart.so.11.0" }
46+ : std::vector<const char *>{" libamdhip64.so" };
47+ for (auto name : libraries) {
48+ library = dlopen (name, RTLD_LAZY | RTLD_LOCAL );
49+ if (library) {
50+ break ;
51+ }
52+ }
4553 if (!library) {
46- throw std::runtime_error (" GPU telemetry requires libamdhip64.so on LD_LIBRARY_PATH" );
54+ throw std::runtime_error (cuda ? " GPU telemetry requires libcudart.so (CUDA 11/12/13) on LD_LIBRARY_PATH"
55+ : " GPU telemetry requires libamdhip64.so on LD_LIBRARY_PATH" );
4756 }
48- auto setDevice = reinterpret_cast <int (*)(int )>(dlsym (library, " hipSetDevice" ));
49- getMemory = reinterpret_cast <int (*)(size_t *, size_t *)>(dlsym (library, " hipMemGetInfo" ));
57+ auto setDevice = reinterpret_cast <int (*)(int )>(dlsym (library, cuda ? " cudaSetDevice" : " hipSetDevice" ));
58+ memoryFunction = cuda ? " cudaMemGetInfo" : " hipMemGetInfo" ;
59+ getMemory = reinterpret_cast <int (*)(size_t *, size_t *)>(dlsym (library, memoryFunction));
5060 if (!setDevice || !getMemory || setDevice (device) != 0 ) {
5161 dlclose (library);
5262 library = nullptr ;
53- throw std::runtime_error (" Cannot select HIP device or resolve HIP memory API" );
63+ throw std::runtime_error (cuda ? " Cannot select CUDA device or resolve CUDA memory API"
64+ : " Cannot select HIP device or resolve HIP memory API" );
5465 }
5566 }
56- ~HipMemory ()
67+ ~GpuMemory ()
5768 {
5869 if (library) {
5970 dlclose (library);
6071 }
6172 }
62- HipMemory (HipMemory const &) = delete ;
63- HipMemory & operator =(HipMemory const &) = delete ;
73+ GpuMemory (GpuMemory const &) = delete ;
74+ GpuMemory & operator =(GpuMemory const &) = delete ;
6475 uint64_t used () const
6576 {
6677 size_t free = 0 , total = 0 ;
6778 if (getMemory (&free, &total) != 0 || free > total) {
68- throw std::runtime_error (" hipMemGetInfo failed" );
79+ throw std::runtime_error (std::string (memoryFunction) + " failed" );
6980 }
7081 return total - free;
7182 }
7283
7384 private:
85+ const char * memoryFunction = nullptr ;
7486 void * library = nullptr ;
7587 int (*getMemory)(size_t *, size_t *) = nullptr ;
7688};
@@ -92,8 +104,8 @@ using namespace o2::framework;
92104
93105struct BenchmarkOnnxModel {
94106 Configurable<std::string> modelPath{" model-path" , " model.onnx" , " Local ONNX model" };
95- Configurable<std::string> provider{" provider" , " cpu" , " cpu or migraphx " };
96- Configurable<int > gpuId{" gpu-id" , 0 , " HIP logical device ID (after visibility masking)" };
107+ Configurable<std::string> provider{" provider" , " cpu" , " cpu, migraphx or cuda " };
108+ Configurable<int > gpuId{" gpu-id" , 0 , " HIP/CUDA logical device ID (after visibility masking)" };
97109 Configurable<bool > fp16{" fp16" , false , " Require FP16 input; does not convert model weights" };
98110 Configurable<int > batchSize{" batch-size" , 1024 , " Samples per inference" };
99111 Configurable<int > features{" features" , 5 , " Features per sample, in model order" };
@@ -114,33 +126,47 @@ struct BenchmarkOnnxModel {
114126 if (batchSize.value <= 0 || features.value <= 0 || iterations.value <= 0 || warmup.value < 1 || cpuThreads.value <= 0 || gpuId.value < 0 ) {
115127 throw std::runtime_error (" Require positive batch, features, iterations, warmup, threads and nonnegative GPU ID" );
116128 }
117- bool gpu = provider.value == " migraphx" ;
129+ bool cuda = provider.value == " cuda" ;
130+ bool gpu = cuda || provider.value == " migraphx" ;
118131 if (!gpu && provider.value != " cpu" ) {
119- throw std::runtime_error (" provider must be cpu or migraphx " );
132+ throw std::runtime_error (" provider must be cpu, migraphx or cuda " );
120133 }
121134 if (!gpu && fp16.value ) {
122135 throw std::runtime_error (" Use FP32 for the CPU baseline" );
123136 }
124- std::unique_ptr<HipMemory > memory;
137+ std::unique_ptr<GpuMemory > memory;
125138 uint64_t before = 0 , afterWarmup = 0 , afterRun = 0 ;
126139 Ort::Env env (ORT_LOGGING_LEVEL_WARNING , " tutorial-benchmark" );
127140 Ort::SessionOptions options;
128141 options.SetIntraOpNumThreads (cpuThreads.value );
129142 options.SetInterOpNumThreads (1 );
130143 options.SetGraphOptimizationLevel (GraphOptimizationLevel::ORT_ENABLE_ALL );
131144 if (gpu) {
145+ const std::string providerName = cuda ? " CUDAExecutionProvider" : " MIGraphXExecutionProvider" ;
132146 auto providers = Ort::GetAvailableProviders ();
133- if (std::find (providers.begin (), providers.end (), " MIGraphXExecutionProvider " ) == providers.end ()) {
134- throw std::runtime_error (" This ONNX Runtime has no MIGraphXExecutionProvider " );
147+ if (std::find (providers.begin (), providers.end (), providerName ) == providers.end ()) {
148+ throw std::runtime_error (" This ONNX Runtime has no " + providerName );
135149 }
136- memory = std::make_unique<HipMemory >(gpuId.value );
150+ memory = std::make_unique<GpuMemory >(gpuId.value , cuda );
137151 before = memory->used ();
138- OrtMIGraphXProviderOptions amd{};
139- amd.device_id = gpuId.value ;
140- amd.migraphx_mem_limit = std::numeric_limits<size_t >::max ();
141- // Keep the supplied model precision; do not automatically quantize FP32.
142- amd.migraphx_fp16_enable = 0 ;
143- options.AppendExecutionProvider_MIGraphX (amd);
152+ if (cuda) {
153+ // V2 options allow disabling TF32 for the FP32 comparison.
154+ OrtCUDAProviderOptionsV2* raw = nullptr ;
155+ Ort::ThrowOnError (Ort::GetApi ().CreateCUDAProviderOptions (&raw));
156+ std::unique_ptr<OrtCUDAProviderOptionsV2, decltype (Ort::GetApi ().ReleaseCUDAProviderOptions )> nvidia (raw, Ort::GetApi ().ReleaseCUDAProviderOptions );
157+ const std::string device = std::to_string (gpuId.value );
158+ const char * keys[] = {" device_id" , " use_tf32" };
159+ const char * values[] = {device.c_str (), " 0" };
160+ Ort::ThrowOnError (Ort::GetApi ().UpdateCUDAProviderOptions (nvidia.get (), keys, values, 2 ));
161+ options.AppendExecutionProvider_CUDA_V2 (*nvidia);
162+ } else {
163+ OrtMIGraphXProviderOptions amd{};
164+ amd.device_id = gpuId.value ;
165+ amd.migraphx_mem_limit = std::numeric_limits<size_t >::max ();
166+ // Keep the supplied model precision; do not automatically quantize FP32.
167+ amd.migraphx_fp16_enable = 0 ;
168+ options.AppendExecutionProvider_MIGraphX (amd);
169+ }
144170 options.AddConfigEntry (" session.disable_cpu_ep_fallback" , " 1" );
145171 }
146172 Ort::Session session (env, modelPath.value .c_str (), options);
0 commit comments