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

  1. 在 Windows 上,PyTorch 安装目录下的 lib 目录。
  2. NVIDIA CUDA 或 cuDNN 库的 Python site-packages 目录(例如,nvidia_cuda_runtime_cu12, nvidia_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 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_streamenable_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 卷积算法执行的搜索类型。

描述
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 为卷积操作选择张量核算法(如果硬件支持张量核操作)。对于其他数据类型(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-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 图 功能有一些限制

  • 不支持带有控制流算子(即 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);