CUDA 执行提供程序

CUDA 执行提供程序支持在 Nvidia 启用 CUDA 的 GPU 上进行硬件加速计算。

目录

安装

ONNX Runtime 预构建的 CUDA EP 二进制文件已发布到大多数语言绑定。请参考 安装 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 中获取。与 PyTorch >= 2.4.0 兼容(适用于 CUDA 12.x)。
1.19.x 12.x 9.x 可在 PyPI 中获取。与 PyTorch >= 2.4.0 兼容(适用于 CUDA 12.x)。
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。与 PyTorch <= 2.3.1 兼容(适用于 CUDA 11.8)。
1.19.x 11.8 8.x PyPI 中不提供。有关详细信息,请参阅 安装 ORT。与 PyTorch <= 2.3.1 兼容(适用于 CUDA 11.8)。
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 站点包中搜索。
    • 特定路径:从指定目录加载 DLL。

默认搜索顺序

directory=None 时,该函数按以下顺序搜索 CUDA 和 cuDNN DLL:

  1. 在 Windows 上,PyTorch 安装目录下的 lib 目录。
  2. NVIDIA CUDA 或 cuDNN 库的 Python 站点包目录(例如,nvidia_cuda_runtime_cu12nvidia_cudnn_cu12)。
  3. 回退到默认的 DLL 加载行为。

通过使用默认搜索顺序预加载必要的 DLL,您可以确保 ONNX Runtime 与 PyTorch 无缝协作。

通过 onnxruntime-gpu 安装 CUDA 和 cuDNN

您可以使用 pip 与 onnxruntime-gpu 包一起安装必要的 CUDA 和 cuDNN 运行时 DLL:

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_streamenable_cuda_graph 隐式启用,或在使用外部分配器时启用。

默认值:false

gpu_mem_limit

设备内存区域的大小限制(以字节为单位)。此大小限制仅适用于执行提供程序的区域。总设备内存使用量可能更高。s:C++ size_t 类型的最大值(实际无限制)

注意:将被 default_memory_arena_cfg 的内容覆盖(如果指定)

arena_extend_strategy

扩展设备内存区域的策略。

描述
kNextPowerOfTwo (0) 后续扩展以更大的量扩展(乘以 2 的幂)
kSameAsRequested (1) 按请求量扩展

默认值:kNextPowerOfTwo

注意:将被 default_memory_arena_cfg 的内容覆盖(如果指定)

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 图(预览)。此标志仅在使用 C API 时,从提供程序选项结构体的 V2 版本开始支持。(示例如下)

默认值:0

enable_skip_layer_norm_strict_mode

是否在 SkipLayerNormalization CUDA 实现中使用严格模式。默认和推荐设置为 false。如果启用,预计准确性会提高,但性能会下降。此标志仅在使用 C API 时,从提供程序选项结构体的 V2 版本开始支持。(示例如下)

默认值:0

use_tf32

TF32 是自 Ampere 架构以来 NVIDIA GPU 上可用的一种数学模式。它允许某些 float32 矩阵乘法和卷积在 Tensor Core 上以 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 Tensor Core 在 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 为卷积操作选择 Tensor Core 算法(如果硬件支持 Tensor Core 操作)。此标志可能不会对其他数据类型(floatdouble)产生性能增益。

  • 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 维或 5 维张量作为卷积操作的输入,但如果输入是 3 维张量,则需要进行维度填充。给定形状为 [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 传递给运行选项。当会话使用一个 CUDA 图时,gpu_graph_id 是可选的。如果未设置,默认值为 0。如果 gpu_graph_id 设置为 -1,则在该运行中禁用 CUDA 图捕获/重放。

目前,使用 CUDA 图功能存在一些限制:

  • 不支持带有控制流操作符(即 IfLoopScan 操作符)的模型。

  • 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);