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