[PATCH dpdk v2 1/2] graph: add diamond topology performance test

Robin Jarry rjarry at redhat.com
Tue May 19 23:38:21 CEST 2026


Add a fan-out-then-converge test case to graph_perf_autotest. The
fan_out node sends 50% of objects to a converge node (edge 0) and
50% to a branch node (edge 1). The branch node forwards everything
to the same converge node.

Because converge is edge 0, a FIFO scheduler visits it before the
branch has run, causing two visits at 128 objs/call each. This
topology is used in a follow-up patch to demonstrate the benefit of
priority-based scheduling.

Signed-off-by: Robin Jarry <rjarry at redhat.com>
---
 app/test/test_graph_perf.c | 130 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 129 insertions(+), 1 deletion(-)

diff --git a/app/test/test_graph_perf.c b/app/test/test_graph_perf.c
index 983735c2d9c4..c509d364b826 100644
--- a/app/test/test_graph_perf.c
+++ b/app/test/test_graph_perf.c
@@ -263,7 +263,7 @@ graph_perf_teardown(void)
 }
 
 static inline rte_node_t
-graph_node_get(const char *pname, char *nname)
+graph_node_get(const char *pname, const char *nname)
 {
 	rte_node_t pnode_id = rte_node_from_name(pname);
 	char lookup_name[RTE_NODE_NAMESIZE];
@@ -1042,6 +1042,132 @@ graph_init_parallel_tree(void)
  *	snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
  */
 
+/* Graph Topology: fan-out-then-converge (diamond)
+ *
+ *   src --> fan_out --(50%)-------------------+-> converge --(100%)--> sink
+ *                   `-(50%)--> branch -(100%)-'
+ *
+ * The fan_out node enqueues to converge (edge 0) before branch (edge 1).
+ * With a FIFO scheduler, converge is visited first with only 50% of the
+ * objects, then branch runs and re-enqueues to converge for a second visit.
+ * With priority-based bitmap scheduling, branch (priority -1) runs before
+ * converge (priority 0), so converge accumulates all objects and is visited
+ * only once.
+ */
+static inline int
+graph_init_diamond(void)
+{
+	rte_node_t fan_out, branch, converge, src, snk;
+	struct test_graph_perf *graph_data;
+	struct rte_graph_param gconf = {0};
+	struct test_node_data *node_data;
+	const struct rte_memzone *mz;
+	const char *edge_names[2];
+	char *node_patterns[5];
+	rte_graph_t graph_id;
+
+	mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
+				 sizeof(struct test_graph_perf), 0, 0);
+	if (mz == NULL) {
+		printf("Failed to allocate graph common memory\n");
+		return -ENOMEM;
+	}
+	graph_data = mz->addr;
+	graph_data->nb_nodes = 5;
+	graph_data->node_data = calloc(5, sizeof(struct test_node_data));
+	if (graph_data->node_data == NULL)
+		goto memzone_free;
+
+	/* Clone all nodes */
+	src = graph_node_get(TEST_GRAPH_SRC_NAME, "0");
+	fan_out = graph_node_get(TEST_GRAPH_WRK_NAME, "fan");
+	/* converge must be edge 0 from fan_out so FIFO visits it first */
+	converge = graph_node_get(TEST_GRAPH_WRK_NAME, "conv");
+	branch = graph_node_get(TEST_GRAPH_WRK_NAME, "br");
+	snk = graph_node_get(TEST_GRAPH_SNK_NAME, "0");
+
+	if (src == RTE_NODE_ID_INVALID || fan_out == RTE_NODE_ID_INVALID ||
+	    converge == RTE_NODE_ID_INVALID || branch == RTE_NODE_ID_INVALID ||
+	    snk == RTE_NODE_ID_INVALID) {
+		printf("Failed to create nodes\n");
+		goto data_free;
+	}
+
+	/* src -> fan_out (100%) */
+	edge_names[0] = rte_node_id_to_name(fan_out);
+	rte_node_edge_update(src, 0, edge_names, 1);
+	node_data = &graph_data->node_data[0];
+	node_data->node_id = src;
+	node_data->is_sink = false;
+	node_data->next_nodes[0] = fan_out;
+	node_data->next_percentage[0] = 100;
+
+	/* fan_out: edge 0 -> converge (50%), edge 1 -> branch (50%) */
+	edge_names[0] = rte_node_id_to_name(converge);
+	edge_names[1] = rte_node_id_to_name(branch);
+	rte_node_edge_update(fan_out, 0, edge_names, 2);
+	node_data = &graph_data->node_data[1];
+	node_data->node_id = fan_out;
+	node_data->is_sink = false;
+	node_data->next_nodes[0] = converge;
+	node_data->next_percentage[0] = 50;
+	node_data->next_nodes[1] = branch;
+	node_data->next_percentage[1] = 50;
+
+	/* branch -> converge (100%) */
+	edge_names[0] = rte_node_id_to_name(converge);
+	rte_node_edge_update(branch, 0, edge_names, 1);
+	node_data = &graph_data->node_data[2];
+	node_data->node_id = branch;
+	node_data->is_sink = false;
+	node_data->next_nodes[0] = converge;
+	node_data->next_percentage[0] = 100;
+
+	/* converge -> sink (100%) */
+	edge_names[0] = rte_node_id_to_name(snk);
+	rte_node_edge_update(converge, 0, edge_names, 1);
+	node_data = &graph_data->node_data[3];
+	node_data->node_id = converge;
+	node_data->is_sink = false;
+	node_data->next_nodes[0] = snk;
+	node_data->next_percentage[0] = 100;
+
+	/* sink */
+	node_data = &graph_data->node_data[4];
+	node_data->node_id = snk;
+	node_data->is_sink = true;
+
+	node_patterns[0] = rte_node_id_to_name(src);
+	node_patterns[1] = rte_node_id_to_name(fan_out);
+	node_patterns[2] = rte_node_id_to_name(converge);
+	node_patterns[3] = rte_node_id_to_name(branch);
+	node_patterns[4] = rte_node_id_to_name(snk);
+
+	gconf.socket_id = SOCKET_ID_ANY;
+	gconf.nb_node_patterns = 5;
+	gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
+
+	graph_id = rte_graph_create("graph_diamond", &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		goto data_free;
+	}
+	graph_data->graph_id = graph_id;
+	return 0;
+
+data_free:
+	free(graph_data->node_data);
+memzone_free:
+	rte_memzone_free(mz);
+	return -ENOMEM;
+}
+
+static inline int
+graph_diamond_1src_1snk(void)
+{
+	return measure_perf();
+}
+
 static struct unit_test_suite graph_perf_testsuite = {
 	.suite_name = "Graph library performance test suite",
 	.setup = graph_perf_setup,
@@ -1061,6 +1187,8 @@ static struct unit_test_suite graph_perf_testsuite = {
 			     graph_reverse_tree_3s_4n_1src_1snk),
 		TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
 			     graph_parallel_tree_5s_4n_4src_4snk),
+		TEST_CASE_ST(graph_init_diamond, graph_fini,
+			     graph_diamond_1src_1snk),
 		TEST_CASES_END(), /**< NULL terminate unit test array */
 	},
 };
-- 
2.54.0



More information about the dev mailing list