onnxruntime
d8f03186 - Add API to get ep graph partitioning info (#26781)

Commit
5 days ago
Add API to get ep graph partitioning info (#26781) ### Description - Adds API functions to get information about the subgraphs/nodes assigned to the EPs in the session. - `Session_GetEpGraphAssignmentInfo`: Returns a list of "subgraphs", each with information about the assigned EP and nodes. - Note: App must enable session configuration `"session.record_ep_graph_assignment_info"` to signal ORT to collect this information. If not enabled, API returns empty results. - `EpAssignedSubgraph_GetEpName`: Returns the name of the EP to which the subgraph is assigned - `EpAssignedSubgraph_GetNodes`: Returns a list of assigned nodes - `EpAssignedNode_GetName`: Returns the assigned node's name - `EpAssignedNode_GetDomain`: Returns the assigned node's domain - `EpAssignedNode_GetOperatorType`: Returns the assigned node's operator type - Also adds C++ and Python bindings #### Structure of returned information The API returns a list of "subgraphs". Each subgraph has the following information: - Subgraph info: - EP name: The name of the execution provider to which this subgraph is assigned. - nodes: Name and operator type of each node. Ex: `[{"multiply", "Mul"}, ...]` Python example program (taken from unit tests): ```python def test_get_graph_provider_assignment_info(self): """ Tests querying for information about the nodes assigned to the CPU EP. """ # Create session options that enables recording EP graph partitioning info. session_options = onnxrt.SessionOptions() session_options.add_session_config_entry("session.record_ep_graph_assignment_info", "1") session = onnxrt.InferenceSession(get_name("add_mul_add.onnx"), sess_options=session_options) # Query session for information on each subgraph assigned to an EP. ep_subgraphs = session.get_provider_graph_assignment_info() # Check that all 3 nodes are assigned to CPU EP (each in its own subgraph) self.assertEqual(len(ep_subgraphs), 3) for ep_subgraph in ep_subgraphs: self.assertEqual(ep_subgraph.ep_name, "CPUExecutionProvider") self.assertEqual(len(ep_subgraph.get_nodes()), 1) # Serialize each node to an identifier (concatenates operator type and node name) node_ids: list[str] = [f"{n.op_type}/{n.name}" for s in ep_subgraphs for n in s.get_nodes()] # Should have 1 Mul and 2 Adds. self.assertEqual(len(node_ids), 3) self.assertIn("Add/add_0", node_ids) self.assertIn("Add/add_1", node_ids) self.assertIn("Mul/mul_0", node_ids) ``` C++ program (taken from unit test): ```c++ // Check the ep graph partitioning (Mul on plugin EP, others on CPU EP). // Model has 3 subgraphs (in no particular order): // - Subgraph 1: Add assigned to CPU EP. // - Subgraph 2: Mul assigned to plugin EP. // - Subgraph 3: Add assigned to CPU EP. std::vector<Ort::ConstEpAssignedSubgraph> ep_subgraphs = session.GetEpGraphAssignmentInfo(); ASSERT_EQ(ep_subgraphs.size(), 3); for (Ort::ConstEpAssignedSubgraph ep_subgraph : ep_subgraphs) { std::string ep_name = ep_subgraph.EpName(); ASSERT_TRUE(ep_name == Utils::example_ep_info.ep_name || ep_name == kCpuExecutionProvider); const std::vector<Ort::ConstEpAssignedNode> ep_nodes = ep_subgraph.GetNodes(); ASSERT_GE(ep_nodes.size(), 1); // All of these subgraphs just have one node. if (ep_name == kCpuExecutionProvider) { std::string op_type = ep_nodes[0].OpType(); std::string node_name = ep_nodes[0].Name(); ASSERT_EQ(op_type, "Add"); ASSERT_TRUE(node_name == "add_0" || node_name == "add_1"); } else { ASSERT_TRUE(ep_name == Utils::example_ep_info.ep_name); std::string op_type = ep_nodes[0].OpType(); std::string node_name = ep_nodes[0].Name(); ASSERT_EQ(op_type, "Mul"); ASSERT_EQ(node_name, "mul_0"); } } ``` ### Motivation and Context <!-- - Why is this change required? What problem does it solve? - If it fixes an open issue, please link to the issue here. --> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Parents
Loading