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 <[email protected]>
---
 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

Reply via email to