CUDA Execution Provider
Der CUDA Execution Provider ermöglicht hardwarebeschleunigte Berechnungen auf Nvidia CUDA-fähigen GPUs.
Inhalt
- Installieren
- Erstellung aus Quelle
- Voraussetzungen
- Erstellen
- Kompatibilität mit PyTorch
- DLLs vorladen
- Konfigurationsoptionen
- Performance-Tuning
- Beispiele
Installieren
Vorkompilierte Binärdateien von ONNX Runtime mit CUDA EP werden für die meisten Sprachbindungen veröffentlicht. Bitte beachten Sie Install ORT.
Aus dem Quellcode kompilieren
Siehe Build-Anweisungen.
Voraussetzungen
Bitte beachten Sie die folgende Tabelle für die Abhängigkeiten offizieller GPU-Pakete für das ONNX Runtime Inferencing-Paket. Beachten Sie, dass ONNX Runtime Training mit PyTorch CUDA-Versionen abgestimmt ist; siehe den Tab "Optimize Training" auf onnxruntime.ai für unterstützte Versionen.
Aufgrund der Nvidia CUDA Minor Version Compatibility sind mit CUDA 11.8 erstellte ONNX Runtime-Versionen mit jeder CUDA 11.x-Version kompatibel; mit CUDA 12.x erstellte ONNX Runtime-Versionen sind mit jeder CUDA 12.x-Version kompatibel.
ONNX Runtime, das mit cuDNN 8.x erstellt wurde, ist nicht mit cuDNN 9.x kompatibel und umgekehrt. Sie können das Paket basierend auf den Hauptversionen von CUDA und cuDNN wählen, die Ihrer Laufzeitumgebung entsprechen (z. B. verwendet PyTorch 2.3 cuDNN 8.x, während PyTorch 2.4 oder neuer cuDNN 9.x verwendet).
Hinweis: Ab Version 1.19 wird **CUDA 12.x** zur Standardversion bei der Verteilung von ONNX Runtime GPU-Paketen in PyPI.
Um den Bedarf an manuellen Installationen von CUDA und cuDNN zu reduzieren und eine nahtlose Integration zwischen ONNX Runtime und PyTorch zu gewährleisten, bietet das Python-Paket onnxruntime-gpu APIs zum entsprechenden Laden von CUDA- und cuDNN-Dynamikbibliotheken (DLLs). Weitere Details finden Sie in den Abschnitten Kompatibilität mit PyTorch und DLLs vorladen.
CUDA 12.x
| ONNX Runtime | CUDA | cuDNN | Hinweise |
|---|---|---|---|
| 1.20.x | 12.x | 9.x | Verfügbar in PyPI. Kompatibel mit PyTorch >= 2.4.0 für CUDA 12.x. |
| 1.19.x | 12.x | 9.x | Verfügbar in PyPI. Kompatibel mit PyTorch >= 2.4.0 für CUDA 12.x. |
| 1.18.1 | 12.x | 9.x | cuDNN 9 ist erforderlich. Kein Java-Paket. |
| 1.18.0 | 12.x | 8.x | Java-Paket wurde hinzugefügt. |
| 1.17.x | 12.x | 8.x | Nur C++/C#-Nuget- und Python-Pakete werden veröffentlicht. Kein Java-Paket. |
CUDA 11.x
| ONNX Runtime | CUDA | cuDNN | Hinweise |
|---|---|---|---|
| 1.20.x | 11.8 | 8.x | Nicht in PyPI verfügbar. Siehe Install ORT für Details. Kompatibel mit PyTorch <= 2.3.1 für CUDA 11.8. |
| 1.19.x | 11.8 | 8.x | Nicht in PyPI verfügbar. Siehe Install ORT für Details. Kompatibel mit PyTorch <= 2.3.1 für CUDA 11.8. |
| 1.18.x | 11.8 | 8.x | Verfügbar in PyPI. |
| 1.17 1.16 1.15 | 11.8 | 8.2.4 (Linux) 8.5.0.96 (Windows) | Getestet mit CUDA-Versionen von 11.6 bis 11.8 und cuDNN von 8.2 bis 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 | Hinweise |
|---|---|---|---|
| 1.5-1.6 | 10.2 | 8.0.3 | CUDA 11 kann aus dem Quellcode erstellt werden |
| 1.2-1.4 | 10.1 | 7.6.5 | Erfordert cublas10-10.2.1.243; cublas 10.1.x funktioniert nicht |
| 1.0-1.1 | 10.0 | 7.6.4 | CUDA-Versionen von 9.1 bis 10.1 und cuDNN-Versionen von 7.1 bis 7.4 sollten auch mit Visual Studio 2017 funktionieren |
Für ältere Versionen beachten Sie bitte die Readme- und Build-Seiten des Release-Zweigs.
Build
Für Build-Anweisungen siehe die BUILD-Seite.
Kompatibilität mit PyTorch
Das Paket onnxruntime-gpu ist so konzipiert, dass es nahtlos mit PyTorch zusammenarbeitet, vorausgesetzt, beide werden gegen dieselbe Hauptversion von CUDA und cuDNN erstellt. Bei der Installation von PyTorch mit CUDA-Unterstützung sind die erforderlichen CUDA- und cuDNN-DLLs enthalten, sodass keine separaten Installationen des CUDA-Toolkits oder cuDNN erforderlich sind.
Um sicherzustellen, dass ONNX Runtime die von PyTorch installierten DLLs verwendet, können Sie diese Bibliotheken vorladen, bevor Sie eine Inferenzsitzung erstellen. Dies kann durch den Import von PyTorch oder durch Verwendung der Funktion onnxruntime.preload_dlls() erreicht werden.
Beispiel 1: PyTorch importieren
# 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"])
Beispiel 2: Verwendung der Funktion 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"])
DLLs vorladen
Seit Version 1.21.0 bietet das Paket onnxruntime-gpu die Funktion preload_dlls zum Vorladen von CUDA-, cuDNN- und Microsoft Visual C++ (MSVC)-Laufzeit-DLLs. Diese Funktion bietet Flexibilität bei der Angabe, welche Bibliotheken geladen und aus welchen Verzeichnissen.
Funktionssignatur
onnxruntime.preload_dlls(cuda=True, cudnn=True, msvc=True, directory=None)
Parameter
cuda(bool): Lädt CUDA-DLLs vor, wenn aufTruegesetzt.cudnn(bool): Lädt cuDNN-DLLs vor, wenn aufTruegesetzt.msvc(bool): Lädt MSVC-Laufzeit-DLLs vor, wenn aufTruegesetzt.directory(str oder None): Verzeichnis, aus dem die DLLs geladen werden sollen.None: Suche in Standardverzeichnissen.""(leerer String): Suche in NVIDIA Site Packages.- Spezifischer Pfad: Lädt DLLs aus dem angegebenen Verzeichnis.
Standard-Suchreihenfolge
Wenn directory=None, sucht die Funktion nach CUDA- und cuDNN-DLLs in der folgenden Reihenfolge:
- Unter Windows das
lib-Verzeichnis unter der PyTorch-Installation. - Python-Site-Packages-Verzeichnisse für NVIDIA CUDA- oder cuDNN-Bibliotheken (z. B.
nvidia_cuda_runtime_cu12,nvidia_cudnn_cu12). - Fallback zum Standardverhalten für das Laden von DLLs.
Durch das Vorladen der erforderlichen DLLs unter Verwendung der Standard-Suchreihenfolge können Sie sicherstellen, dass ONNX Runtime nahtlos mit PyTorch funktioniert.
Installation von CUDA und cuDNN über onnxruntime-gpu
Sie können die erforderlichen CUDA- und cuDNN-Laufzeit-DLLs zusammen mit dem Paket onnxruntime-gpu mit pip installieren.
pip install onnxruntime-gpu[cuda,cudnn]
Vorladen von DLLs aus NVIDIA Site Packages
So laden Sie CUDA- und cuDNN-DLLs aus NVIDIA Site Packages vor und zeigen Debug-Informationen an.
import onnxruntime
# Preload DLLs from NVIDIA site packages
onnxruntime.preload_dlls(directory="")
# Print debug information
onnxruntime.print_debug_info()
Laden von DLLs aus bestimmten Verzeichnissen
Um DLLs von einem bestimmten Speicherort zu laden, setzen Sie den Parameter directory auf einen absoluten Pfad oder einen Pfad relativ zum Wurzelverzeichnis des ONNX Runtime-Pakets.
Beispiel: Laden von CUDA aus der Systeminstallation und cuDNN aus dem NVIDIA Site Package
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()
Konfigurationsoptionen
Der CUDA Execution Provider unterstützt die folgenden Konfigurationsoptionen.
device_id
Die Geräte-ID.
Standardwert: 0
user_compute_stream
Definiert den Compute-Stream, auf dem die Inferenz ausgeführt werden soll. Er setzt implizit die Option has_user_compute_stream. Sie kann nicht über UpdateCUDAProviderOptions, sondern über UpdateCUDAProviderOptionsWithValue gesetzt werden. Dies kann nicht in Kombination mit einem externen Allocator verwendet werden.
Beispiel für Python-Nutzung
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)
Um den Vorteil des Benutzer-Compute-Streams zu nutzen, wird empfohlen, I/O Binding zu verwenden, um Ein- und Ausgaben an Tensoren im Gerät zu binden.
do_copy_in_default_stream
Ob Kopien im Standard-Stream durchgeführt werden sollen oder separate Streams verwendet werden sollen. Die empfohlene Einstellung ist true. Wenn false, gibt es Race Conditions und möglicherweise eine bessere Leistung.
Standardwert: true
use_ep_level_unified_stream
Verwendet denselben CUDA-Stream für alle Threads des CUDA EP. Dies wird implizit durch has_user_compute_stream, enable_cuda_graph oder bei Verwendung eines externen Allocators aktiviert.
Standardwert: false
gpu_mem_limit
Die Größenbeschränkung des Gerätespeicher-Arenas in Bytes. Diese Größenbeschränkung gilt nur für die Arena des Execution Providers. Die Gesamtauslastung des Gerätespeichers kann höher sein. s: Maximalwert des C++ size_t-Typs (effektiv unbegrenzt)
Hinweis: Wird durch den Inhalt von default_memory_arena_cfg (falls angegeben) überschrieben
arena_extend_strategy
Die Strategie zur Erweiterung des Gerätespeicher-Arenas.
| Wert | Beschreibung |
|---|---|
| kNextPowerOfTwo (0) | nachfolgende Erweiterungen erfolgen in grösseren Schritten (multipliziert mit Zweierpotenzen) |
| kSameAsRequested (1) | Erweiterung um den angeforderten Betrag |
Standardwert: kNextPowerOfTwo
Hinweis: Wird durch den Inhalt von default_memory_arena_cfg (falls angegeben) überschrieben
cudnn_conv_algo_search
Der Typ der Suche, die für cuDNN-Konvolutionsalgorithmen durchgeführt wird.
| Wert | Beschreibung |
|---|---|
| EXHAUSTIVE (0) | Aufwendiges, erschöpfendes Benchmarking mit cudnnFindConvolutionForwardAlgorithmEx |
| HEURISTIC (1) | Leichtgewichtige heuristische Suche mit cudnnGetConvolutionForwardAlgorithm_v7 |
| DEFAULT (2) | Standardalgorithmus mit CUDNN_CONVOLUTION_FWD_ALGO_IMPLICIT_PRECOMP_GEMM |
Standardwert: EXHAUSTIVE
cudnn_conv_use_max_workspace
Siehe Tuning-Leistung für konvolutionslastige Modelle für Details, was dieses Flag bewirkt. Dieses Flag wird nur ab Version V2 der Provider-Optionsstruktur unterstützt, wenn es über die C-API verwendet wird. (Beispiel unten)
Standardwert: 1, ab Version 1.14; 0, für frühere Versionen
cudnn_conv1d_pad_to_nc1d
Siehe Konvolutions-Input-Padding im CUDA EP für Details, was dieses Flag bewirkt. Dieses Flag wird nur ab Version V2 der Provider-Optionsstruktur unterstützt, wenn es über die C-API verwendet wird. (Beispiel unten)
Standardwert: 0
enable_cuda_graph
Siehe Verwendung von CUDA-Graphen in der CUDA EP für Details, was dieses Flag bewirkt. Dieses Flag wird nur ab Version V2 der Provider-Optionsstruktur unterstützt, wenn es über die C-API verwendet wird. (Beispiel unten)
Standardwert: 0
enable_skip_layer_norm_strict_mode
Ob der Strict Mode in der SkipLayerNormalization CUDA-Implementierung verwendet werden soll. Die Standard- und empfohlene Einstellung ist false. Wenn aktiviert, sind Genauigkeitsverbesserungen und Leistungseinbußen zu erwarten. Dieses Flag wird nur ab Version V2 der Provider-Optionsstruktur unterstützt, wenn es über die C-API verwendet wird. (Beispiel unten)
Standardwert: 0
use_tf32
TF32 ist ein mathematischer Modus, der auf NVIDIA-GPUs seit Ampere verfügbar ist. Er ermöglicht die Ausführung bestimmter Float32-Matrixmultiplikationen und -konvolutionen auf Tensor-Kernen mit viel höherer Geschwindigkeit mit der TensorFloat-32 reduzierten Präzision: Float32-Eingaben werden mit 10 Bits Mantisse gerundet und Ergebnisse mit Float32-Präzision akkumuliert.
Standardwert: 1
TensorFloat-32 ist standardmäßig aktiviert. Ab ONNX Runtime 1.18 können Sie dieses Flag verwenden, um es für eine Inferenzsitzung zu deaktivieren.
Beispiel für Python-Nutzung
providers = [("CUDAExecutionProvider", {"use_tf32": 0})]
sess_options = ort.SessionOptions()
sess = ort.InferenceSession("my_model.onnx", sess_options=sess_options, providers=providers)
Dieses Flag wird nur ab Version V2 der Provider-Optionsstruktur unterstützt, wenn es über die C-API verwendet wird. (Beispiel unten)
gpu_external_[alloc|free|empty_cache]
gpu_external_* wird verwendet, um externe Allocatoren zu übergeben. Beispiel für Python-Nutzung
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())
Standardwert: 0
prefer_nhwc
Diese Option ist seit ONNX Runtime 1.20 verfügbar, wobei der Build standardmäßig onnxruntime_USE_CUDA_NHWC_OPS=ON hat.
Wenn diese Option aktiviert ist, bevorzugt der Execution Provider NHWC-Operatoren gegenüber NCHW. Notwendige Layout-Transformationen werden automatisch auf das Modell angewendet. Da NVIDIA Tensor-Kerne mit NHWC-Layout effizienter arbeiten, kann die Aktivierung dieser Option die Leistung verbessern, wenn das Modell aus vielen unterstützten Operatoren besteht und keine übermäßigen zusätzlichen Transpose-Operationen erfordert. Eine breitere Unterstützung von Operatoren für NHWC ist für zukünftige Releases geplant.
Dieses Flag wird nur in der V2-Version der Provider-Optionsstruktur unterstützt, wenn die C-API verwendet wird. Die V2-Provider-Optionsstruktur kann mit CreateCUDAProviderOptions erstellt und mit UpdateCUDAProviderOptions aktualisiert werden.
Standardwert: 0
Performance-Tuning
Die Funktion I/O Binding sollte verwendet werden, um Overhead durch Kopien bei Ein- und Ausgaben zu vermeiden. Idealerweise können Up- und Downloads für Eingaben hinter der Inferenz verborgen werden. Dies kann durch asynchrone Kopien während der Ausführung der Inferenz erreicht werden. Dies wird in diesem PR demonstriert.
Ort::RunOptions run_options;
run_options.AddConfigEntry("disable_synchronize_execution_providers", "1");
session->Run(run_options, io_binding);
Durch die Deaktivierung der Synchronisation bei der Inferenz muss der Benutzer die Synchronisation des Compute-Streams nach der Ausführung übernehmen. Dieses Feature sollte nur mit speicherlokalem Speicher oder einem ORT-Wert verwendet werden, der in Pinned Memory zugewiesen wurde, andernfalls ist der ausgegebene Download blockierend und verhält sich nicht wie gewünscht.
Konvolutionslastige Modelle
ORT nutzt CuDNN für Konvolutionsoperationen, und der erste Schritt in diesem Prozess besteht darin, den "optimalen" Konvolutionsalgorithmus zu bestimmen, der für die gegebene Eingabekonfiguration (Input-Form, Filter-Form usw.) in jedem Conv-Knoten verwendet werden soll. Dieser Teilschritt beinhaltet die Abfrage von CuDNN nach der Größe eines "Workspace"-Speichers und dessen Zuweisung, damit CuDNN diesen Hilfsspeicher zur Bestimmung des "optimalen" Konvolutionsalgorithmus verwenden kann.
Der Standardwert von cudnn_conv_use_max_workspace ist 1 für Versionen ab 1.14 und 0 für frühere Versionen. Wenn sein Wert 0 ist, begrenzt ORT die Workspace-Größe auf 32 MB, was dazu führen kann, dass CuDNN einen sub-optimalen Konvolutionsalgorithmus wählt. Um ORT die Zuweisung des maximalen Workspaces, der von CuDNN bestimmt wird, zu ermöglichen, muss eine Provider-Option namens cudnn_conv_use_max_workspace gesetzt werden (wie unten gezeigt).
Beachten Sie, dass die Verwendung dieses Flags die Spitzen-Speichernutzung um einen Faktor (manchmal wenige GBs) erhöhen kann, aber CuDNN hilft, den besten Konvolutionsalgorithmus für die gegebene Eingabe auszuwählen. Wir haben festgestellt, dass dies ein wichtiges Flag bei der Verwendung eines fp16-Modells ist, da es CuDNN ermöglicht, Tensor-Core-Algorithmen für Konvolutionsoperationen auszuwählen (falls die Hardware Tensor-Core-Operationen unterstützt). Dieses Flag kann bei anderen Datentypen (float und double) zu Leistungssteigerungen führen oder auch nicht.
- 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
Konvolutions-Input-Padding
ORT nutzt CuDNN für Konvolutionsoperationen. Während CuDNN nur 4-D- oder 5-D-Tensoren als Eingabe für Konvolutionsoperationen akzeptiert, ist eine Dimensions-Padding erforderlich, wenn die Eingabe ein 3-D-Tensor ist. Bei einem Eingabetensor der Form [N, C, D] kann dieser zu [N, C, D, 1] oder [N, C, 1, D] aufgepolstert werden. Obwohl beide Polsterungsarten die gleiche Ausgabe erzeugen, kann die Leistung sehr unterschiedlich sein, da unterschiedliche Konvolutionsalgorithmen ausgewählt werden, insbesondere auf einigen Geräten wie A100. Standardmäßig wird die Eingabe zu [N, C, D, 1] aufgepolstert. Eine Provider-Option namens cudnn_conv1d_pad_to_nc1d muss gesetzt werden (wie unten gezeigt), wenn [N, C, 1, D] bevorzugt wird.
- 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
Verwendung von CUDA-Graphen (Vorschau)
Bei Verwendung des CUDA EP unterstützt ORT die Verwendung von CUDA-Graphen, um den CPU-Overhead zu entfernen, der mit der sequentiellen Ausführung von CUDA-Kernels verbunden ist. Um die Verwendung von CUDA-Graphen zu aktivieren, verwenden Sie die Provider-Optionen wie in den folgenden Beispielen gezeigt. ORT unterstützt die Multi-Graph-Erfassungsfähigkeit, indem die vom Benutzer angegebene gpu_graph_id an die Run-Optionen übergeben wird. gpu_graph_id ist optional, wenn die Sitzung einen CUDA-Graphen verwendet. Wenn nicht gesetzt, ist der Standardwert 0. Wenn gpu_graph_id auf -1 gesetzt ist, ist die CUDA-Graph-Erfassung/Wiederholung bei diesem Lauf deaktiviert.
Derzeit gibt es einige Einschränkungen bei der Verwendung der CUDA-Graphen-Funktion:
-
Modelle mit Kontrollfluss-Ops (d. h.
If,LoopundScanOps) werden nicht unterstützt. -
Die Verwendung von CUDA-Graphen ist auf Modelle beschränkt, bei denen alle Modell-Ops (Graph-Knoten) auf den CUDA EP partitioniert werden können.
-
Die Ein-/Ausgabetypen von Modellen müssen Tensoren sein.
-
Formen und Adressen von Ein-/Ausgaben dürfen sich bei Inferenzaufrufen für dieselbe Graph-Annotation-ID nicht ändern. Eingabetensoren für die Wiederholung müssen an die Adresse der Eingabetensoren kopiert werden, die bei der Graphenerfassung verwendet wurden.
-
Im Multi-Graph-Erfassungsmodus bleiben die erfassten Graphen über die Lebensdauer der Sitzung bestehen und die Funktion zum Löschen erfasster Graphen wird derzeit nicht unterstützt.
-
Per Design ist CUDA Graphs so konzipiert, dass während des Graphenwiederholungsschritts aus denselben CUDA-virtuellen Speicheradressen gelesen/in diese geschrieben wird, wie während des Graphenerfassungsschritts. Aufgrund dieser Anforderung erfordert die Verwendung dieser Funktion IOBinding, um Speicher zu binden, der als Ein-/Ausgabe(n) für die CUDA-Graph-Maschinen zum Lesen/Schreiben verwendet wird (siehe Beispiele unten).
-
Bei der Aktualisierung der Eingaben für nachfolgende Inferenzaufrufe müssen die neuen Eingaben über die entsprechenden CUDA-Speicheradressen der gebundenen
OrtValue-Eingaben kopiert werden (siehe Beispiele unten, um zu sehen, wie dies erreicht werden kann). Dies liegt daran, dass die "Graph-Wiederholung" Eingaben von denselben CUDA-virtuellen Speicheradressen lesen muss. -
Multi-Thread-Nutzung wird derzeit nicht unterstützt, d.h.
Run()darf NICHT von mehreren Threads auf demselbenInferenceSession-Objekt aufgerufen werden, während CUDA-Graphen verwendet werden.
HINWEIS: Der allererste Run()-Aufruf führt eine Vielzahl von Aufgaben im Hintergrund aus, wie z. B. CUDA-Speicherzuweisungen, die Erfassung des CUDA-Graphen für das Modell und dann eine Graph-Wiederholung, um sicherzustellen, dass der Graph ausgeführt wird. Aus diesem Grund ist die Latenz des ersten Run()-Aufrufs zwangsläufig hoch. Nachfolgende Run()-Aufrufe führen nur Graph-Wiederholungen des im ersten Run() erfassten und zwischengespeicherten Graphen aus.
-
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# (zukünftig)
Beispiele
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++
Verwendung der Legacy-Provider-Optionsstruktur
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);
Verwendung der V2-Provider-Optionsstruktur
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
Siehe auch das Tutorial hier, wie CUDA für C# unter Windows konfigurieren.
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);