CUDA 执行提供程序
CUDA 执行提供程序支持在启用 Nvidia CUDA 的 GPU 上进行硬件加速计算。
目录
安装
预构建的带有 CUDA EP 的 ONNX Runtime 二进制文件已针对大多数语言绑定发布。请参考安装 ORT。
从源代码构建
请参阅构建说明。
要求
请参考下表了解 ONNX Runtime 推理包的官方 GPU 包依赖项。请注意,ONNX Runtime Training 与 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 不兼容,反之亦然。您可以根据与您的运行时环境匹配的 CUDA 和 cuDNN 主要版本选择软件包(例如,PyTorch 2.3 使用 cuDNN 8.x,而 PyTorch 2.4 或更高版本使用 cuDNN 9.x)。
注意:从 1.19 版本开始,在 PyPI 中分发 ONNX Runtime GPU 包时,CUDA 12.x 成为默认版本。
为了减少手动安装 CUDA 和 cuDNN 的需求,并确保 ONNX Runtime 和 PyTorch 之间的无缝集成,onnxruntime-gpu
Python 包提供了 API 以适当加载 CUDA 和 cuDNN 动态链接库 (DLL)。有关更多详细信息,请参阅与 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 和构建页面。
构建
有关构建说明,请参阅构建页面。
与 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 站点包中搜索。- 特定路径:从指定目录加载 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 将必要的 CUDA 和 cuDNN 运行时 DLL 与 onnxruntime-gpu
包一起安装
pip install onnxruntime-gpu[cuda,cudnn]
从 NVIDIA 站点包预加载 DLL
要从 NVIDIA 站点包预加载 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 站点包加载 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 的大小限制(以字节为单位)。此大小限制仅适用于执行提供程序的 Arena。总设备内存使用量可能会更高。s:C++ size_t 类型的最大值(实际上是无限的)
注意:将被 default_memory_arena_cfg
的内容覆盖(如果指定)
arena_extend_strategy
扩展设备内存 Arena 的策略。
值 | 描述 |
---|---|
kNextPowerOfTwo (0) | 后续扩展按更大的量扩展(乘以 2 的幂) |
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.14 及更高版本为 1,对于以前版本为 0
cudnn_conv1d_pad_to_nc1d
查看CUDA EP 中的卷积输入填充,详细了解此标志的作用。此标志仅在使用 C API 时,从提供程序选项结构的 V2 版本开始支持。(以下示例)
默认值:0
enable_cuda_graph
查看在 CUDA EP 中使用 CUDA Graphs,详细了解此标志的作用。此标志仅在使用 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 版本中受支持。V2 提供程序选项结构可以使用 CreateCUDAProviderOptions 创建,并使用 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 Value 一起使用,否则,发出的下载将是阻塞的,并且不会按预期运行。
卷积密集型模型
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 Graphs(预览)
在使用 CUDA EP 时,ORT 支持使用 CUDA Graphs 来消除与顺序启动 CUDA 内核相关的 CPU 开销。要启用 CUDA Graphs 的使用,请使用以下示例中所示的提供程序选项。ORT 通过将用户指定的 gpu_graph_id 传递给运行选项来支持多图捕获功能。当会话使用一个 cuda 图时,gpu_graph_id 是可选的。如果未设置,则默认值为 0。如果 gpu_graph_id 设置为 -1,则在该运行中禁用 cuda 图捕获/重放。
目前,对于使用 CUDA Graphs 功能存在一些约束
-
不支持带有控制流操作(即
If
、Loop
和Scan
操作)的模型。 -
CUDA Graphs 的使用仅限于模型中的所有模型操作(图节点)都可以分区到 CUDA EP 的情况。
-
模型的输入/输出类型必须是张量。
-
对于相同的图注释 ID,输入/输出的形状和地址不能在推理调用之间更改。重放的输入张量应复制到图捕获中使用的输入张量的地址。
-
在多图捕获模式下,捕获的图将保留在会话的生命周期内,并且目前不支持捕获的图删除功能。
-
按照设计,CUDA Graphs 旨在在图重放步骤期间从与图捕获步骤期间相同的 CUDA 虚拟内存地址读取/写入。由于此要求,此功能的使用需要使用 IOBinding,以便绑定将用作 CUDA Graph 机制读取/写入的输入/输出的内存(请参阅以下示例)。
-
在更新后续推理调用的输入时,需要将新的输入复制到绑定
OrtValue
输入的相应 CUDA 内存位置(请参阅以下示例,了解如何实现此目的)。这是因为“图重放”将需要从相同的 CUDA 虚拟内存地址读取输入。 -
目前不支持多线程使用,即在使用 CUDA Graphs 时,可能无法从多个线程在同一
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);