onnxruntime/onnxruntime/test/python/onnxruntime_test_python_cudagraph.py
Justin Chu 09c4cc7b36
Target py310 and modernize codebase with ruff (#23401)
Change `target-version = "py310"` and modernize the code base with ruff.
2025-01-16 19:10:14 -08:00

221 lines
9.7 KiB
Python

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import unittest
import numpy as np
from helper import get_name
import onnxruntime as onnxrt
class CudaGraphHelper:
def __init__(
self,
ort_session: onnxrt.InferenceSession,
input_and_output_shape: dict[str, list[int]],
device_id: int = 0,
):
self.input_names = [input.name for input in ort_session.get_inputs()]
self.output_names = [output.name for output in ort_session.get_outputs()]
self.input_and_output_shape = input_and_output_shape
self.io_numpy_type = self.get_io_numpy_type_map(ort_session)
self.io_binding = ort_session.io_binding()
self.io_ort_value = {}
for name in self.input_names + self.output_names:
ort_value = onnxrt.OrtValue.ortvalue_from_shape_and_type(
input_and_output_shape[name], self.io_numpy_type[name], "cuda", device_id
)
self.io_ort_value[name] = ort_value
if name in self.input_names:
self.io_binding.bind_ortvalue_input(name, ort_value)
else:
self.io_binding.bind_ortvalue_output(name, ort_value)
def get_io_numpy_type_map(self, ort_session: onnxrt.InferenceSession):
ort_type_to_numpy_type = {
"tensor(int64)": np.longlong,
"tensor(int32)": np.intc,
"tensor(float)": np.float32,
"tensor(float16)": np.float16,
}
name_to_numpy_type = {}
for _input in ort_session.get_inputs():
name_to_numpy_type[_input.name] = ort_type_to_numpy_type[_input.type]
for output in ort_session.get_outputs():
name_to_numpy_type[output.name] = ort_type_to_numpy_type[output.type]
return name_to_numpy_type
def update_inputs(self, inputs: dict[str, np.ndarray]):
for input_name in self.input_names:
self.io_ort_value[input_name].update_inplace(inputs[input_name])
def get_output(self, output_name: str):
return self.io_ort_value[output_name].numpy()
class TestInferenceSessionWithCudaGraph(unittest.TestCase):
def test_ort_value_update_in_place(self):
x0 = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], dtype=np.float32)
ortvalue_cpu = onnxrt.OrtValue.ortvalue_from_numpy(x0)
np.testing.assert_allclose(x0, ortvalue_cpu.numpy())
x1 = np.array([[10.0, 20.0], [30.0, 40.0], [50.0, 60.0]], dtype=np.float32)
ortvalue_cpu.update_inplace(x1)
np.testing.assert_allclose(x1, ortvalue_cpu.numpy())
if "CUDAExecutionProvider" in onnxrt.get_available_providers():
ortvalue_gpu = onnxrt.OrtValue.ortvalue_from_numpy(x0, "cuda", 0)
np.testing.assert_allclose(x0, ortvalue_gpu.numpy())
ortvalue_gpu.update_inplace(x1)
np.testing.assert_allclose(x1, ortvalue_gpu.numpy())
def test_select_ep_to_run_cuda_graph(self):
if "TensorrtExecutionProvider" in onnxrt.get_available_providers():
providers = [("TensorrtExecutionProvider", {"trt_cuda_graph_enable": True})]
self.run_model_with_cuda_graph(providers)
elif "CUDAExecutionProvider" in onnxrt.get_available_providers():
providers = [("CUDAExecutionProvider", {"enable_cuda_graph": True})]
self.run_model_with_cuda_graph(providers)
self.run_model_with_cuda_graph_annotation(providers)
def run_model_with_cuda_graph(self, providers):
INPUT_SIZE = 1280 # noqa: N806
x = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]] * INPUT_SIZE, dtype=np.float32)
y = np.array([[0.0], [0.0], [0.0]] * INPUT_SIZE, dtype=np.float32)
x_ortvalue = onnxrt.OrtValue.ortvalue_from_numpy(x, "cuda", 0)
y_ortvalue = onnxrt.OrtValue.ortvalue_from_numpy(y, "cuda", 0)
onnxrt.set_default_logger_severity(0)
session = onnxrt.InferenceSession(get_name("matmul_2.onnx"), providers=providers)
io_binding = session.io_binding()
# Bind the input and output
io_binding.bind_ortvalue_input("X", x_ortvalue)
io_binding.bind_ortvalue_output("Y", y_ortvalue)
ro = onnxrt.RunOptions()
# 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]] * INPUT_SIZE, 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
x_ortvalue.update_inplace(
np.array(
[[10.0, 20.0], [30.0, 40.0], [50.0, 60.0]] * INPUT_SIZE,
dtype=np.float32,
)
)
session.run_with_iobinding(io_binding, ro)
np.testing.assert_allclose(
np.array([[50.0], [110.0], [170.0]] * INPUT_SIZE, dtype=np.float32),
y_ortvalue.numpy(),
rtol=1e-05,
atol=1e-05,
)
def run_model_with_cuda_graph_annotation(self, providers):
INPUT_SIZE = 1280 # noqa: N806
x_base = [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0]]
y_base = [[0.0], [0.0], [0.0], [0.0]]
expected_y_base = [[5.0], [11.0], [17.0], [23.0]]
x_base_mul_10 = [[10.0, 20.0], [30.0, 40.0], [50.0, 60.0], [70.0, 80.0]]
expected_y_base_mul_10 = [[50.0], [110.0], [170.0], [230.0]]
test_num = 4
x_ortvalues = []
y_ortvalues = []
for i in range(test_num):
x = np.array(x_base[: i + 1][:] * INPUT_SIZE, dtype=np.float32)
y = np.array(y_base[: i + 1][:] * INPUT_SIZE, dtype=np.float32)
x_ortvalues.append(onnxrt.OrtValue.ortvalue_from_numpy(x, "cuda", 0))
y_ortvalues.append(onnxrt.OrtValue.ortvalue_from_numpy(y, "cuda", 0))
onnxrt.set_default_logger_severity(0)
session = onnxrt.InferenceSession(get_name("matmul_2.onnx"), providers=providers)
io_bindings = [session.io_binding()] * test_num
ro = onnxrt.RunOptions()
# Regular run to capture CUDA graph
for i in range(test_num):
io_bindings[i].bind_ortvalue_input("X", x_ortvalues[i])
io_bindings[i].bind_ortvalue_output("Y", y_ortvalues[i])
# TODO: Temporarily remove the default cuda graph capture test for the first regular run
# because it fails on a training CI. Need to investigate the root cause.
ro.add_run_config_entry("gpu_graph_id", str(i + 1))
io_bindings[i].synchronize_inputs()
session.run_with_iobinding(io_bindings[i], ro)
io_bindings[i].synchronize_outputs()
expected_y = np.array(expected_y_base[: i + 1][:] * INPUT_SIZE, dtype=np.float32)
np.testing.assert_allclose(expected_y, y_ortvalues[i].numpy(), rtol=1e-05, atol=1e-05)
del ro
ro = onnxrt.RunOptions()
# After capturing, CUDA graph replay happens from this Run onwards
for i in range(test_num):
# Update input and then replay CUDA graph
x_ortvalues[i].update_inplace(np.array(x_base_mul_10[: i + 1][:] * INPUT_SIZE, dtype=np.float32))
ro.add_run_config_entry("gpu_graph_id", str(i + 1))
io_bindings[i].synchronize_inputs()
session.run_with_iobinding(io_bindings[i], ro)
io_bindings[i].synchronize_outputs()
expected_y = np.array(expected_y_base_mul_10[: i + 1][:] * INPUT_SIZE, dtype=np.float32)
np.testing.assert_allclose(expected_y, y_ortvalues[i].numpy(), rtol=1e-05, atol=1e-05)
def test_arena_with_cuda_graph(self):
if "CUDAExecutionProvider" in onnxrt.get_available_providers():
# To test cuda graph catpure, we set Arena extend strategy to be SameAsRequested so as to detect any
# potential memory allocation after the first run.
providers = [
("CUDAExecutionProvider", {"enable_cuda_graph": True, "arena_extend_strategy": "kSameAsRequested"})
]
test_model_path = get_name("squeezenet/model.onnx")
input_and_output_shape = {
"data_0": [16, 3, 224, 224],
"softmaxout_1": [16, 1000, 1, 1],
}
session_options = onnxrt.SessionOptions()
# It is optional to disable memory pattern since min_num_runs_before_cuda_graph_capture_ = 2.
session_options.enable_mem_pattern = False
session = onnxrt.InferenceSession(test_model_path, session_options, providers=providers)
cuda_graph_helper = CudaGraphHelper(session, input_and_output_shape)
io_binding = cuda_graph_helper.io_binding
# Create a random input for testing.
np.random.seed(0)
inputs = {"data_0": np.random.randint(0, 256, size=input_and_output_shape["data_0"]).astype(np.float32)}
# One regular run for the necessary memory allocation and cuda graph capturing
cuda_graph_helper.update_inputs(inputs)
session.run_with_iobinding(io_binding)
expected_output = cuda_graph_helper.get_output("softmaxout_1")
# After capturing, CUDA graph replay happens from this Run onwards
cuda_graph_helper.update_inputs(inputs)
session.run_with_iobinding(io_binding)
output = cuda_graph_helper.get_output("softmaxout_1")
np.testing.assert_allclose(expected_output, output, rtol=1e-02, atol=1e-02)
if __name__ == "__main__":
unittest.main()