Make TRT EP fully support control flow op and its subgraphs (#12692)

* sync graph proto in node's attributes

* Don't fuse nodes of control flow op until later in control flow op level

* remove unnecessary ep funtions

* remove unnecessary ep funtions

* remove unnecessary ep funtions

* missing 'override' keyword which makes MacOS/Web CI fail

* Add one more test run for Test3LayerNestedSubgraph with disabling graph optimization

* Update the comments to better understand the 4 cases
This commit is contained in:
Chi Lo 2022-08-26 12:45:47 -07:00 committed by GitHub
parent a972db06bf
commit c9fd193ef6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 212 additions and 7 deletions

View file

@ -313,6 +313,7 @@ struct ProviderHost {
virtual void AttributeProto__operator_delete(ONNX_NAMESPACE::AttributeProto* p) = 0;
virtual void AttributeProto__operator_assign(ONNX_NAMESPACE::AttributeProto* p, const ONNX_NAMESPACE::AttributeProto& v) = 0;
virtual const std::string& AttributeProto__name(const ONNX_NAMESPACE::AttributeProto* p) const = 0;
virtual ONNX_NAMESPACE::AttributeProto_AttributeType AttributeProto__type(const ONNX_NAMESPACE::AttributeProto* p) = 0;
virtual int AttributeProto__ints_size(const ONNX_NAMESPACE::AttributeProto* p) = 0;
virtual int AttributeProto__floats_size(const ONNX_NAMESPACE::AttributeProto* p) = 0;
@ -360,6 +361,13 @@ struct ProviderHost {
virtual void ModelProto__set_ir_version(ONNX_NAMESPACE::ModelProto* p, int64_t value) = 0;
// NodeProto
virtual std::unique_ptr<ONNX_NAMESPACE::NodeProto> NodeProto__construct() = 0;
virtual void NodeProto__operator_delete(ONNX_NAMESPACE::NodeProto* p) = 0;
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;
// TensorProto
virtual std::unique_ptr<ONNX_NAMESPACE::TensorProto> TensorProto__construct() = 0;
virtual void TensorProto__operator_delete(ONNX_NAMESPACE::TensorProto* p) = 0;
@ -586,6 +594,7 @@ struct ProviderHost {
virtual ConstPointerContainer<std::vector<NodeArg*>> Node__InputDefs(const Node* p) noexcept = 0;
virtual ConstPointerContainer<std::vector<NodeArg*>> Node__OutputDefs(const Node* p) noexcept = 0;
virtual NodeIndex Node__Index(const Node* p) noexcept = 0;
virtual std::vector<gsl::not_null<const Graph*>> Node__GetSubgraphs(const Node* p) const noexcept = 0;
virtual void Node__ToProto(const Node* p, ONNX_NAMESPACE::NodeProto& proto, bool update_subgraphs = false) = 0;
@ -627,6 +636,8 @@ struct ProviderHost {
virtual std::unique_ptr<NodeAttributes_Iterator> NodeAttributes__end(const NodeAttributes* p) = 0;
virtual std::unique_ptr<NodeAttributes_Iterator> NodeAttributes__find(const NodeAttributes* p, const std::string& key) = 0;
virtual void NodeAttributes__insert(NodeAttributes* p, const NodeAttributes& v) = 0;
virtual void NodeAttributes__emplace(NodeAttributes* p, const std::string& k, const ONNX_NAMESPACE::AttributeProto& v) = 0;
virtual void NodeAttributes__reserve(NodeAttributes* p, size_t size) = 0;
// Model
virtual void Model__operator_delete(Model* p) = 0;
@ -649,6 +660,8 @@ struct ProviderHost {
virtual const std::vector<const NodeArg*>& Graph__GetInputs(const Graph* p) noexcept = 0;
virtual bool Graph__GetInitializedTensor(const Graph* p, const std::string& tensor_name, const ONNX_NAMESPACE::TensorProto*& value) = 0;
virtual const Node* Graph__ParentNode(const Graph* p) const = 0;
// GraphViewer
virtual void GraphViewer__operator_delete(GraphViewer* p) = 0;
virtual std::unique_ptr<Model> GraphViewer__CreateModel(const GraphViewer* p, const logging::Logger& logger) = 0;
@ -660,7 +673,9 @@ struct ProviderHost {
virtual const NodeArg* GraphViewer__GetNodeArg(const GraphViewer* p, const std::string& name) = 0;
virtual bool GraphViewer__IsSubgraph(const GraphViewer* p) = 0;
virtual const Graph& GraphViewer__GetGraph(const GraphViewer* p) const = 0;
virtual bool GraphViewer__IsConstantInitializer(const GraphViewer* p, const std::string& name, bool check_outer_scope) = 0;
virtual const Node* GraphViewer__ParentNode(const GraphViewer* p) = 0;
virtual int GraphViewer__NumberOfNodes(const GraphViewer* p) noexcept = 0;
virtual int GraphViewer__MaxNodeIndex(const GraphViewer* p) noexcept = 0;

View file

@ -57,6 +57,7 @@ struct AttributeProto final {
void operator=(const AttributeProto& v) { g_host->AttributeProto__operator_assign(this, v); }
static void operator delete(void* p) { g_host->AttributeProto__operator_delete(reinterpret_cast<AttributeProto*>(p)); }
const std::string& name() const { return g_host->AttributeProto__name(this); }
AttributeProto_AttributeType type() const { return g_host->AttributeProto__type(this); }
int ints_size() const { return g_host->AttributeProto__ints_size(this); }
int floats_size() const { return g_host->AttributeProto__floats_size(this); }
@ -136,6 +137,17 @@ struct ModelProto final {
void operator=(const ModelProto&) = delete;
};
struct NodeProto final {
static std::unique_ptr<NodeProto> Create() { return g_host->NodeProto__construct(); }
static void operator delete(void* p) { g_host->NodeProto__operator_delete(reinterpret_cast<NodeProto*>(p)); }
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); }
NodeProto() = delete;
NodeProto(const NodeProto&) = delete;
};
struct TensorProto final {
static std::unique_ptr<TensorProto> Create() { return g_host->TensorProto__construct(); }
static void operator delete(void* p) { g_host->TensorProto__operator_delete(reinterpret_cast<TensorProto*>(p)); }
@ -574,6 +586,8 @@ struct Node final {
ConstPointerContainer<std::vector<NodeArg*>> OutputDefs() const noexcept { return g_host->Node__OutputDefs(this); }
NodeIndex Index() const noexcept { return g_host->Node__Index(this); }
std::vector<gsl::not_null<const Graph*>> GetSubgraphs() const noexcept { return g_host->Node__GetSubgraphs(this); }
void ToProto(ONNX_NAMESPACE::NodeProto& proto, bool update_subgraphs = false) const { return g_host->Node__ToProto(this, proto, update_subgraphs); }
const NodeAttributes& GetAttributes() const noexcept { return g_host->Node__GetAttributes(this); }
@ -646,6 +660,8 @@ struct NodeAttributes final {
IteratorHolder<NodeAttributes_Iterator, std::pair<const std::string, ONNX_NAMESPACE::AttributeProto>> end() const { return g_host->NodeAttributes__end(this); }
IteratorHolder<NodeAttributes_Iterator, std::pair<const std::string, ONNX_NAMESPACE::AttributeProto>> find(const std::string& key) const { return g_host->NodeAttributes__find(this, key); }
void insert(const NodeAttributes& v) { return g_host->NodeAttributes__insert(this, v); }
void emplace(const std::string& k, const ONNX_NAMESPACE::AttributeProto& v) { g_host->NodeAttributes__emplace(this, k, v); }
void reserve(size_t size) { g_host->NodeAttributes__reserve(this, size); }
NodeAttributes() = delete;
NodeAttributes(const NodeAttributes&) = delete;
@ -680,6 +696,8 @@ struct Graph final {
bool GetInitializedTensor(const std::string& tensor_name, const ONNX_NAMESPACE::TensorProto*& value) const { return g_host->Graph__GetInitializedTensor(this, tensor_name, value); }
const Node* ParentNode() const { return g_host->Graph__ParentNode(this); }
PROVIDER_DISALLOW_ALL(Graph)
};
@ -695,7 +713,9 @@ struct GraphViewer final {
const NodeArg* GetNodeArg(const std::string& name) const { return g_host->GraphViewer__GetNodeArg(this, name); }
bool IsSubgraph() const { return g_host->GraphViewer__IsSubgraph(this); }
const Graph& GetGraph() const { return g_host->GraphViewer__GetGraph(this); }
bool IsConstantInitializer(const std::string& name, bool check_outer_scope) const { return g_host->GraphViewer__IsConstantInitializer(this, name, check_outer_scope); }
const Node* ParentNode() const { return g_host->GraphViewer__ParentNode(this); }
int NumberOfNodes() const noexcept { return g_host->GraphViewer__NumberOfNodes(this); }
int MaxNodeIndex() const noexcept { return g_host->GraphViewer__MaxNodeIndex(this); }

View file

@ -555,6 +555,46 @@ Status TensorrtExecutionProvider::SetComputeStream(void* stream) {
return Status::OK();
}
// Check the graph is the subgraph of control flow op
bool TensorrtExecutionProvider::IsSubGraphOfControlFlowOp(const GraphViewer& graph) const {
if (graph.IsSubgraph()) {
const auto& node = graph.ParentNode();
if (control_flow_op_set_.find(node->OpType()) != control_flow_op_set_.end()) {
return true;
}
}
return false;
}
// Check whether all the nodes of the graph are assigned to specific ep
bool TensorrtExecutionProvider::AllNodesAssignedToSpecificEP(const GraphViewer& graph, const std::string& provider_type) const {
const int number_of_ort_nodes = graph.NumberOfNodes();
std::vector<size_t> nodes_vector(number_of_ort_nodes);
std::iota(std::begin(nodes_vector), std::end(nodes_vector), 0);
const std::vector<NodeIndex>& node_index = graph.GetNodesInTopologicalOrder();
for (const auto& index : nodes_vector) {
const auto& node = graph.GetNode(node_index[index]);
if (node->GetExecutionProviderType() != provider_type) {
return false;
}
}
return number_of_ort_nodes != 0;
}
// Check whether all the nodes of subgraph are supported
bool TensorrtExecutionProvider::IsSubGraphFullySupported(SubGraphCollection_t supported_nodes_vector, const int number_of_ort_nodes) const {
int number_of_trt_nodes = 0;
for (const auto& group : supported_nodes_vector) {
if (!group.first.empty()) {
number_of_trt_nodes += static_cast<int>(group.first.size());
}
}
return number_of_trt_nodes == number_of_ort_nodes;
}
std::unique_ptr<IndexedSubGraph> TensorrtExecutionProvider::GetSubGraph(SubGraph_t graph_nodes_index, const GraphViewer& graph) const {
const std::vector<NodeIndex>& node_index = graph.GetNodesInTopologicalOrder();
std::unordered_set<size_t> node_set;
@ -767,7 +807,29 @@ SubGraphCollection_t TensorrtExecutionProvider::GetSupportedList(SubGraphCollect
subgraph_output_names.push_back(name);
}
}
graph_build.AddNode(node->Name(), node->OpType(), node->Description(), inputs, outputs, &node->GetAttributes(), node->Domain());
// If the node has subgraph, it's possible that the ORT graph of that subgraph and the GraphProto in the node attributes are not in sync because of graph optimization.
// Therefore, we need to force GraphProto attributes to be updated in order to get the valid GraphProto.
if (node->GetAttributes().size() > 0) {
auto node_proto = ONNX_NAMESPACE::NodeProto::Create();
// we need to update any GraphProto attributes for subgraphs so that any changes made by things
// such as the optimizers are captured. otherwise we can end up saving an invalid graph.
node->ToProto(*node_proto, /* update_subgraphs */ true);
const int num_attributes = node_proto->attribute_size();
auto node_attributes = ONNX_NAMESPACE::NodeAttributes::Create();
node_attributes->reserve(num_attributes);
for (int i = 0; i < num_attributes; ++i) {
auto& attr = node_proto->attribute(i);
node_attributes->emplace(attr.name(), attr);
}
// The GraphProto attributes are the updated ones.
graph_build.AddNode(node->Name(), node->OpType(), node->Description(), inputs, outputs, node_attributes.get(), node->Domain());
} else {
// The GraphProto attributes are the original ones.
graph_build.AddNode(node->Name(), node->OpType(), node->Description(), inputs, outputs, &node->GetAttributes(), node->Domain());
}
}
ORT_ENFORCE(graph_build.Resolve().IsOK());
@ -984,16 +1046,36 @@ TensorrtExecutionProvider::GetCapability(const GraphViewer& graph,
const int number_of_ort_nodes = graph.NumberOfNodes();
std::vector<size_t> nodes_vector(number_of_ort_nodes);
std::iota(std::begin(nodes_vector), std::end(nodes_vector), 0);
std::vector<size_t> filtered_nodes_vector;
const std::vector<NodeIndex>& node_index = graph.GetNodesInTopologicalOrder();
// We currently exclude "If" and "Loop" control flow ops from original node vector before calling TensorRT parser.
// The reason is, these control flow ops have subgraph which might contain TRT fused node after ORT partition.
// If this is the case, TensorRT parser will complain the non-recognized TRT fused node and fail.
for (const auto& index : nodes_vector) {
const auto& node = graph.GetNode(node_index[index]);
if (node->OpType() == "If" || node->OpType() == "Loop" || node->OpType() == "Scan") {
continue;
/* If current node is control flow op, we take different approach based on following four cases:
*
* (1) control flow op is supported by TRT, and its subgraphs are all supported by TRT. Assign this node to TRT.
* (2) control flow op is supported by TRT, but not all its subgraphs supported by TRT. Don't assign this node to TRT.
* (3) control flow op is not supported by TRT, but its subgraphs all supported by TRT. Don't assign this node to TRT.
* (4) control flow op is not supported by TRT, and not all its subgraphs supported by TRT. Don't assign this node to TRT.
*
* For cases 2, 3, 4, even though the control flow op is not assigned to TRT, any portion of its subgraphs that can run in TRT will be still fused and assigned to TRT EP.
*/
if (control_flow_op_set_.find(node->OpType()) != control_flow_op_set_.end()) {
auto sub_graphs = node->GetSubgraphs();
if (sub_graphs.size() != 0) {
bool all_subgraphs_are_supported = true;
for (auto sub_graph : sub_graphs) {
if (!AllNodesAssignedToSpecificEP(*(sub_graph->CreateGraphViewer()), kTensorrtExecutionProvider)) {
all_subgraphs_are_supported = false;
break;
}
}
if (!all_subgraphs_are_supported) {
// if not all its subgraphs are supported, we need to exclude this control flow op
continue;
}
}
}
filtered_nodes_vector.push_back(index);
}
@ -1035,6 +1117,52 @@ TensorrtExecutionProvider::GetCapability(const GraphViewer& graph,
// Construct subgraph capability from node list
std::vector<std::unique_ptr<ComputeCapability>> result;
// Handle the case where the graph is subgraph of control flow op.
// The purpose is to make control flow op as well as its subgraphs run on TRT.
// Here we need to check whether subgraph is fully supported by TRT and don't fuse the nodes of the subgraph until control flow op level.
if (IsSubGraphOfControlFlowOp(graph) && IsSubGraphFullySupported(supported_nodes_vector, number_of_ort_nodes)) {
const std::vector<NodeIndex>& node_index = graph.GetNodesInTopologicalOrder();
bool all_subgraphs_are_supported = true;
// "If" control flow op has two subgraph bodies, "then" body and "else" body respectively.
// Check its parent node's another subgraph to see whether that subgraph is also fully supported by TRT.
if (graph.ParentNode()->OpType() == "If") {
all_subgraphs_are_supported = false;
SubGraphCollection_t subgraph_supported_nodes_vector;
auto sub_graphs = graph.ParentNode()->GetSubgraphs();
for (auto sub_graph : sub_graphs) {
if (sub_graph.get() != &graph.GetGraph()) {
auto sub_graph_veiwer = sub_graph->CreateGraphViewer();
const int number_of_ort_subgraph_nodes = sub_graph_veiwer->NumberOfNodes();
std::vector<size_t> subgraph_nodes_vector(number_of_ort_subgraph_nodes);
std::iota(std::begin(subgraph_nodes_vector), std::end(subgraph_nodes_vector), 0);
SubGraphCollection_t parser_subgraph_nodes_vector = {{subgraph_nodes_vector, false}};
bool subgraph_early_termination = false;
subgraph_supported_nodes_vector = GetSupportedList(parser_subgraph_nodes_vector, 0, max_partition_iterations_, *sub_graph_veiwer, &subgraph_early_termination);
all_subgraphs_are_supported = IsSubGraphFullySupported(subgraph_supported_nodes_vector, number_of_ort_subgraph_nodes);
break;
}
}
}
if (all_subgraphs_are_supported) {
// We want the subgraph nodes to be assigned to TRT EP but don't want them to be fused until later at the control flow op level.
// Simply request the subgraph nodes with a single ComputeCapability for each with no MetaDef (i.e. what the default implementation for IExecutionProvider::GetCapability does).
for (const auto& group : supported_nodes_vector) {
if (!group.first.empty()) {
for (const auto& index : group.first) {
std::unique_ptr<IndexedSubGraph> sub_graph = onnxruntime::IndexedSubGraph::Create();
sub_graph->Nodes().push_back(node_index[index]);
result.push_back(ComputeCapability::Create(std::move(sub_graph)));
}
}
}
LOGS_DEFAULT(INFO) << "[TensorRT EP] Whole graph will run on TensorRT execution provider";
return result;
}
}
int number_of_trt_nodes = 0;
for (const auto& group : supported_nodes_vector) {
if (!group.first.empty()) {

View file

@ -175,6 +175,7 @@ class TensorrtExecutionProvider : public IExecutionProvider {
int (*engine_decryption_)(const char*, char*, size_t*);
int (*engine_encryption_)(const char*, char*, size_t);
std::unordered_set<std::string> control_flow_op_set_ = {"If", "Loop", "Scan"};
std::unordered_map<std::string, tensorrt_ptr::unique_pointer<nvonnxparser::IParser>> parsers_;
std::unordered_map<std::string, tensorrt_ptr::unique_pointer<nvinfer1::ICudaEngine>> engines_;
std::unordered_map<std::string, tensorrt_ptr::unique_pointer<nvinfer1::IExecutionContext>> contexts_;
@ -206,5 +207,15 @@ class TensorrtExecutionProvider : public IExecutionProvider {
should be protected by a lock when invoked by multiple threads concurrently.
*/
std::unique_lock<OrtMutex> GetApiLock() const;
/**Check the graph is the subgraph of control flow op*/
bool IsSubGraphOfControlFlowOp(const GraphViewer& graph) const;
/**Check whether all the nodes of the graph are assigned to specific ep*/
bool AllNodesAssignedToSpecificEP(const GraphViewer& graph, const std::string& provider_type) const;
/**Check whether all the nodes of subgraph are supported*/
bool IsSubGraphFullySupported(SubGraphCollection_t supported_nodes_vector, const int number_of_ort_nodes) const;
};
} // namespace onnxruntime

View file

@ -385,6 +385,7 @@ struct ProviderHostImpl : ProviderHost {
void AttributeProto__operator_delete(ONNX_NAMESPACE::AttributeProto* p) override { delete p; }
void AttributeProto__operator_assign(ONNX_NAMESPACE::AttributeProto* p, const ONNX_NAMESPACE::AttributeProto& v) override { *p = v; }
const std::string& AttributeProto__name(const ONNX_NAMESPACE::AttributeProto* p) const override { return p->name(); }
ONNX_NAMESPACE::AttributeProto_AttributeType AttributeProto__type(const ONNX_NAMESPACE::AttributeProto* p) override { return p->type(); }
int AttributeProto__ints_size(const ONNX_NAMESPACE::AttributeProto* p) override { return p->ints_size(); }
int AttributeProto__floats_size(const ONNX_NAMESPACE::AttributeProto* p) override { return p->floats_size(); }
@ -433,6 +434,13 @@ struct ProviderHostImpl : ProviderHost {
void ModelProto__set_ir_version(ONNX_NAMESPACE::ModelProto* p, int64_t value) override { p->set_ir_version(value); }
// NodeProto (wrapped)
std::unique_ptr<ONNX_NAMESPACE::NodeProto> NodeProto__construct() override { return std::make_unique<ONNX_NAMESPACE::NodeProto>(); }
void NodeProto__operator_delete(ONNX_NAMESPACE::NodeProto* p) override { delete p; }
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); }
// TensorProto (wrapped)
std::unique_ptr<ONNX_NAMESPACE::TensorProto> TensorProto__construct() override { return std::make_unique<ONNX_NAMESPACE::TensorProto>(); }
void TensorProto__operator_delete(ONNX_NAMESPACE::TensorProto* p) override { delete p; }
@ -669,6 +677,7 @@ struct ProviderHostImpl : ProviderHost {
ConstPointerContainer<std::vector<NodeArg*>> Node__InputDefs(const Node* p) noexcept override { return p->InputDefs(); }
ConstPointerContainer<std::vector<NodeArg*>> Node__OutputDefs(const Node* p) noexcept override { return p->OutputDefs(); }
NodeIndex Node__Index(const Node* p) noexcept override { return p->Index(); }
std::vector<gsl::not_null<const Graph*>> Node__GetSubgraphs(const Node* p) const noexcept override { return p->GetSubgraphs(); }
void Node__ToProto(const Node* p, ONNX_NAMESPACE::NodeProto& proto, bool update_subgraphs = false) override { p->ToProto(proto, update_subgraphs); }
@ -715,6 +724,8 @@ struct ProviderHostImpl : ProviderHost {
return std::make_unique<NodeAttributes_Iterator_Impl>(p->find(key));
}
void NodeAttributes__insert(NodeAttributes* p, const NodeAttributes& v) override { return p->insert(v.begin(), v.end()); }
void NodeAttributes__emplace(NodeAttributes* p, const std::string& k, const ONNX_NAMESPACE::AttributeProto& v) override { p->emplace(k, v); }
void NodeAttributes__reserve(NodeAttributes* p, size_t size) override { p->reserve(size); }
// Model (wrapped)
void Model__operator_delete(Model* p) override { delete p; }
@ -739,6 +750,8 @@ struct ProviderHostImpl : ProviderHost {
const std::vector<const NodeArg*>& Graph__GetInputs(const Graph* p) noexcept override { return p->GetInputs(); }
bool Graph__GetInitializedTensor(const Graph* p, const std::string& tensor_name, const ONNX_NAMESPACE::TensorProto*& value) override { return p->GetInitializedTensor(tensor_name, value); }
const Node* Graph__ParentNode(const Graph* p) const override { return p->ParentNode(); }
// GraphViewer (wrapped)
void GraphViewer__operator_delete(GraphViewer* p) override { delete p; }
std::unique_ptr<Model> GraphViewer__CreateModel(const GraphViewer* graph_viewer, const logging::Logger& logger) override {
@ -758,7 +771,9 @@ struct ProviderHostImpl : ProviderHost {
const NodeArg* GraphViewer__GetNodeArg(const GraphViewer* p, const std::string& name) override { return p->GetNodeArg(name); }
bool GraphViewer__IsSubgraph(const GraphViewer* p) override { return p->IsSubgraph(); }
const Graph& GraphViewer__GetGraph(const GraphViewer* p) const override { return p->GetGraph(); }
bool GraphViewer__IsConstantInitializer(const GraphViewer* p, const std::string& name, bool check_outer_scope) override { return p->IsConstantInitializer(name, check_outer_scope); }
const Node* GraphViewer__ParentNode(const GraphViewer* p) override { return p->ParentNode(); }
int GraphViewer__NumberOfNodes(const GraphViewer* p) noexcept override { return p->NumberOfNodes(); }
int GraphViewer__MaxNodeIndex(const GraphViewer* p) noexcept override { return p->MaxNodeIndex(); }

View file

@ -1526,6 +1526,22 @@ TEST(InferenceSessionTests, Test3LayerNestedSubgraph) {
status = session_object.Run(run_options, feeds, output_names, &fetches);
ASSERT_TRUE(status.IsOK());
VerifyOutputs(fetches, expected_dims, expected_values);
#if USE_TENSORRT
// previous run with graph being optimized, one of If nodes both subgraphs become empty, so TRT EP wont assign this If node to TRT and later ORT assign it to CUDA.
// we also want to test graph not being optimized and TRT EP should also be able to run it and make the whole graph run on TRT.
so.graph_optimization_level = TransformerLevel::Default;
InferenceSession session_object_2{so, GetEnvironment()};
ASSERT_STATUS_OK(session_object_2.RegisterExecutionProvider(DefaultTensorrtExecutionProvider()));
status = session_object_2.Load(model_file_name);
ASSERT_TRUE(status.IsOK());
status = session_object_2.Initialize();
ASSERT_TRUE(status.IsOK());
// Now run
status = session_object_2.Run(run_options, feeds, output_names, &fetches);
ASSERT_TRUE(status.IsOK());
VerifyOutputs(fetches, expected_dims, expected_values);
#endif
}
TEST(InferenceSessionTests, Test2LayerNestedSubgraph) {