mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-13 18:08:13 +00:00
[TensorRT EP] Load precompiled TRT engine file directly (#18217)
When the TRT engine cache (precompiled engine) is present, it doesn't make sense to go over the processes of model verification, model optimization, TRT EP's GetCapability(), TRT EP's model proto reconstruction, calling TRT parser and engine compilation. This PR makes TRT EP skip those processes and directly load the engine to perform inference. The feature request: https://github.com/microsoft/onnxruntime/issues/18072 Features: - Replace original model with TRT engine wrapped ONNX model. It can save a lot of time as mentioned above. - How to get TRT engine wrapped ONNX model? 1. Set `trt_dump_ep_context_model` provider option to "true" and run the inference. You will find the "xxx_wrapper.onnx" at the engine cache path. (The same logic of generating engine cache) 2. Use gen_trt_engine_wrapper_onnx_model.py - Three provider options are added, `trt_dump_ep_context_model`: Enable dump wrapped onnx model by TRT EP `trt_ep_context_embed_mode`: Add embed_mode as attribute. 0 means engine cache path, 1 means engine binary data. `trt_ep_context_compute_capability_enable`: Add hardware_arch as attribute. When running the model, TRT EP will check consistency between model's hardware_arch and GPU's compute capability. - When the engine cache path is given in the wrapped model, TRT EP will first search for the engine file using the path (relative to model path), if it can't find it, it will change to use the path as it is (depends on user, could be relative to working dir or absolute path) Note: 1. This PR includes the change of https://github.com/microsoft/onnxruntime/pull/17751 Constraints: 1. The whole model should be fully supported by TRT. 4. Users need to make sure the engine is built with min/max/opt optimization profiles that large enough to cover the range of all inputs. TRT EP will simply fail and won't rebuild the engine if the input shape is out of range during runtime.
This commit is contained in:
parent
b6d82834d4
commit
46dd0d3f52
17 changed files with 1897 additions and 864 deletions
|
|
@ -1588,6 +1588,8 @@ This version of the operator has been available since version 1 of the 'com.micr
|
|||
<dd>payload of the execution provider context if embed_mode=1, or path to the context file if embed_mode=0.</dd>
|
||||
<dt><tt>ep_sdk_version</tt> : string</dt>
|
||||
<dd>(Optional) SDK version used to convert the model.</dd>
|
||||
<dt><tt>hardware_architecture</tt> : string</dt>
|
||||
<dd>(Optional) Hardware architecture.</dd>
|
||||
<dt><tt>main_context</tt> : int</dt>
|
||||
<dd>Usually each single EPContext associate with a graph partition.But for some case like QNN, it has single EPContext contains all partitions.In that case, the node with ep_cache_context should set main_context=1. Other nodes set main_context=0 and skip ep_cache_context.The path is relative to this Onnx file. Default is 1.</dd>
|
||||
<dt><tt>notes</tt> : string</dt>
|
||||
|
|
|
|||
|
|
@ -46,4 +46,7 @@ struct OrtTensorRTProviderOptionsV2 {
|
|||
const char* trt_profile_max_shapes{nullptr}; // Specify the range of the input shapes to build the engine with
|
||||
const char* trt_profile_opt_shapes{nullptr}; // Specify the range of the input shapes to build the engine with
|
||||
int trt_cuda_graph_enable{0}; // Enable CUDA graph in ORT TRT
|
||||
int trt_dump_ep_context_model{0}; // Dump EP context node model
|
||||
int trt_ep_context_embed_mode{0}; // Specify EP context embed mode. Default 0 = context is engine cache path, 1 = context is engine binary data
|
||||
int trt_ep_context_compute_capability_enable{1}; // Add GPU compute capability as an EP context node's attribute
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3230,6 +3230,11 @@ void RegisterContribSchemas() {
|
|||
"(Optional) SDK version used to convert the model.",
|
||||
AttributeProto::STRING,
|
||||
OPTIONAL_VALUE)
|
||||
.Attr(
|
||||
"hardware_architecture",
|
||||
"(Optional) Hardware architecture.",
|
||||
AttributeProto::STRING,
|
||||
OPTIONAL_VALUE)
|
||||
.Attr(
|
||||
"partition_name",
|
||||
"(Optional) partitioned graph name.",
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ struct ProviderHost {
|
|||
virtual int64_t AttributeProto__i(const ONNX_NAMESPACE::AttributeProto* p) = 0;
|
||||
virtual float AttributeProto__f(const ONNX_NAMESPACE::AttributeProto* p) = 0;
|
||||
virtual void AttributeProto__set_s(ONNX_NAMESPACE::AttributeProto* p, const ::std::string& value) = 0;
|
||||
virtual void AttributeProto__set_i(ONNX_NAMESPACE::AttributeProto* p, int64_t value) = 0;
|
||||
virtual const ::std::string& AttributeProto__s(const ONNX_NAMESPACE::AttributeProto* p) = 0;
|
||||
virtual void AttributeProto__set_name(ONNX_NAMESPACE::AttributeProto* p, const ::std::string& value) = 0;
|
||||
virtual void AttributeProto__set_type(ONNX_NAMESPACE::AttributeProto* p, ONNX_NAMESPACE::AttributeProto_AttributeType value) = 0;
|
||||
|
|
@ -351,6 +352,7 @@ struct ProviderHost {
|
|||
virtual ONNX_NAMESPACE::ValueInfoProtos* GraphProto__mutable_value_info(ONNX_NAMESPACE::GraphProto* p) = 0;
|
||||
virtual ONNX_NAMESPACE::TensorProtos* GraphProto__mutable_initializer(ONNX_NAMESPACE::GraphProto* p) = 0;
|
||||
virtual ONNX_NAMESPACE::NodeProto* GraphProto__add_node(ONNX_NAMESPACE::GraphProto* p) = 0;
|
||||
virtual ONNX_NAMESPACE::NodeProto* GraphProto__mutable_node(ONNX_NAMESPACE::GraphProto* p, int index) = 0;
|
||||
|
||||
// ModelProto
|
||||
virtual std::unique_ptr<ONNX_NAMESPACE::ModelProto> ModelProto__construct() = 0;
|
||||
|
|
@ -372,6 +374,7 @@ struct ProviderHost {
|
|||
virtual void NodeProto__operator_assign(ONNX_NAMESPACE::NodeProto* p, const ONNX_NAMESPACE::NodeProto& v) = 0;
|
||||
virtual int NodeProto__attribute_size(ONNX_NAMESPACE::NodeProto* p) = 0;
|
||||
virtual const ONNX_NAMESPACE::AttributeProto& NodeProto__attribute(const ONNX_NAMESPACE::NodeProto* p, int index) const = 0;
|
||||
virtual ONNX_NAMESPACE::AttributeProto* NodeProto__mutable_attribute(ONNX_NAMESPACE::NodeProto* p, int index) = 0;
|
||||
|
||||
// TensorProto
|
||||
virtual std::unique_ptr<ONNX_NAMESPACE::TensorProto> TensorProto__construct() = 0;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ struct AttributeProto final {
|
|||
int64_t i() const { return g_host->AttributeProto__i(this); }
|
||||
float f() const { return g_host->AttributeProto__f(this); }
|
||||
void set_s(const ::std::string& value) { return g_host->AttributeProto__set_s(this, value); }
|
||||
void set_i(int64_t value) { return g_host->AttributeProto__set_i(this, value); }
|
||||
const ::std::string& s() const { return g_host->AttributeProto__s(this); }
|
||||
void set_name(const ::std::string& value) { return g_host->AttributeProto__set_name(this, value); }
|
||||
void set_type(AttributeProto_AttributeType value) { return g_host->AttributeProto__set_type(this, value); }
|
||||
|
|
@ -118,6 +119,7 @@ struct GraphProto final {
|
|||
ValueInfoProtos* mutable_value_info() { return g_host->GraphProto__mutable_value_info(this); }
|
||||
TensorProtos* mutable_initializer() { return g_host->GraphProto__mutable_initializer(this); }
|
||||
NodeProto* add_node() { return g_host->GraphProto__add_node(this); }
|
||||
NodeProto* mutable_node(int index) { return g_host->GraphProto__mutable_node(this, index); }
|
||||
|
||||
GraphProto() = delete;
|
||||
GraphProto(const GraphProto&) = delete;
|
||||
|
|
@ -148,6 +150,7 @@ struct NodeProto final {
|
|||
void operator=(const NodeProto& v) { g_host->NodeProto__operator_assign(this, v); }
|
||||
int attribute_size() { return g_host->NodeProto__attribute_size(this); }
|
||||
const AttributeProto& attribute(int index) const { return g_host->NodeProto__attribute(this, index); }
|
||||
AttributeProto* mutable_attribute(int index) { return g_host->NodeProto__mutable_attribute(this, index); }
|
||||
|
||||
NodeProto() = delete;
|
||||
NodeProto(const NodeProto&) = delete;
|
||||
|
|
|
|||
229
onnxruntime/core/providers/tensorrt/onnx_ctx_model_helper.cc
Normal file
229
onnxruntime/core/providers/tensorrt/onnx_ctx_model_helper.cc
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "onnx_ctx_model_helper.h"
|
||||
#include "core/providers/cuda/shared_inc/cuda_call.h"
|
||||
#include "core/framework/execution_provider.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
/*
|
||||
* Check whether the graph has the EP context contrib op.
|
||||
* The op can contain the precompiled engine info for TRT EP to directly load the engine.
|
||||
*
|
||||
* Note: Please see more details about "EPContext" contrib op in contrib_defs.cc
|
||||
*/
|
||||
bool GraphHasCtxNode(const GraphViewer& graph_viewer) {
|
||||
for (int i = 0; i < graph_viewer.MaxNodeIndex(); ++i) {
|
||||
auto node = graph_viewer.GetNode(i);
|
||||
if (node != nullptr && node->OpType() == EPCONTEXT_OP) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const onnxruntime::Path& GetModelPath(const GraphViewer& graph_viewer) {
|
||||
// find the top level graph
|
||||
const Graph* cur_graph = &graph_viewer.GetGraph();
|
||||
while (cur_graph->IsSubgraph()) {
|
||||
cur_graph = cur_graph->ParentGraph();
|
||||
}
|
||||
|
||||
const Graph& main_graph = *cur_graph;
|
||||
return main_graph.ModelPath();
|
||||
}
|
||||
|
||||
std::filesystem::path LocateEngineRelativeToPath(std::string engine_cache_path, const onnxruntime::Path& path) {
|
||||
std::filesystem::path base_path(path.ToPathString());
|
||||
std::filesystem::path parent_path = base_path.parent_path();
|
||||
std::filesystem::path engine_path = parent_path.append(engine_cache_path);
|
||||
return engine_path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update ep_cache_context attribute of the EP context node with the given engine binary data
|
||||
*/
|
||||
void UpdateCtxNodeModelEngineContext(ONNX_NAMESPACE::ModelProto* model_proto,
|
||||
char* engine_data,
|
||||
size_t size) {
|
||||
ONNX_NAMESPACE::GraphProto* graph_proto = model_proto->mutable_graph();
|
||||
ONNX_NAMESPACE::NodeProto* node_proto = graph_proto->mutable_node(0);
|
||||
|
||||
for (int i = 0; i < node_proto->attribute_size(); ++i) {
|
||||
ONNX_NAMESPACE::AttributeProto* attribute_proto = node_proto->mutable_attribute(i);
|
||||
if (attribute_proto->name() == EP_CACHE_CONTEXT) {
|
||||
std::string engine_data_str = "";
|
||||
if (size > 0) {
|
||||
engine_data_str.assign(engine_data, size);
|
||||
}
|
||||
attribute_proto->set_s(engine_data_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create "EP context node" model where engine information is embedded
|
||||
*/
|
||||
ONNX_NAMESPACE::ModelProto* CreateCtxNodeModel(const GraphViewer& graph_viewer,
|
||||
const std::string engine_cache_path,
|
||||
char* engine_data,
|
||||
size_t size,
|
||||
const int64_t embed_mode,
|
||||
bool compute_capability_enable,
|
||||
std::string compute_capability,
|
||||
const logging::Logger* logger) {
|
||||
auto model_build = graph_viewer.CreateModel(*logger);
|
||||
auto& graph_build = model_build->MainGraph();
|
||||
|
||||
// Get graph inputs and outputs
|
||||
std::vector<onnxruntime::NodeArg*> inputs, outputs;
|
||||
for (auto input : graph_viewer.GetInputs()) {
|
||||
auto& n_input = graph_build.GetOrCreateNodeArg(input->Name(), input->TypeAsProto());
|
||||
inputs.push_back(&n_input);
|
||||
}
|
||||
|
||||
for (auto output : graph_viewer.GetOutputs()) {
|
||||
auto& n_output = graph_build.GetOrCreateNodeArg(output->Name(), output->TypeAsProto());
|
||||
outputs.push_back(&n_output);
|
||||
}
|
||||
|
||||
// Create EP context node attributes
|
||||
auto attr_0 = ONNX_NAMESPACE::AttributeProto::Create(); // embed_mode
|
||||
auto attr_1 = ONNX_NAMESPACE::AttributeProto::Create(); // ep_cache_context
|
||||
auto attr_2 = ONNX_NAMESPACE::AttributeProto::Create(); // hardware_architecture
|
||||
std::string engine_data_str = "";
|
||||
attr_0->set_name(EMBED_MODE);
|
||||
attr_0->set_type(onnx::AttributeProto_AttributeType_INT);
|
||||
attr_0->set_i(embed_mode);
|
||||
attr_1->set_name(EP_CACHE_CONTEXT);
|
||||
attr_1->set_type(onnx::AttributeProto_AttributeType_STRING);
|
||||
if (embed_mode) {
|
||||
if (size > 0) {
|
||||
engine_data_str.assign(engine_data, size);
|
||||
}
|
||||
attr_1->set_s(engine_data_str);
|
||||
} else {
|
||||
attr_1->set_s(engine_cache_path);
|
||||
}
|
||||
auto node_attributes = ONNX_NAMESPACE::NodeAttributes::Create();
|
||||
int num_attributes = compute_capability_enable ? 3 : 2;
|
||||
node_attributes->reserve(num_attributes);
|
||||
node_attributes->emplace(EMBED_MODE, *attr_0);
|
||||
node_attributes->emplace(EP_CACHE_CONTEXT, *attr_1);
|
||||
|
||||
if (compute_capability_enable) {
|
||||
attr_2->set_name(COMPUTE_CAPABILITY);
|
||||
attr_2->set_type(onnx::AttributeProto_AttributeType_STRING);
|
||||
attr_2->set_s(compute_capability);
|
||||
node_attributes->emplace(COMPUTE_CAPABILITY, *attr_2);
|
||||
}
|
||||
|
||||
// Create EP context node
|
||||
graph_build.AddNode(EPCONTEXT_OP, EPCONTEXT_OP, "", inputs, outputs, node_attributes.get(), EPCONTEXT_OP_DOMAIN);
|
||||
ORT_ENFORCE(graph_build.Resolve().IsOK());
|
||||
|
||||
// Serialize modelproto to string
|
||||
auto new_graph_viewer = graph_build.CreateGraphViewer();
|
||||
auto model = new_graph_viewer->CreateModel(*logger);
|
||||
auto model_proto = model->ToProto();
|
||||
new_graph_viewer->ToProto(*model_proto->mutable_graph(), true, true);
|
||||
model_proto->set_ir_version(ONNX_NAMESPACE::Version::IR_VERSION);
|
||||
|
||||
return model_proto.release();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump "EP context node" model
|
||||
*
|
||||
*/
|
||||
void DumpCtxNodeModel(ONNX_NAMESPACE::ModelProto* model_proto,
|
||||
const std::string engine_cache_path) {
|
||||
std::fstream dump(engine_cache_path + "_wrapper.onnx", std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
model_proto->SerializeToOstream(dump);
|
||||
LOGS_DEFAULT(VERBOSE) << "[TensorRT EP] Serialized " + engine_cache_path + "_wrapper.onnx";
|
||||
}
|
||||
|
||||
Status TensorRTCacheModelHandler::GetEpContextFromGraph(const GraphViewer& graph_viewer) {
|
||||
if (!ValidateEPCtxNode(graph_viewer)) {
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, EP_FAIL, "It's not a valid EP Context node");
|
||||
}
|
||||
auto node = graph_viewer.GetNode(0);
|
||||
auto& attrs = node->GetAttributes();
|
||||
|
||||
const int64_t embed_mode = attrs.at(EMBED_MODE).i();
|
||||
if (embed_mode) {
|
||||
// Get engine from byte stream
|
||||
const std::string& context_binary = attrs.at(EP_CACHE_CONTEXT).s();
|
||||
*(trt_engine_) = std::unique_ptr<nvinfer1::ICudaEngine>(trt_runtime_->deserializeCudaEngine(const_cast<char*>(context_binary.c_str()),
|
||||
static_cast<size_t>(context_binary.length())));
|
||||
LOGS_DEFAULT(VERBOSE) << "[TensorRT EP] Read engine as binary data from \"ep_cache_context\" attribute of ep context node and deserialized it";
|
||||
if (!(*trt_engine_)) {
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, EP_FAIL,
|
||||
"TensorRT EP could not deserialize engine from binary data");
|
||||
}
|
||||
} else {
|
||||
// Get engine from cache file
|
||||
std::ifstream engine_file(engine_cache_path_.string(), std::ios::binary | std::ios::in);
|
||||
engine_file.seekg(0, std::ios::end);
|
||||
size_t engine_size = engine_file.tellg();
|
||||
engine_file.seekg(0, std::ios::beg);
|
||||
std::unique_ptr<char[]> engine_buf{new char[engine_size]};
|
||||
engine_file.read((char*)engine_buf.get(), engine_size);
|
||||
*(trt_engine_) = std::unique_ptr<nvinfer1::ICudaEngine>(trt_runtime_->deserializeCudaEngine(engine_buf.get(), engine_size));
|
||||
LOGS_DEFAULT(VERBOSE) << "[TensorRT EP] DeSerialized " + engine_cache_path_.string();
|
||||
if (!(*trt_engine_)) {
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, EP_FAIL,
|
||||
"TensorRT EP could not deserialize engine from cache: " + engine_cache_path_.string());
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
/*
|
||||
* The sanity check for EP context contrib op.
|
||||
*/
|
||||
bool TensorRTCacheModelHandler::ValidateEPCtxNode(const GraphViewer& graph_viewer) {
|
||||
assert(graph_viewer.NumberOfNodes() == 1);
|
||||
assert(graph_viewer.GetNode(0)->OpType() == EPCONTEXT_OP);
|
||||
auto node = graph_viewer.GetNode(0);
|
||||
auto& attrs = node->GetAttributes();
|
||||
|
||||
// Check hardware_architecture(compute_capability) if it's present as an attribute
|
||||
if (attrs.count(COMPUTE_CAPABILITY) > 0) {
|
||||
std::string model_compute_capability = attrs.at(COMPUTE_CAPABILITY).s();
|
||||
if (model_compute_capability != compute_capability_) {
|
||||
LOGS_DEFAULT(ERROR) << "The compute capability of the engine cache doesn't match with the GPU's compute capability";
|
||||
LOGS_DEFAULT(ERROR) << "The compute capability of the engine cache: " << model_compute_capability;
|
||||
LOGS_DEFAULT(ERROR) << "The compute capability of the GPU: " << compute_capability_;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// "embed_mode" attr and "ep_cache_context" attr should be present
|
||||
if (attrs.count(EMBED_MODE) > 0 && attrs.count(EP_CACHE_CONTEXT) > 0) {
|
||||
// ep_cache_context: payload of the execution provider context if embed_mode=1, or path to the context file if embed_mode=0
|
||||
const int64_t embed_mode = attrs.at(EMBED_MODE).i();
|
||||
|
||||
// engine cache path
|
||||
if (embed_mode == 0) {
|
||||
// First assume engine cache path is relatvie to model path,
|
||||
// If not, then assume the engine cache path is an absolute path.
|
||||
engine_cache_path_ = LocateEngineRelativeToPath(attrs.at(EP_CACHE_CONTEXT).s(), GetModelPath(graph_viewer));
|
||||
auto default_engine_cache_path_ = engine_cache_path_;
|
||||
if (!std::filesystem::exists(engine_cache_path_)) {
|
||||
engine_cache_path_.assign(attrs.at(EP_CACHE_CONTEXT).s());
|
||||
if (!std::filesystem::exists(engine_cache_path_)) {
|
||||
LOGS_DEFAULT(ERROR) << "Can't find " << default_engine_cache_path_.string() << " or " << engine_cache_path_.string() << " TensorRT engine";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace onnxruntime
|
||||
55
onnxruntime/core/providers/tensorrt/onnx_ctx_model_helper.h
Normal file
55
onnxruntime/core/providers/tensorrt/onnx_ctx_model_helper.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
#include "NvInfer.h"
|
||||
#include "core/providers/shared_library/provider_api.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
|
||||
static const std::string EPCONTEXT_OP = "EPContext";
|
||||
static const std::string EMBED_MODE = "embed_mode";
|
||||
static const std::string EP_CACHE_CONTEXT = "ep_cache_context";
|
||||
static const std::string COMPUTE_CAPABILITY = "hardware_architecture";
|
||||
static const std::string EPCONTEXT_OP_DOMAIN = "com.microsoft";
|
||||
|
||||
bool GraphHasCtxNode(const GraphViewer& graph_viewer);
|
||||
const onnxruntime::Path& GetModelPath(const GraphViewer& graph_viewer);
|
||||
std::filesystem::path LocateEngineRelativeToPath(std::string engine_cache_path, const onnxruntime::Path& path);
|
||||
ONNX_NAMESPACE::ModelProto* CreateCtxNodeModel(const GraphViewer& graph_viewer,
|
||||
const std::string engine_cache_path,
|
||||
char* engine_data,
|
||||
size_t size,
|
||||
const int64_t embed_mode,
|
||||
bool compute_capability_enable,
|
||||
std::string compute_capability,
|
||||
const logging::Logger* logger);
|
||||
void DumpCtxNodeModel(ONNX_NAMESPACE::ModelProto* model_proto,
|
||||
const std::string engine_cache_path);
|
||||
void UpdateCtxNodeModelEngineContext(ONNX_NAMESPACE::ModelProto* model_proto,
|
||||
char* engine_data,
|
||||
size_t size);
|
||||
|
||||
class TensorRTCacheModelHandler {
|
||||
public:
|
||||
TensorRTCacheModelHandler(std::unique_ptr<nvinfer1::ICudaEngine>* trt_engine,
|
||||
nvinfer1::IRuntime* trt_runtime,
|
||||
std::string compute_capability) : trt_engine_(trt_engine), trt_runtime_(trt_runtime), compute_capability_(compute_capability) {
|
||||
}
|
||||
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(TensorRTCacheModelHandler);
|
||||
|
||||
bool ValidateEPCtxNode(const GraphViewer& graph_viewer);
|
||||
|
||||
Status GetEpContextFromGraph(const GraphViewer& graph_viewer);
|
||||
|
||||
private:
|
||||
std::unique_ptr<nvinfer1::ICudaEngine>* trt_engine_;
|
||||
nvinfer1::IRuntime* trt_runtime_;
|
||||
std::filesystem::path engine_cache_path_;
|
||||
std::string compute_capability_;
|
||||
}; // TRTCacheModelHandler
|
||||
} // namespace onnxruntime
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -46,6 +46,9 @@ static const std::string kProfilesMinShapes = "ORT_TENSORRT_PROFILE_MIN_SHAPES";
|
|||
static const std::string kProfilesMaxShapes = "ORT_TENSORRT_PROFILE_MAX_SHAPES";
|
||||
static const std::string kProfilesOptShapes = "ORT_TENSORRT_PROFILE_OPT_SHAPES";
|
||||
static const std::string kCudaGraphEnable = "ORT_TENSORRT_CUDA_GRAPH_ENABLE";
|
||||
static const std::string kDumpEpContextModel = "ORT_DUMP_EP_CONTEXT_MODEL";
|
||||
static const std::string kEpContextEmbedMode = "ORT_EP_CONTEXT_EMBED_MODE";
|
||||
static const std::string kEpContextComputeCapabilityEnable = "ORT_EP_CONTEXT_COMPUTE_CAPABILITY_ENABLE";
|
||||
// Old env variable for backward compatibility
|
||||
static const std::string kEngineCachePath = "ORT_TENSORRT_ENGINE_CACHE_PATH";
|
||||
} // namespace tensorrt_env_vars
|
||||
|
|
@ -177,6 +180,22 @@ struct TensorrtFuncState {
|
|||
bool cuda_graph_enable = 0;
|
||||
};
|
||||
|
||||
// Minimum information to construct kernel function state for direct engine load code path
|
||||
struct TensorrtShortFuncState {
|
||||
AllocateFunc test_allocate_func = nullptr;
|
||||
DestroyFunc test_release_func = nullptr;
|
||||
AllocatorHandle allocator = nullptr;
|
||||
std::string fused_node_name;
|
||||
std::unique_ptr<nvinfer1::ICudaEngine>* engine = nullptr;
|
||||
std::unique_ptr<nvinfer1::IExecutionContext>* context = nullptr;
|
||||
std::vector<std::unordered_map<std::string, size_t>> input_info;
|
||||
std::vector<std::unordered_map<std::string, size_t>> output_info;
|
||||
bool sync_stream_after_enqueue = false;
|
||||
bool context_memory_sharing_enable = false;
|
||||
size_t* max_context_mem_size_ptr = nullptr;
|
||||
OrtMutex* tensorrt_mu_ptr = nullptr;
|
||||
};
|
||||
|
||||
// Holds important information for building valid ORT graph.
|
||||
struct SubGraphContext {
|
||||
std::unordered_set<std::string> output_args;
|
||||
|
|
@ -276,6 +295,12 @@ class TensorrtExecutionProvider : public IExecutionProvider {
|
|||
// and should be kept for the lifetime of TRT EP object.
|
||||
OrtAllocator* alloc_ = nullptr;
|
||||
|
||||
// For create/dump EP context node model
|
||||
bool dump_ep_context_model_ = false;
|
||||
int ep_context_embed_mode_ = 0;
|
||||
bool ep_context_compute_capability_enable_ = true;
|
||||
std::unique_ptr<ONNX_NAMESPACE::ModelProto> model_proto_ = ONNX_NAMESPACE::ModelProto::Create();
|
||||
|
||||
std::unordered_set<std::string> control_flow_op_set_ = {"If", "Loop", "Scan"};
|
||||
mutable std::unordered_map<std::string, std::unique_ptr<SubGraphContext>> subgraph_context_map_;
|
||||
|
||||
|
|
@ -489,6 +514,25 @@ class TensorrtExecutionProvider : public IExecutionProvider {
|
|||
*/
|
||||
bool IsLocalValue(const Graph& graph, const std::string& name) const;
|
||||
|
||||
/**
|
||||
* Create a vector of NodeComputeInfo instances directly from "TRT engine" wrapped onnx model without
|
||||
* going through the time-consuming processes of model parsing and engine building.
|
||||
*/
|
||||
Status CreateNodeComputeInfoFromPrecompiledEngine(const GraphViewer& graph_body_viewer,
|
||||
const Node& fused_node,
|
||||
std::unordered_map<std::string, size_t>& input_map,
|
||||
std::unordered_map<std::string, size_t>& output_map,
|
||||
std::vector<NodeComputeInfo>& node_compute_funcs);
|
||||
|
||||
/**
|
||||
* Create a vector of NodeComputeInfo instances from graph.
|
||||
*/
|
||||
Status CreateNodeComputeInfoFromGraph(const GraphViewer& graph_body_viewer,
|
||||
const Node& fused_node,
|
||||
std::unordered_map<std::string, size_t>& input_map,
|
||||
std::unordered_map<std::string, size_t>& output_map,
|
||||
std::vector<NodeComputeInfo>& node_compute_funcs);
|
||||
|
||||
bool IsGraphCaptureAllowed() const;
|
||||
void CaptureBegin();
|
||||
void CaptureEnd();
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ constexpr const char* kProfilesMinShapes = "trt_profile_min_shapes";
|
|||
constexpr const char* kProfilesMaxShapes = "trt_profile_max_shapes";
|
||||
constexpr const char* kProfilesOptShapes = "trt_profile_opt_shapes";
|
||||
constexpr const char* kCudaGraphEnable = "trt_cuda_graph_enable";
|
||||
constexpr const char* kDumpEpContextModel = "trt_dump_ep_context_model";
|
||||
constexpr const char* kEpContextEmbedMode = "trt_ep_context_embed_mode";
|
||||
constexpr const char* kEpContextComputeCapabilityEnable = "trt_ep_context_compute_capability_enable";
|
||||
} // namespace provider_option_names
|
||||
} // namespace tensorrt
|
||||
|
||||
|
|
@ -97,6 +100,9 @@ TensorrtExecutionProviderInfo TensorrtExecutionProviderInfo::FromProviderOptions
|
|||
.AddAssignmentToReference(tensorrt::provider_option_names::kProfilesMaxShapes, info.profile_max_shapes)
|
||||
.AddAssignmentToReference(tensorrt::provider_option_names::kProfilesOptShapes, info.profile_opt_shapes)
|
||||
.AddAssignmentToReference(tensorrt::provider_option_names::kCudaGraphEnable, info.cuda_graph_enable)
|
||||
.AddAssignmentToReference(tensorrt::provider_option_names::kDumpEpContextModel, info.dump_ep_context_model)
|
||||
.AddAssignmentToReference(tensorrt::provider_option_names::kEpContextEmbedMode, info.ep_context_embed_mode)
|
||||
.AddAssignmentToReference(tensorrt::provider_option_names::kEpContextComputeCapabilityEnable, info.ep_context_compute_capability_enable)
|
||||
.Parse(options)); // add new provider option here.
|
||||
|
||||
return info;
|
||||
|
|
@ -138,6 +144,9 @@ ProviderOptions TensorrtExecutionProviderInfo::ToProviderOptions(const TensorrtE
|
|||
{tensorrt::provider_option_names::kProfilesMaxShapes, MakeStringWithClassicLocale(info.profile_max_shapes)},
|
||||
{tensorrt::provider_option_names::kProfilesOptShapes, MakeStringWithClassicLocale(info.profile_opt_shapes)},
|
||||
{tensorrt::provider_option_names::kCudaGraphEnable, MakeStringWithClassicLocale(info.cuda_graph_enable)},
|
||||
{tensorrt::provider_option_names::kDumpEpContextModel, MakeStringWithClassicLocale(info.dump_ep_context_model)},
|
||||
{tensorrt::provider_option_names::kEpContextEmbedMode, MakeStringWithClassicLocale(info.ep_context_embed_mode)},
|
||||
{tensorrt::provider_option_names::kEpContextComputeCapabilityEnable, MakeStringWithClassicLocale(info.ep_context_compute_capability_enable)},
|
||||
};
|
||||
return options;
|
||||
}
|
||||
|
|
@ -188,6 +197,9 @@ ProviderOptions TensorrtExecutionProviderInfo::ToProviderOptions(const OrtTensor
|
|||
{tensorrt::provider_option_names::kProfilesMaxShapes, kProfilesMaxShapes_},
|
||||
{tensorrt::provider_option_names::kProfilesOptShapes, kProfilesOptShapes_},
|
||||
{tensorrt::provider_option_names::kCudaGraphEnable, MakeStringWithClassicLocale(info.trt_cuda_graph_enable)},
|
||||
{tensorrt::provider_option_names::kDumpEpContextModel, MakeStringWithClassicLocale(info.trt_dump_ep_context_model)},
|
||||
{tensorrt::provider_option_names::kEpContextEmbedMode, MakeStringWithClassicLocale(info.trt_ep_context_embed_mode)},
|
||||
{tensorrt::provider_option_names::kEpContextComputeCapabilityEnable, MakeStringWithClassicLocale(info.trt_ep_context_compute_capability_enable)},
|
||||
};
|
||||
return options;
|
||||
}
|
||||
|
|
@ -279,5 +291,8 @@ void TensorrtExecutionProviderInfo::UpdateProviderOptions(void* provider_options
|
|||
trt_provider_options_v2.trt_profile_opt_shapes = copy_string_if_needed(internal_options.profile_opt_shapes);
|
||||
|
||||
trt_provider_options_v2.trt_cuda_graph_enable = internal_options.cuda_graph_enable;
|
||||
trt_provider_options_v2.trt_dump_ep_context_model = internal_options.dump_ep_context_model;
|
||||
trt_provider_options_v2.trt_ep_context_embed_mode = internal_options.ep_context_embed_mode;
|
||||
trt_provider_options_v2.trt_ep_context_compute_capability_enable = internal_options.ep_context_compute_capability_enable;
|
||||
}
|
||||
} // namespace onnxruntime
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ struct TensorrtExecutionProviderInfo {
|
|||
std::string profile_max_shapes{""};
|
||||
std::string profile_opt_shapes{""};
|
||||
bool cuda_graph_enable{false};
|
||||
bool dump_ep_context_model{false};
|
||||
int ep_context_embed_mode{0};
|
||||
bool ep_context_compute_capability_enable{1};
|
||||
|
||||
static TensorrtExecutionProviderInfo FromProviderOptions(const ProviderOptions& options);
|
||||
static ProviderOptions ToProviderOptions(const TensorrtExecutionProviderInfo& info);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <experimental/filesystem>
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "ort_trt_int8_cal_table.fbs.h"
|
||||
|
|
|
|||
|
|
@ -116,6 +116,9 @@ struct Tensorrt_Provider : Provider {
|
|||
info.profile_max_shapes = options.trt_profile_max_shapes == nullptr ? "" : options.trt_profile_max_shapes;
|
||||
info.profile_opt_shapes = options.trt_profile_opt_shapes == nullptr ? "" : options.trt_profile_opt_shapes;
|
||||
info.cuda_graph_enable = options.trt_cuda_graph_enable != 0;
|
||||
info.dump_ep_context_model = options.trt_dump_ep_context_model != 0;
|
||||
info.ep_context_embed_mode = options.trt_ep_context_embed_mode;
|
||||
info.ep_context_compute_capability_enable = options.trt_ep_context_compute_capability_enable != 0;
|
||||
|
||||
return std::make_shared<TensorrtProviderFactory>(info);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -427,6 +427,7 @@ struct ProviderHostImpl : ProviderHost {
|
|||
int64_t AttributeProto__i(const ONNX_NAMESPACE::AttributeProto* p) override { return p->i(); }
|
||||
float AttributeProto__f(const ONNX_NAMESPACE::AttributeProto* p) override { return p->f(); }
|
||||
void AttributeProto__set_s(ONNX_NAMESPACE::AttributeProto* p, const ::std::string& value) override { return p->set_s(value); }
|
||||
void AttributeProto__set_i(ONNX_NAMESPACE::AttributeProto* p, int64_t value) override { return p->set_i(value); }
|
||||
const ::std::string& AttributeProto__s(const ONNX_NAMESPACE::AttributeProto* p) override { return p->s(); }
|
||||
void AttributeProto__set_name(ONNX_NAMESPACE::AttributeProto* p, const ::std::string& value) override { return p->set_name(value); }
|
||||
void AttributeProto__set_type(ONNX_NAMESPACE::AttributeProto* p, ONNX_NAMESPACE::AttributeProto_AttributeType value) override { return p->set_type(value); }
|
||||
|
|
@ -447,6 +448,7 @@ struct ProviderHostImpl : ProviderHost {
|
|||
ONNX_NAMESPACE::ValueInfoProtos* GraphProto__mutable_value_info(ONNX_NAMESPACE::GraphProto* p) override { return p->mutable_value_info(); }
|
||||
ONNX_NAMESPACE::TensorProtos* GraphProto__mutable_initializer(ONNX_NAMESPACE::GraphProto* p) override { return p->mutable_initializer(); }
|
||||
ONNX_NAMESPACE::NodeProto* GraphProto__add_node(ONNX_NAMESPACE::GraphProto* p) override { return p->add_node(); }
|
||||
ONNX_NAMESPACE::NodeProto* GraphProto__mutable_node(ONNX_NAMESPACE::GraphProto* p, int index) override { return p->mutable_node(index); }
|
||||
|
||||
void GraphProto__operator_assign(ONNX_NAMESPACE::GraphProto* p, const ONNX_NAMESPACE::GraphProto& v) override { *p = v; }
|
||||
|
||||
|
|
@ -470,6 +472,7 @@ struct ProviderHostImpl : ProviderHost {
|
|||
void NodeProto__operator_assign(ONNX_NAMESPACE::NodeProto* p, const ONNX_NAMESPACE::NodeProto& v) override { *p = v; }
|
||||
int NodeProto__attribute_size(ONNX_NAMESPACE::NodeProto* p) override { return p->attribute_size(); }
|
||||
const ONNX_NAMESPACE::AttributeProto& NodeProto__attribute(const ONNX_NAMESPACE::NodeProto* p, int index) const override { return p->attribute(index); }
|
||||
ONNX_NAMESPACE::AttributeProto* NodeProto__mutable_attribute(ONNX_NAMESPACE::NodeProto* p, int index) override { return p->mutable_attribute(index); }
|
||||
|
||||
// TensorProto (wrapped)
|
||||
std::unique_ptr<ONNX_NAMESPACE::TensorProto> TensorProto__construct() override { return std::make_unique<ONNX_NAMESPACE::TensorProto>(); }
|
||||
|
|
|
|||
|
|
@ -713,6 +713,28 @@ std::unique_ptr<IExecutionProvider> CreateExecutionProviderInstance(
|
|||
} else {
|
||||
ORT_THROW("[ERROR] [TensorRT] The value for the key 'trt_cuda_graph_enable' should be 'True' or 'False'. Default value is 'False'.\n");
|
||||
}
|
||||
} else if (option.first == "trt_dump_ep_context_model") {
|
||||
if (option.second == "True" || option.second == "true") {
|
||||
params.trt_dump_ep_context_model = true;
|
||||
} else if (option.second == "False" || option.second == "false") {
|
||||
params.trt_dump_ep_context_model = false;
|
||||
} else {
|
||||
ORT_THROW("[ERROR] [TensorRT] The value for the key 'trt_dump_ep_context_model' should be 'True' or 'False'. Default value is 'False'.\n");
|
||||
}
|
||||
} else if (option.first == "trt_ep_context_embed_mode") {
|
||||
if (!option.second.empty()) {
|
||||
params.trt_ep_context_embed_mode = std::stoi(option.second);
|
||||
} else {
|
||||
ORT_THROW("[ERROR] [TensorRT] The value for the key 'trt_ep_context_embed_mode' should be a positive integer number i.e. '1'.\n");
|
||||
}
|
||||
} else if (option.first == "trt_ep_context_compute_capability_enable") {
|
||||
if (option.second == "True" || option.second == "true") {
|
||||
params.trt_ep_context_compute_capability_enable = true;
|
||||
} else if (option.second == "False" || option.second == "false") {
|
||||
params.trt_ep_context_compute_capability_enable = false;
|
||||
} else {
|
||||
ORT_THROW("[ERROR] [TensorRT] The value for the key 'trt_ep_context_compute_capability_enable' should be 'True' or 'False'. Default value is 'False'.\n");
|
||||
}
|
||||
} else {
|
||||
ORT_THROW("Invalid TensorRT EP option: ", option.first);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
import onnx
|
||||
import tensorrt as trt
|
||||
from onnx import TensorProto, helper
|
||||
|
||||
|
||||
class TensorRTEngineWrapperCreator:
|
||||
def __init__(self, args):
|
||||
ctx_embed_mode = args.embed_mode
|
||||
engine_cache_path = args.trt_engine_cache_path
|
||||
self.model_name = args.model_name
|
||||
self.dynamic_dim_count = 0
|
||||
|
||||
# Get serialized engine from engine cache
|
||||
with open(engine_cache_path, "rb") as file:
|
||||
engine_buffer = file.read()
|
||||
|
||||
if ctx_embed_mode:
|
||||
ep_cache_context_content = engine_buffer
|
||||
else:
|
||||
ep_cache_context_content = engine_cache_path
|
||||
|
||||
# Deserialize an TRT engine
|
||||
logger = trt.Logger(trt.Logger.WARNING)
|
||||
runtime = trt.Runtime(logger)
|
||||
engine = runtime.deserialize_cuda_engine(engine_buffer)
|
||||
num_bindings = engine.num_bindings
|
||||
|
||||
input_tensors = []
|
||||
output_tensors = []
|
||||
input_tensor_shapes = []
|
||||
output_tensor_shapes = []
|
||||
input_tensor_types = []
|
||||
output_tensor_types = []
|
||||
|
||||
# Get type and shape of each input/output
|
||||
for b_index in range(num_bindings):
|
||||
tensor_name = engine.get_tensor_name(b_index)
|
||||
tensor_shape = engine.get_tensor_shape(tensor_name)
|
||||
tensor_type = engine.get_tensor_dtype(tensor_name)
|
||||
if engine.get_tensor_mode(tensor_name) == trt.TensorIOMode.INPUT:
|
||||
input_tensors.append(tensor_name)
|
||||
input_tensor_shapes.append(tensor_shape)
|
||||
input_tensor_types.append(tensor_type)
|
||||
else:
|
||||
output_tensors.append(tensor_name)
|
||||
output_tensor_shapes.append(tensor_shape)
|
||||
output_tensor_types.append(tensor_type)
|
||||
|
||||
# Note:
|
||||
# The TRT engine should be built with min, max and opt profiles so that dynamic shape input can have dimension of "-1"
|
||||
print(input_tensors)
|
||||
print(input_tensor_types)
|
||||
print(input_tensor_shapes)
|
||||
print(output_tensors)
|
||||
print(output_tensor_types)
|
||||
print(output_tensor_shapes)
|
||||
|
||||
nodes = [
|
||||
helper.make_node(
|
||||
"EPContext",
|
||||
input_tensors,
|
||||
output_tensors,
|
||||
"EPContext",
|
||||
domain="com.microsoft",
|
||||
embed_mode=ctx_embed_mode,
|
||||
ep_cache_context=ep_cache_context_content,
|
||||
),
|
||||
]
|
||||
|
||||
model_inputs = []
|
||||
for i in range(len(input_tensors)):
|
||||
model_inputs.append(
|
||||
helper.make_tensor_value_info(
|
||||
input_tensors[i],
|
||||
self.trt_data_type_to_onnx_data_type(input_tensor_types[i]),
|
||||
self.trt_shape_to_ort_shape(input_tensor_shapes[i]),
|
||||
)
|
||||
)
|
||||
|
||||
model_outputs = []
|
||||
for i in range(len(output_tensors)):
|
||||
model_outputs.append(
|
||||
helper.make_tensor_value_info(
|
||||
output_tensors[i],
|
||||
self.trt_data_type_to_onnx_data_type(output_tensor_types[i]),
|
||||
self.trt_shape_to_ort_shape(output_tensor_shapes[i]),
|
||||
)
|
||||
)
|
||||
|
||||
self.graph = helper.make_graph(
|
||||
nodes,
|
||||
"trt_engine_wrapper",
|
||||
model_inputs,
|
||||
model_outputs,
|
||||
)
|
||||
|
||||
def trt_data_type_to_onnx_data_type(self, trt_data_type):
|
||||
if trt_data_type == trt.DataType.FLOAT:
|
||||
return TensorProto.FLOAT
|
||||
elif trt_data_type == trt.DataType.HALF:
|
||||
return TensorProto.FLOAT16
|
||||
elif trt_data_type == trt.DataType.INT8:
|
||||
return TensorProto.INT8
|
||||
elif trt_data_type == trt.DataType.INT32:
|
||||
return TensorProto.INT32
|
||||
elif trt_data_type == trt.DataType.BOOL:
|
||||
return TensorProto.BOOL
|
||||
elif trt_data_type == trt.DataType.UINT8:
|
||||
return TensorProto.UINT8
|
||||
else:
|
||||
return TensorProto.UNDEFINED
|
||||
|
||||
# TRT uses "-1" to represent dynamic dimension
|
||||
# ORT uses symbolic name to represent dynamic dimension
|
||||
# Here we only do the conversion when there is any dynamic dimension in the shape
|
||||
def trt_shape_to_ort_shape(self, trt_data_shape):
|
||||
def has_dynamic_dim(trt_data_shape):
|
||||
if any(dim == -1 for dim in trt_data_shape):
|
||||
return True
|
||||
return False
|
||||
|
||||
if not has_dynamic_dim(trt_data_shape):
|
||||
return trt_data_shape
|
||||
|
||||
ort_data_shape = []
|
||||
if has_dynamic_dim(trt_data_shape):
|
||||
for dim in trt_data_shape:
|
||||
if dim == -1:
|
||||
ort_data_shape.append("free_dim_" + str(self.dynamic_dim_count))
|
||||
self.dynamic_dim_count += 1
|
||||
else:
|
||||
ort_data_shape.append(dim)
|
||||
return ort_data_shape
|
||||
|
||||
def create_model(self):
|
||||
model = helper.make_model(self.graph)
|
||||
onnx.save(model, self.model_name)
|
||||
print(self.model_name + " is created.")
|
||||
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser("Generate Onnx model which includes the TensorRT engine binary.")
|
||||
parser.add_argument(
|
||||
"-p", "--trt_engine_cache_path", help="Required. Path to TensorRT engine cache.", required=True, type=str
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
"--embed_mode",
|
||||
help="mode 0 means the engine cache path and mode 1 means engine binary data",
|
||||
required=False,
|
||||
default=0,
|
||||
type=int,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--model_name",
|
||||
help="Model name to be created",
|
||||
required=False,
|
||||
default="trt_engine_wrapper.onnx",
|
||||
type=str,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
ctor = TensorRTEngineWrapperCreator(args)
|
||||
ctor.create_model()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
100
onnxruntime/test/python/onnxruntime_test_engine_wrapper.py
Normal file
100
onnxruntime/test/python/onnxruntime_test_engine_wrapper.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
import onnx
|
||||
from helper import get_name
|
||||
from onnx import TensorProto, helper
|
||||
|
||||
import onnxruntime as onnxrt
|
||||
|
||||
|
||||
class TestInferenceSessionWithCtxNode(unittest.TestCase):
|
||||
trt_engine_cache_path_ = "./trt_engine_cache"
|
||||
ctx_node_model_name_ = "ctx_node.onnx"
|
||||
|
||||
# This test is only for TRT EP to test EPContext node with TRT engine
|
||||
@unittest.skipIf(
|
||||
"TensorrtExecutionProvider" not in onnxrt.get_available_providers(),
|
||||
reason="Test TRT EP only",
|
||||
)
|
||||
def create_ctx_node(self, ctx_embed_mode=0, cache_path=""):
|
||||
if ctx_embed_mode:
|
||||
# Get engine buffer from engine cache
|
||||
with open(cache_path, "rb") as file:
|
||||
engine_buffer = file.read()
|
||||
ep_cache_context_content = engine_buffer
|
||||
else:
|
||||
ep_cache_context_content = cache_path
|
||||
|
||||
nodes = [
|
||||
helper.make_node(
|
||||
"EPContext",
|
||||
["X"],
|
||||
["Y"],
|
||||
"EPContext",
|
||||
domain="com.microsoft",
|
||||
embed_mode=ctx_embed_mode,
|
||||
ep_cache_context=ep_cache_context_content,
|
||||
),
|
||||
]
|
||||
|
||||
graph = helper.make_graph(
|
||||
nodes,
|
||||
"trt_engine_wrapper",
|
||||
[ # input
|
||||
helper.make_tensor_value_info("X", TensorProto.FLOAT, ["N", 2]),
|
||||
],
|
||||
[ # output
|
||||
helper.make_tensor_value_info("Y", TensorProto.FLOAT, ["N", 1]),
|
||||
],
|
||||
)
|
||||
model = helper.make_model(graph)
|
||||
onnx.save(model, self.ctx_node_model_name_)
|
||||
|
||||
def test_ctx_node(self):
|
||||
x = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], dtype=np.float32)
|
||||
|
||||
# First session and run to create engine cache
|
||||
providers = [
|
||||
(
|
||||
"TensorrtExecutionProvider",
|
||||
{"trt_engine_cache_enable": True, "trt_engine_cache_path": self.trt_engine_cache_path_},
|
||||
)
|
||||
]
|
||||
session = onnxrt.InferenceSession(get_name("matmul_2.onnx"), providers=providers)
|
||||
session.run(
|
||||
["Y"],
|
||||
{"X": x},
|
||||
)
|
||||
|
||||
# Get engine cache name
|
||||
cache_name = ""
|
||||
for f in os.listdir(self.trt_engine_cache_path_):
|
||||
if f.endswith(".engine"):
|
||||
cache_name = f
|
||||
print(cache_name)
|
||||
|
||||
# Second session and run to test ctx node with engine cache path
|
||||
self.create_ctx_node(cache_path=os.path.join(self.trt_engine_cache_path_, cache_name))
|
||||
providers = [("TensorrtExecutionProvider", {})]
|
||||
session = onnxrt.InferenceSession(get_name(self.ctx_node_model_name_), providers=providers)
|
||||
session.run(
|
||||
["Y"],
|
||||
{"X": x},
|
||||
)
|
||||
|
||||
# Third session and run to test ctx node with engine binary content
|
||||
self.create_ctx_node(ctx_embed_mode=1, cache_path=os.path.join(self.trt_engine_cache_path_, cache_name))
|
||||
session = onnxrt.InferenceSession(get_name(self.ctx_node_model_name_), providers=providers)
|
||||
session.run(
|
||||
["Y"],
|
||||
{"X": x},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Reference in a new issue