I/O 绑定
当使用非 CPU 执行提供程序时,最高效的做法是在执行图(调用 Run()
)之前,将输入(和/或输出)安排在目标设备(由所使用的执行提供程序抽象)上。如果输入未复制到目标设备,ORT 会在 Run()
调用的一部分中从 CPU 复制它。类似地,如果输出未在设备上预先分配,ORT 会假定在 CPU 上请求输出,并在 Run()
调用的最后一步将其从设备复制出来。这会占用图的执行时间,误导用户认为 ORT 速度慢,而实际上大部分时间都花在了这些复制操作上。
为了解决这个问题,我们引入了 IOBinding 的概念。核心思想是在调用 Run()
之前,将输入复制到设备上,并将输出预先分配在设备上。IOBinding 在我们的所有语言绑定中都可用。
以下是不同语言的代码片段,演示了此功能的用法。
- C++
Ort::Env env; Ort::Session session(env, model_path, session_options); Ort::IoBinding io_binding{session}; auto input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), 4); io_binding.BindInput("input1", input_tensor); Ort::MemoryInfo output_mem_info{"Cuda", OrtDeviceAllocator, 0, OrtMemTypeDefault}; // Use this to bind output to a device when the shape is not known in advance. If the shape is known you can use the other overload of this function that takes an Ort::Value as input (IoBinding::BindOutput(const char* name, const Value& value)). // This internally calls the BindOutputToDevice C API. io_binding.BindOutput("output1", output_mem_info); session.Run(run_options, io_binding);
请注意,在上面的代码示例中,输出张量在绑定之前并未分配,而是将一个
Ort::MemoryInfo
绑定为输出。这是一种有效的方式,可以让会话根据所需的形状分配张量。特别是对于数据依赖的形状或动态形状,这可以是获取正确分配的绝佳解决方案。然而,如果输出形状已知且输出张量应该被重用,则最好也将一个Ort::Value
绑定到输出。这可以使用会话分配器或外部内存进行分配。有关更多详细信息,请参阅设备张量文档。Ort::Allocator gpu_allocator(session, output_mem_info); auto output_value = Ort::Value::CreateTensor( gpu_allocator, output_shape.data(), output_shape.size(), ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16); io_binding.BindOutput("output1", output_mem_info);
-
Python(参阅Python API 文档)
- C#(参阅OrtIoBindingAllocationTest.cs)