mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-21 19:18:55 +00:00
221 lines
9.7 KiB
Python
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()
|