CUDA 执行提供程序
CUDA 执行提供程序可在 Nvidia CUDA-enabled GPU 上启用硬件加速计算。
目录
安装
大多数语言绑定都发布了包含 CUDA EP 的 ONNX Runtime 预构建二进制文件。请参考 安装 ORT。
从源码构建
请参阅 构建说明。
要求
请参考下表了解 ONNX Runtime 推理包的官方 GPU 包依赖项。请注意,ONNX Runtime 训练版本与 PyTorch CUDA 版本保持一致;有关支持的版本,请参考 onnxruntime.ai 上的优化训练选项卡。
由于 Nvidia CUDA 小版本兼容性,使用 CUDA 11.8 构建的 ONNX Runtime 与任何 CUDA 11.x 版本兼容;使用 CUDA 12.x 构建的 ONNX Runtime 与任何 CUDA 12.x 版本兼容。
使用 cuDNN 8.x 构建的 ONNX Runtime 与 cuDNN 9.x 不兼容,反之亦然。您可以根据与您的运行时环境(例如,PyTorch 2.3 使用 cuDNN 8.x,而 PyTorch 2.4 或更高版本使用 cuDNN 9.x)匹配的 CUDA 和 cuDNN 主要版本选择包。
注意:从 1.19 版本开始,在 PyPI 中分发 ONNX Runtime GPU 包时,CUDA 12.x 成为默认版本。
为了减少手动安装 CUDA 和 cuDNN 的需求,并确保 ONNX Runtime 和 PyTorch 之间的无缝集成,onnxruntime-gpu
Python 包提供了适当加载 CUDA 和 cuDNN 动态链接库 (DLL) 的 API。更多详情,请参考与 PyTorch 的兼容性和预加载 DLL部分。
CUDA 12.x
ONNX Runtime | CUDA | cuDNN | 备注 |
---|---|---|---|
1.20.x | 12.x | 9.x | PyPI 中可用。与 CUDA 12.x 的 PyTorch >= 2.4.0 兼容。 |
1.19.x | 12.x | 9.x | PyPI 中可用。与 CUDA 12.x 的 PyTorch >= 2.4.0 兼容。 |
1.18.1 | 12.x | 9.x | 需要 cuDNN 9。无 Java 包。 |
1.18.0 | 12.x | 8.x | 已添加 Java 包。 |
1.17.x | 12.x | 8.x | 仅发布了 C++/C# Nuget 和 Python 包。无 Java 包。 |
CUDA 11.x
ONNX Runtime | CUDA | cuDNN | 备注 |
---|---|---|---|
1.20.x | 11.8 | 8.x | PyPI 中不可用。详情请参阅 安装 ORT。与 CUDA 11.8 的 PyTorch <= 2.3.1 兼容。 |
1.19.x | 11.8 | 8.x | PyPI 中不可用。详情请参阅 安装 ORT。与 CUDA 11.8 的 PyTorch <= 2.3.1 兼容。 |
1.18.x | 11.8 | 8.x | PyPI 中可用。 |
1.17 1.16 1.15 | 11.8 | 8.2.4 (Linux) 8.5.0.96 (Windows) | 已使用 CUDA 11.6 至 11.8 版本以及 cuDNN 8.2 至 8.9 版本进行测试 |
1.14 1.13 | 11.6 | 8.2.4 (Linux) 8.5.0.96 (Windows) | libcudart 11.4.43 libcufft 10.5.2.100 libcurand 10.2.5.120 libcublasLt 11.6.5.2 libcublas 11.6.5.2 libcudnn 8.2.4 |
1.12 1.11 | 11.4 | 8.2.4 (Linux) 8.2.2.26 (Windows) | libcudart 11.4.43 libcufft 10.5.2.100 libcurand 10.2.5.120 libcublasLt 11.6.5.2 libcublas 11.6.5.2 libcudnn 8.2.4 |
1.10 | 11.4 | 8.2.4 (Linux) 8.2.2.26 (Windows) | libcudart 11.4.43 libcufft 10.5.2.100 libcurand 10.2.5.120 libcublasLt 11.6.1.51 libcublas 11.6.1.51 libcudnn 8.2.4 |
1.9 | 11.4 | 8.2.4 (Linux) 8.2.2.26 (Windows) | libcudart 11.4.43 libcufft 10.5.2.100 libcurand 10.2.5.120 libcublasLt 11.6.1.51 libcublas 11.6.1.51 libcudnn 8.2.4 |
1.8 | 11.0.3 | 8.0.4 (Linux) 8.0.2.39 (Windows) | libcudart 11.0.221 libcufft 10.2.1.245 libcurand 10.2.1.245 libcublasLt 11.2.0.252 libcublas 11.2.0.252 libcudnn 8.0.4 |
1.7 | 11.0.3 | 8.0.4 (Linux) 8.0.2.39 (Windows) | libcudart 11.0.221 libcufft 10.2.1.245 libcurand 10.2.1.245 libcublasLt 11.2.0.252 libcublas 11.2.0.252 libcudnn 8.0.4 |
CUDA 10.x
ONNX Runtime | CUDA | cuDNN | 备注 |
---|---|---|---|
1.5-1.6 | 10.2 | 8.0.3 | CUDA 11 可以从源码构建 |
1.2-1.4 | 10.1 | 7.6.5 | 需要 cublas10-10.2.1.243;cublas 10.1.x 版本将无法工作 |
1.0-1.1 | 10.0 | 7.6.4 | CUDA 9.1 至 10.1 版本以及 cuDNN 7.1 至 7.4 版本也应与 Visual Studio 2017 兼容 |
对于旧版本,请参考发布分支上的 readme 和构建页面。
构建
有关构建说明,请参阅 BUILD 页面。
与 PyTorch 的兼容性
onnxruntime-gpu
包旨在与 PyTorch 无缝协作,前提是两者都针对相同的主要版本 CUDA 和 cuDNN 构建。安装支持 CUDA 的 PyTorch 时,会包含必要的 CUDA 和 cuDNN DLL,无需单独安装 CUDA 工具包或 cuDNN。
为了确保 ONNX Runtime 利用 PyTorch 安装的 DLL,您可以在创建推理会话之前预加载这些库。这可以通过导入 PyTorch 或使用 onnxruntime.preload_dlls()
函数来实现。
示例 1:导入 PyTorch
# Import torch will preload necessary DLLs. It need to be done before creating session.
import torch
import onnxruntime
# Create an inference session with CUDA execution provider
session = onnxruntime.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])
示例 2:使用 preload_dlls
函数
import onnxruntime
# Preload necessary DLLs
onnxruntime.preload_dlls()
# Create an inference session with CUDA execution provider
session = onnxruntime.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])
预加载 DLL
从 1.21.0 版本开始,onnxruntime-gpu
包提供了 preload_dlls
函数,用于预加载 CUDA、cuDNN 和 Microsoft Visual C++ (MSVC) 运行时 DLL。此函数在指定要加载的库以及从哪个目录加载方面提供了灵活性。
函数签名
onnxruntime.preload_dlls(cuda=True, cudnn=True, msvc=True, directory=None)
参数
cuda
(bool):如果设置为True
,则预加载 CUDA DLL。cudnn
(bool):如果设置为True
,则预加载 cuDNN DLL。msvc
(bool):如果设置为True
,则预加载 MSVC 运行时 DLL。directory
(str 或 None):加载 DLL 的目录。None
:在默认目录中搜索。""
(空字符串):在 NVIDIA site packages 中搜索。- 指定路径:从指定目录加载 DLL。
默认搜索顺序
当 directory=None
时,函数按以下顺序搜索 CUDA 和 cuDNN DLL
- 在 Windows 上,PyTorch 安装目录下的
lib
目录。 - NVIDIA CUDA 或 cuDNN 库的 Python site-packages 目录(例如,
nvidia_cuda_runtime_cu12
,nvidia_cudnn_cu12
)。 - 回退到默认的 DLL 加载行为。
通过使用默认搜索顺序预加载必要的 DLL,您可以确保 ONNX Runtime 与 PyTorch 无缝运行。
通过 onnxruntime-gpu
安装 CUDA 和 cuDNN
您可以使用 pip 与 onnxruntime-gpu
包一起安装必要的 CUDA 和 cuDNN 运行时 DLL
pip install onnxruntime-gpu[cuda,cudnn]
从 NVIDIA Site Packages 预加载 DLL
要从 NVIDIA site packages 预加载 CUDA 和 cuDNN DLL 并显示调试信息
import onnxruntime
# Preload DLLs from NVIDIA site packages
onnxruntime.preload_dlls(directory="")
# Print debug information
onnxruntime.print_debug_info()
从指定目录加载 DLL
要从指定位置加载 DLL,请将 directory
参数设置为绝对路径或相对于 ONNX Runtime 包根目录的路径。
示例:从系统安装加载 CUDA 并从 NVIDIA Site Package 加载 cuDNN
import os
import onnxruntime
# Load CUDA DLLs from system installation
cuda_path = os.path.join(os.environ["CUDA_PATH"], "bin")
onnxruntime.preload_dlls(cuda=True, cudnn=False, directory=cuda_path)
# Load cuDNN DLLs from NVIDIA site package
onnxruntime.preload_dlls(cuda=False, cudnn=True, directory="..\\nvidia\\cudnn\\bin")
# Print debug information
onnxruntime.print_debug_info()
配置选项
CUDA 执行提供程序支持以下配置选项。
device_id
设备 ID。
默认值:0
user_compute_stream
定义推理运行的计算流。它隐式设置了 has_user_compute_stream
选项。不能通过 UpdateCUDAProviderOptions
设置,而应通过 UpdateCUDAProviderOptionsWithValue
设置。不能与外部分配器组合使用。
Python 使用示例
providers = [("CUDAExecutionProvider", {"device_id": torch.cuda.current_device(),
"user_compute_stream": str(torch.cuda.current_stream().cuda_stream)})]
sess_options = ort.SessionOptions()
sess = ort.InferenceSession("my_model.onnx", sess_options=sess_options, providers=providers)
为了利用用户计算流,建议使用I/O 绑定将输入和输出绑定到设备上的张量。
do_copy_in_default_stream
是否在默认流中进行复制或使用单独的流。推荐设置为 true。如果为 false,可能存在竞态条件,性能可能更好。
默认值:true
use_ep_level_unified_stream
为 CUDA EP 的所有线程使用相同的 CUDA 流。此选项由 has_user_compute_stream
、enable_cuda_graph
或使用外部分配器时隐式启用。
默认值:false
gpu_mem_limit
设备内存竞技场(arena)的字节大小限制。此大小限制仅适用于执行提供程序的竞技场。总设备内存使用量可能更高。s: C++ size_t 类型的最大值(实际上无限制)
注意: 如果指定了 default_memory_arena_cfg
,则其内容将覆盖此设置
arena_extend_strategy
扩展设备内存竞技场(arena)的策略。
值 | 描述 |
---|---|
kNextPowerOfTwo (0) | 后续扩展以更大的量(乘以二的幂)进行 |
kSameAsRequested (1) | 按请求的量扩展 |
默认值:kNextPowerOfTwo
注意: 如果指定了 default_memory_arena_cfg
,则其内容将覆盖此设置
cudnn_conv_algo_search
为 cuDNN 卷积算法执行的搜索类型。
值 | 描述 |
---|---|
EXHAUSTIVE (0) | 使用 cudnnFindConvolutionForwardAlgorithmEx 进行耗时的穷尽基准测试 |
HEURISTIC (1) | 使用 cudnnGetConvolutionForwardAlgorithm_v7 进行基于轻量级启发式的搜索 |
DEFAULT (2) | 使用 CUDNN_CONVOLUTION_FWD_ALGO_IMPLICIT_PRECOMP_GEMM 的默认算法 |
默认值:EXHAUSTIVE
cudnn_conv_use_max_workspace
查看 用于计算密集型模型的性能调优 以了解此标志的作用。此标志仅在使用 C API 的提供程序选项结构 V2 版本时受支持(示例如下)。
默认值:1 (1.14 及更高版本),0 (之前版本)
cudnn_conv1d_pad_to_nc1d
查看 CUDA EP 中的卷积输入填充 以了解此标志的作用。此标志仅在使用 C API 的提供程序选项结构 V2 版本时受支持。(示例如下)
默认值:0
enable_cuda_graph
查看 在 CUDA EP 中使用 CUDA 图(预览) 以了解此标志的作用。此标志仅在使用 C API 的提供程序选项结构 V2 版本时受支持。(示例如下)
默认值:0
enable_skip_layer_norm_strict_mode
在 SkipLayerNormalization cuda 实现中是否使用严格模式。默认和推荐设置为 false。如果启用,预期会提高精度并降低性能。此标志仅在使用 C API 的提供程序选项结构 V2 版本时受支持。(示例如下)
默认值:0
use_tf32
TF32 是自 Ampere 以来在 NVIDIA GPU 上可用的一种数学模式。它允许某些 float32 矩阵乘法和卷积在张量核上以 TensorFloat-32 降低精度运行得更快:float32 输入使用 10 位尾数进行舍入,结果使用 float32 精度累积。
默认值:1
TensorFloat-32 默认启用。从 ONNX Runtime 1.18 开始,您可以使用此标志在推理会话中禁用它。
Python 使用示例
providers = [("CUDAExecutionProvider", {"use_tf32": 0})]
sess_options = ort.SessionOptions()
sess = ort.InferenceSession("my_model.onnx", sess_options=sess_options, providers=providers)
此标志仅在使用 C API 的提供程序选项结构 V2 版本时受支持。(示例如下)
gpu_external_[alloc|free|empty_cache]
gpu_external_* 用于传递外部分配器。Python 使用示例
from onnxruntime.training.ortmodule.torch_cpp_extensions import torch_gpu_allocator
provider_option_map["gpu_external_alloc"] = str(torch_gpu_allocator.gpu_caching_allocator_raw_alloc_address())
provider_option_map["gpu_external_free"] = str(torch_gpu_allocator.gpu_caching_allocator_raw_delete_address())
provider_option_map["gpu_external_empty_cache"] = str(torch_gpu_allocator.gpu_caching_allocator_empty_cache_address())
默认值:0
prefer_nhwc
此选项自 ONNX Runtime 1.20 版本开始可用,其中构建默认带有 onnxruntime_USE_CUDA_NHWC_OPS=ON
。
如果启用此选项,执行提供程序优先选择 NHWC 算子而非 NCHW 算子。必要的布局转换将自动应用于模型。由于 NVIDIA 张量核在 NHWC 布局下运行更高效,当模型包含许多受支持的算子且不需要过多的额外转置操作时,启用此选项可以提高性能。未来版本计划支持更广泛的 NHWC 算子。
此标志仅在使用 C API 时,在提供程序选项结构 V2 版本中受支持。可以使用 CreateCUDAProviderOptions 创建 V2 提供程序选项结构,并使用 UpdateCUDAProviderOptions 更新。
默认值:0
性能调优
应利用 I/O 绑定 功能来避免输入和输出复制产生的开销。理想情况下,输入的上传和下载可以隐藏在推理背后。这可以通过在运行推理时进行异步复制来实现。此 PR 中对此进行了演示
Ort::RunOptions run_options;
run_options.AddConfigEntry("disable_synchronize_execution_providers", "1");
session->Run(run_options, io_binding);
通过禁用推理同步,用户必须在执行后负责同步计算流。此功能只能与设备本地内存或分配在 pinned memory 中的 ORT 值一起使用,否则发出的下载将是阻塞的,并且不会按预期运行。
计算密集型模型
ORT 利用 CuDNN 进行卷积操作,此过程的第一步是确定在每个 Conv
节点中针对给定输入配置(输入形状、滤波器形状等)执行卷积操作时要使用的“最优”卷积算法。此子步骤涉及向 CuDNN 查询“工作空间”内存大小并进行分配,以便 CuDNN 在确定要使用的“最优”卷积算法时可以使用此辅助内存。
cudnn_conv_use_max_workspace
的默认值对于 1.14 及更高版本为 1,对于以前版本为 0。当其值为 0 时,ORT 会将工作空间大小限制为 32 MB,这可能导致 CuDNN 选择次优的卷积算法。为了让 ORT 分配 CuDNN 确定的最大可能工作空间,需要设置一个名为 cudnn_conv_use_max_workspace
的提供程序选项(如下所示)。
请记住,使用此标志可能会按一个因子(有时是几个 GB)增加峰值内存使用量,但这确实有助于 CuDNN 为给定输入选择最佳卷积算法。我们发现,在使用 fp16 模型时使用此标志非常重要,因为它允许 CuDNN 为卷积操作选择张量核算法(如果硬件支持张量核操作)。对于其他数据类型(float
和 double
),此标志可能会或不会带来性能提升。
- Python
providers = [("CUDAExecutionProvider", {"cudnn_conv_use_max_workspace": '1'})] sess_options = ort.SessionOptions() sess = ort.InferenceSession("my_conv_heavy_fp16_model.onnx", sess_options=sess_options, providers=providers)
- C/C++
OrtCUDAProviderOptionsV2* cuda_options = nullptr; CreateCUDAProviderOptions(&cuda_options); std::vector<const char*> keys{"cudnn_conv_use_max_workspace"}; std::vector<const char*> values{"1"}; UpdateCUDAProviderOptions(cuda_options, keys.data(), values.data(), 1); OrtSessionOptions* session_options = /* ... */; SessionOptionsAppendExecutionProvider_CUDA_V2(session_options, cuda_options); // Finally, don't forget to release the provider options ReleaseCUDAProviderOptions(cuda_options);
- C#
var cudaProviderOptions = new OrtCUDAProviderOptions(); // Dispose this finally var providerOptionsDict = new Dictionary<string, string>(); providerOptionsDict["cudnn_conv_use_max_workspace"] = "1"; cudaProviderOptions.UpdateOptions(providerOptionsDict); SessionOptions options = SessionOptions.MakeSessionOptionWithCudaProvider(cudaProviderOptions); // Dispose this finally
卷积输入填充
ORT 利用 CuDNN 进行卷积操作。虽然 CuDNN 仅接受 4-D 或 5-D 张量作为卷积操作的输入,但如果输入是 3-D 张量,则需要进行维度填充。给定形状为 [N, C, D] 的输入张量,可以将其填充到 [N, C, D, 1] 或 [N, C, 1, D]。虽然这两种填充方式产生相同的输出,但性能可能大不相同,因为会选择不同的卷积算法,尤其是在某些设备(如 A100)上。默认情况下,输入被填充到 [N, C, D, 1]。如果首选 [N, C, 1, D],则需要设置一个名为 cudnn_conv1d_pad_to_nc1d
的提供程序选项(如下所示)。
- Python
providers = [("CUDAExecutionProvider", {"cudnn_conv1d_pad_to_nc1d": '1'})] sess_options = ort.SessionOptions() sess = ort.InferenceSession("my_conv_model.onnx", sess_options=sess_options, providers=providers)
- C/C++
OrtCUDAProviderOptionsV2* cuda_options = nullptr; CreateCUDAProviderOptions(&cuda_options); std::vector<const char*> keys{"cudnn_conv1d_pad_to_nc1d"}; std::vector<const char*> values{"1"}; UpdateCUDAProviderOptions(cuda_options, keys.data(), values.data(), 1); OrtSessionOptions* session_options = /* ... */; SessionOptionsAppendExecutionProvider_CUDA_V2(session_options, cuda_options); // Finally, don't forget to release the provider options ReleaseCUDAProviderOptions(cuda_options);
- C#
var cudaProviderOptions = new OrtCUDAProviderOptions(); // Dispose this finally var providerOptionsDict = new Dictionary<string, string>(); providerOptionsDict["cudnn_conv1d_pad_to_nc1d"] = "1"; cudaProviderOptions.UpdateOptions(providerOptionsDict); SessionOptions options = SessionOptions.MakeSessionOptionWithCudaProvider(cudaProviderOptions); // Dispose this finally
使用 CUDA 图(预览)
在使用 CUDA EP 时,ORT 支持使用 CUDA 图 来消除与顺序启动 CUDA 内核相关的 CPU 开销。要启用 CUDA 图 的使用,请使用如下所示的提供程序选项。ORT 通过将用户指定的 gpu_graph_id 传递给运行选项来支持多图捕获功能。gpu_graph_id
是会话使用一个 CUDA 图时的可选参数。如果未设置,默认值为 0。如果 gpu_graph_id
设置为 -1,则在该运行中禁用 CUDA 图捕获/回放。
当前,使用 CUDA 图 功能有一些限制
-
不支持带有控制流算子(即
If
、Loop
和Scan
算子)的模型。 -
CUDA 图 的使用仅限于模型中的所有模型算子(图节点)都可以分区到 CUDA EP 的情况。
-
模型的输入/输出类型需要是张量。
-
对于相同的图注解 ID,输入/输出的形状和地址在推理调用之间不能改变。用于回放的输入张量应复制到图捕获中使用的输入张量地址。
-
在多图捕获模式下,捕获的图将保留在会话的生命周期内,目前不支持捕获图删除功能。
-
根据设计,CUDA 图 旨在在图回放步骤中读写与图捕获步骤中相同的 CUDA 虚拟内存地址。由于此要求,使用此功能需要使用 IOBinding 来绑定将用作 CUDA 图 机制读写输入/输出的内存(请参阅下面的示例,了解如何实现)。
-
在更新后续推理调用的输入时,新的输入需要复制到绑定的
OrtValue
输入对应的 CUDA 内存位置(请参阅下面的示例,了解如何实现)。这是因为“图回放”将需要从相同的 CUDA 虚拟内存地址读取输入。 -
目前不支持多线程使用,即在使用 CUDA 图 时,不能从多个线程在同一个
InferenceSession
对象上调用Run()
。
注意:第一次 Run()
会在幕后执行各种任务,例如分配 CUDA 内存、为模型捕获 CUDA 图,然后执行图回放以确保图运行。因此,第一次 Run()
的延迟必然较高。后续的 Run()
仅对第一次 Run()
中捕获和缓存的图执行图回放。
-
Python
providers = [("CUDAExecutionProvider", {"enable_cuda_graph": '1'})] sess_options = ort.SessionOptions() sess = ort.InferenceSession("my_model.onnx", sess_options=sess_options, providers=providers) providers = [("CUDAExecutionProvider", {'enable_cuda_graph': True})] x = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], dtype=np.float32) y = np.array([[0.0], [0.0], [0.0]], dtype=np.float32) x_ortvalue = onnxrt.OrtValue.ortvalue_from_numpy(x, 'cuda', 0) y_ortvalue = onnxrt.OrtValue.ortvalue_from_numpy(y, 'cuda', 0) session = onnxrt.InferenceSession("matmul_2.onnx", providers=providers) io_binding = session.io_binding() # Pass gpu_graph_id to RunOptions through RunConfigs ro = onnxrt.RunOptions() # gpu_graph_id is optional if the session uses only one cuda graph ro.add_run_config_entry("gpu_graph_id", "1") # Bind the input and output io_binding.bind_ortvalue_input('X', x_ortvalue) io_binding.bind_ortvalue_output('Y', y_ortvalue) # One regular run for the necessary memory allocation and cuda graph capturing session.run_with_iobinding(io_binding, ro) expected_y = np.array([[5.0], [11.0], [17.0]], dtype=np.float32) np.testing.assert_allclose(expected_y, y_ortvalue.numpy(), rtol=1e-05, atol=1e-05) # After capturing, CUDA graph replay happens from this Run onwards session.run_with_iobinding(io_binding, ro) np.testing.assert_allclose(expected_y, y_ortvalue.numpy(), rtol=1e-05, atol=1e-05) # Update input and then replay CUDA graph with the updated input x_ortvalue.update_inplace(np.array([[10.0, 20.0], [30.0, 40.0], [50.0, 60.0]], dtype=np.float32)) session.run_with_iobinding(io_binding, ro)
- C/C++
const auto& api = Ort::GetApi(); struct CudaMemoryDeleter { explicit CudaMemoryDeleter(const Ort::Allocator* alloc) { alloc_ = alloc; } void operator()(void* ptr) const { alloc_->Free(ptr); } const Ort::Allocator* alloc_; }; // Enable cuda graph in cuda provider option. OrtCUDAProviderOptionsV2* cuda_options = nullptr; api.CreateCUDAProviderOptions(&cuda_options); std::unique_ptr<OrtCUDAProviderOptionsV2, decltype(api.ReleaseCUDAProviderOptions)> rel_cuda_options(cuda_options, api.ReleaseCUDAProviderOptions); std::vector<const char*> keys{"enable_cuda_graph"}; std::vector<const char*> values{"1"}; api.UpdateCUDAProviderOptions(rel_cuda_options.get(), keys.data(), values.data(), 1); Ort::SessionOptions session_options; api.SessionOptionsAppendExecutionProvider_CUDA_V2(static_cast<OrtSessionOptions*>(session_options), rel_cuda_options.get(); // Pass gpu_graph_id to RunOptions through RunConfigs Ort::RunOptions run_option; // gpu_graph_id is optional if the session uses only one cuda graph run_option.AddConfigEntry("gpu_graph_id", "1"); // Create IO bound inputs and outputs. Ort::Session session(*ort_env, ORT_TSTR("matmul_2.onnx"), session_options); Ort::MemoryInfo info_cuda("Cuda", OrtAllocatorType::OrtArenaAllocator, 0, OrtMemTypeDefault); Ort::Allocator cuda_allocator(session, info_cuda); const std::array<int64_t, 2> x_shape = {3, 2}; std::array<float, 3 * 2> x_values = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; auto input_data = std::unique_ptr<void, CudaMemoryDeleter>(cuda_allocator.Alloc(x_values.size() * sizeof(float)), CudaMemoryDeleter(&cuda_allocator)); cudaMemcpy(input_data.get(), x_values.data(), sizeof(float) * x_values.size(), cudaMemcpyHostToDevice); // Create an OrtValue tensor backed by data on CUDA memory Ort::Value bound_x = Ort::Value::CreateTensor(info_cuda, reinterpret_cast<float*>(input_data.get()), x_values.size(), x_shape.data(), x_shape.size()); const std::array<int64_t, 2> expected_y_shape = {3, 2}; std::array<float, 3 * 2> expected_y = {1.0f, 4.0f, 9.0f, 16.0f, 25.0f, 36.0f}; auto output_data = std::unique_ptr<void, CudaMemoryDeleter>(cuda_allocator.Alloc(expected_y.size() * sizeof(float)), CudaMemoryDeleter(&cuda_allocator)); // Create an OrtValue tensor backed by data on CUDA memory Ort::Value bound_y = Ort::Value::CreateTensor(info_cuda, reinterpret_cast<float*>(output_data.get()), expected_y.size(), expected_y_shape.data(), expected_y_shape.size()); Ort::IoBinding binding(session); binding.BindInput("X", bound_x); binding.BindOutput("Y", bound_y); // One regular run for necessary memory allocation and graph capturing session.Run(run_option, binding); // After capturing, CUDA graph replay happens from this Run onwards session.Run(run_option, binding); // Update input and then replay CUDA graph with the updated input x_values = {10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f}; cudaMemcpy(input_data.get(), x_values.data(), sizeof(float) * x_values.size(), cudaMemcpyHostToDevice); session.Run(run_option, binding);
- C# (未来)
示例
Python
import onnxruntime as ort
model_path = '<path to model>'
providers = [
('CUDAExecutionProvider', {
'device_id': 0,
'arena_extend_strategy': 'kNextPowerOfTwo',
'gpu_mem_limit': 2 * 1024 * 1024 * 1024,
'cudnn_conv_algo_search': 'EXHAUSTIVE',
'do_copy_in_default_stream': True,
}),
'CPUExecutionProvider',
]
session = ort.InferenceSession(model_path, providers=providers)
C/C++
使用旧版提供程序选项结构体
OrtSessionOptions* session_options = /* ... */;
OrtCUDAProviderOptions options;
options.device_id = 0;
options.arena_extend_strategy = 0;
options.gpu_mem_limit = 2 * 1024 * 1024 * 1024;
options.cudnn_conv_algo_search = OrtCudnnConvAlgoSearchExhaustive;
options.do_copy_in_default_stream = 1;
SessionOptionsAppendExecutionProvider_CUDA(session_options, &options);
使用 V2 提供程序选项结构体
OrtCUDAProviderOptionsV2* cuda_options = nullptr;
CreateCUDAProviderOptions(&cuda_options);
std::vector<const char*> keys{"device_id", "gpu_mem_limit", "arena_extend_strategy", "cudnn_conv_algo_search", "do_copy_in_default_stream", "cudnn_conv_use_max_workspace", "cudnn_conv1d_pad_to_nc1d"};
std::vector<const char*> values{"0", "2147483648", "kSameAsRequested", "DEFAULT", "1", "1", "1"};
UpdateCUDAProviderOptions(cuda_options, keys.data(), values.data(), keys.size());
cudaStream_t cuda_stream;
cudaStreamCreate(&cuda_stream);
// this implicitly sets "has_user_compute_stream"
UpdateCUDAProviderOptionsWithValue(cuda_options, "user_compute_stream", cuda_stream);
OrtSessionOptions* session_options = /* ... */;
SessionOptionsAppendExecutionProvider_CUDA_V2(session_options, cuda_options);
// Finally, don't forget to release the provider options
ReleaseCUDAProviderOptions(cuda_options);
C#
var cudaProviderOptions = new OrtCUDAProviderOptions(); // Dispose this finally
var providerOptionsDict = new Dictionary<string, string>();
providerOptionsDict["device_id"] = "0";
providerOptionsDict["gpu_mem_limit"] = "2147483648";
providerOptionsDict["arena_extend_strategy"] = "kSameAsRequested";
providerOptionsDict["cudnn_conv_algo_search"] = "DEFAULT";
providerOptionsDict["do_copy_in_default_stream"] = "1";
providerOptionsDict["cudnn_conv_use_max_workspace"] = "1";
providerOptionsDict["cudnn_conv1d_pad_to_nc1d"] = "1";
cudaProviderOptions.UpdateOptions(providerOptionsDict);
SessionOptions options = SessionOptions.MakeSessionOptionWithCudaProvider(cudaProviderOptions); // Dispose this finally
另请参阅 此处的教程,了解如何在 Windows 上为 C# 配置 CUDA。
Java
OrtCUDAProviderOptions cudaProviderOptions = new OrtCUDAProviderOptions(/*device id*/0); // Must be closed after the session closes
cudaProviderOptions.add("gpu_mem_limit","2147483648");
cudaProviderOptions.add("arena_extend_strategy","kSameAsRequested");
cudaProviderOptions.add("cudnn_conv_algo_search","DEFAULT");
cudaProviderOptions.add("do_copy_in_default_stream","1");
cudaProviderOptions.add("cudnn_conv_use_max_workspace","1");
cudaProviderOptions.add("cudnn_conv1d_pad_to_nc1d","1");
OrtSession.SessionOptions options = new OrtSession.SessionOptions(); // Must be closed after the session closes
options.addCUDA(cudaProviderOptions);