test/graph: add performance tests
[dpdk.git] / app / test / test_graph_perf.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2020 Marvell International Ltd.
3  */
4 #include <inttypes.h>
5 #include <signal.h>
6 #include <stdio.h>
7 #include <unistd.h>
8
9 #include <rte_common.h>
10 #include <rte_cycles.h>
11 #include <rte_errno.h>
12 #include <rte_graph.h>
13 #include <rte_graph_worker.h>
14 #include <rte_lcore.h>
15 #include <rte_malloc.h>
16 #include <rte_mbuf.h>
17
18 #include "test.h"
19
20 #define TEST_GRAPH_PERF_MZ           "graph_perf_data"
21 #define TEST_GRAPH_SRC_NAME          "test_graph_perf_source"
22 #define TEST_GRAPH_SRC_BRST_ONE_NAME "test_graph_perf_source_one"
23 #define TEST_GRAPH_WRK_NAME          "test_graph_perf_worker"
24 #define TEST_GRAPH_SNK_NAME          "test_graph_perf_sink"
25
26 #define SOURCES(map)         RTE_DIM(map)
27 #define STAGES(map)          RTE_DIM(map)
28 #define NODES_PER_STAGE(map) RTE_DIM(map[0])
29 #define SINKS(map)           RTE_DIM(map[0])
30
31 #define MAX_EDGES_PER_NODE 7
32
33 struct test_node_data {
34         uint8_t node_id;
35         uint8_t is_sink;
36         uint8_t next_nodes[MAX_EDGES_PER_NODE];
37         uint8_t next_percentage[MAX_EDGES_PER_NODE];
38 };
39
40 struct test_graph_perf {
41         uint16_t nb_nodes;
42         rte_graph_t graph_id;
43         struct test_node_data *node_data;
44 };
45
46 struct graph_lcore_data {
47         uint8_t done;
48         rte_graph_t graph_id;
49 };
50
51 static struct test_node_data *
52 graph_get_node_data(struct test_graph_perf *graph_data, rte_node_t id)
53 {
54         struct test_node_data *node_data = NULL;
55         int i;
56
57         for (i = 0; i < graph_data->nb_nodes; i++)
58                 if (graph_data->node_data[i].node_id == id) {
59                         node_data = &graph_data->node_data[i];
60                         break;
61                 }
62
63         return node_data;
64 }
65
66 static int
67 test_node_ctx_init(const struct rte_graph *graph, struct rte_node *node)
68 {
69         struct test_graph_perf *graph_data;
70         struct test_node_data *node_data;
71         const struct rte_memzone *mz;
72         rte_node_t nid = node->id;
73         rte_edge_t edge = 0;
74         int i;
75
76         RTE_SET_USED(graph);
77
78         mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
79         graph_data = mz->addr;
80         node_data = graph_get_node_data(graph_data, nid);
81         node->ctx[0] = node->nb_edges;
82         for (i = 0; i < node->nb_edges && !node_data->is_sink; i++, edge++) {
83                 node->ctx[i + 1] = edge;
84                 node->ctx[i + 9] = node_data->next_percentage[i];
85         }
86
87         return 0;
88 }
89
90 /* Source node function */
91 static uint16_t
92 test_perf_node_worker_source(struct rte_graph *graph, struct rte_node *node,
93                              void **objs, uint16_t nb_objs)
94 {
95         uint16_t count;
96         int i;
97
98         RTE_SET_USED(objs);
99         RTE_SET_USED(nb_objs);
100
101         /* Create a proportional stream for every next */
102         for (i = 0; i < node->ctx[0]; i++) {
103                 count = (node->ctx[i + 9] * RTE_GRAPH_BURST_SIZE) / 100;
104                 rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
105                 rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
106         }
107
108         return RTE_GRAPH_BURST_SIZE;
109 }
110
111 static struct rte_node_register test_graph_perf_source = {
112         .name = TEST_GRAPH_SRC_NAME,
113         .process = test_perf_node_worker_source,
114         .flags = RTE_NODE_SOURCE_F,
115         .init = test_node_ctx_init,
116 };
117
118 RTE_NODE_REGISTER(test_graph_perf_source);
119
120 static uint16_t
121 test_perf_node_worker_source_burst_one(struct rte_graph *graph,
122                                        struct rte_node *node, void **objs,
123                                        uint16_t nb_objs)
124 {
125         uint16_t count;
126         int i;
127
128         RTE_SET_USED(objs);
129         RTE_SET_USED(nb_objs);
130
131         /* Create a proportional stream for every next */
132         for (i = 0; i < node->ctx[0]; i++) {
133                 count = (node->ctx[i + 9]) / 100;
134                 rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
135                 rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
136         }
137
138         return 1;
139 }
140
141 static struct rte_node_register test_graph_perf_source_burst_one = {
142         .name = TEST_GRAPH_SRC_BRST_ONE_NAME,
143         .process = test_perf_node_worker_source_burst_one,
144         .flags = RTE_NODE_SOURCE_F,
145         .init = test_node_ctx_init,
146 };
147
148 RTE_NODE_REGISTER(test_graph_perf_source_burst_one);
149
150 /* Worker node function */
151 static uint16_t
152 test_perf_node_worker(struct rte_graph *graph, struct rte_node *node,
153                       void **objs, uint16_t nb_objs)
154 {
155         uint16_t next = 0;
156         uint16_t enq = 0;
157         uint16_t count;
158         int i;
159
160         /* Move stream for single next node */
161         if (node->ctx[0] == 1) {
162                 rte_node_next_stream_move(graph, node, node->ctx[1]);
163                 return nb_objs;
164         }
165
166         /* Enqueue objects to next nodes proportionally */
167         for (i = 0; i < node->ctx[0]; i++) {
168                 next = node->ctx[i + 1];
169                 count = (node->ctx[i + 9] * nb_objs) / 100;
170                 enq += count;
171                 while (count) {
172                         switch (count & (4 - 1)) {
173                         case 0:
174                                 rte_node_enqueue_x4(graph, node, next, objs[0],
175                                                     objs[1], objs[2], objs[3]);
176                                 objs += 4;
177                                 count -= 4;
178                                 break;
179                         case 1:
180                                 rte_node_enqueue_x1(graph, node, next, objs[0]);
181                                 objs += 1;
182                                 count -= 1;
183                                 break;
184                         case 2:
185                                 rte_node_enqueue_x2(graph, node, next, objs[0],
186                                                     objs[1]);
187                                 objs += 2;
188                                 count -= 2;
189                                 break;
190                         case 3:
191                                 rte_node_enqueue_x2(graph, node, next, objs[0],
192                                                     objs[1]);
193                                 rte_node_enqueue_x1(graph, node, next, objs[0]);
194                                 objs += 3;
195                                 count -= 3;
196                                 break;
197                         }
198                 }
199         }
200
201         if (enq != nb_objs)
202                 rte_node_enqueue(graph, node, next, objs, nb_objs - enq);
203
204         return nb_objs;
205 }
206
207 static struct rte_node_register test_graph_perf_worker = {
208         .name = TEST_GRAPH_WRK_NAME,
209         .process = test_perf_node_worker,
210         .init = test_node_ctx_init,
211 };
212
213 RTE_NODE_REGISTER(test_graph_perf_worker);
214
215 /* Last node in graph a.k.a sink node */
216 static uint16_t
217 test_perf_node_sink(struct rte_graph *graph, struct rte_node *node, void **objs,
218                     uint16_t nb_objs)
219 {
220         RTE_SET_USED(graph);
221         RTE_SET_USED(node);
222         RTE_SET_USED(objs);
223         RTE_SET_USED(nb_objs);
224
225         return nb_objs;
226 }
227
228 static struct rte_node_register test_graph_perf_sink = {
229         .name = TEST_GRAPH_SNK_NAME,
230         .process = test_perf_node_sink,
231         .init = test_node_ctx_init,
232 };
233
234 RTE_NODE_REGISTER(test_graph_perf_sink);
235
236 static int
237 graph_perf_setup(void)
238 {
239         if (rte_lcore_count() < 2) {
240                 printf("Test requires at least 2 lcores\n");
241                 return TEST_SKIPPED;
242         }
243
244         return 0;
245 }
246
247 static void
248 graph_perf_teardown(void)
249 {
250 }
251
252 static inline rte_node_t
253 graph_node_get(const char *pname, char *nname)
254 {
255         rte_node_t pnode_id = rte_node_from_name(pname);
256         char lookup_name[RTE_NODE_NAMESIZE];
257         rte_node_t node_id;
258
259         snprintf(lookup_name, RTE_NODE_NAMESIZE, "%s-%s", pname, nname);
260         node_id = rte_node_from_name(lookup_name);
261
262         if (node_id != RTE_NODE_ID_INVALID) {
263                 if (rte_node_edge_count(node_id))
264                         rte_node_edge_shrink(node_id, 0);
265                 return node_id;
266         }
267
268         return rte_node_clone(pnode_id, nname);
269 }
270
271 static uint16_t
272 graph_node_count_edges(uint32_t stage, uint16_t node, uint16_t nodes_per_stage,
273                        uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
274                        char *ename[], struct test_node_data *node_data,
275                        rte_node_t **node_map)
276 {
277         uint8_t total_percent = 0;
278         uint16_t edges = 0;
279         int i;
280
281         for (i = 0; i < nodes_per_stage && edges < MAX_EDGES_PER_NODE; i++) {
282                 if (edge_map[stage + 1][i][node]) {
283                         ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
284                         snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
285                                  rte_node_id_to_name(node_map[stage + 1][i]));
286                         node_data->next_nodes[edges] = node_map[stage + 1][i];
287                         node_data->next_percentage[edges] =
288                                 edge_map[stage + 1][i][node];
289                         edges++;
290                         total_percent += edge_map[stage + 1][i][node];
291                 }
292         }
293
294         if (edges >= MAX_EDGES_PER_NODE || (edges && total_percent != 100)) {
295                 for (i = 0; i < edges; i++)
296                         free(ename[i]);
297                 return RTE_EDGE_ID_INVALID;
298         }
299
300         return edges;
301 }
302
303 static int
304 graph_init(const char *gname, uint8_t nb_srcs, uint8_t nb_sinks,
305            uint32_t stages, uint16_t nodes_per_stage,
306            uint8_t src_map[][nodes_per_stage], uint8_t snk_map[][nb_sinks],
307            uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
308            uint8_t burst_one)
309 {
310         struct test_graph_perf *graph_data;
311         char nname[RTE_NODE_NAMESIZE / 2];
312         struct test_node_data *node_data;
313         char *ename[nodes_per_stage];
314         struct rte_graph_param gconf;
315         const struct rte_memzone *mz;
316         uint8_t total_percent = 0;
317         rte_node_t *src_nodes;
318         rte_node_t *snk_nodes;
319         rte_node_t **node_map;
320         char **node_patterns;
321         rte_graph_t graph_id;
322         rte_edge_t edges;
323         rte_edge_t count;
324         uint32_t i, j, k;
325
326         mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
327                                  sizeof(struct test_graph_perf), 0, 0);
328         if (mz == NULL) {
329                 printf("Failed to allocate graph common memory\n");
330                 return -ENOMEM;
331         }
332
333         graph_data = mz->addr;
334         graph_data->nb_nodes = 0;
335         graph_data->node_data =
336                 malloc(sizeof(struct test_node_data) *
337                        (nb_srcs + nb_sinks + stages * nodes_per_stage));
338         if (graph_data->node_data == NULL) {
339                 printf("Failed to reserve memzone for graph data\n");
340                 goto memzone_free;
341         }
342
343         node_patterns = malloc(sizeof(char *) *
344                                (nb_srcs + nb_sinks + stages * nodes_per_stage));
345         if (node_patterns == NULL) {
346                 printf("Failed to reserve memory for node patterns\n");
347                 goto data_free;
348         }
349
350         src_nodes = malloc(sizeof(rte_node_t) * nb_srcs);
351         if (src_nodes == NULL) {
352                 printf("Failed to reserve memory for src nodes\n");
353                 goto pattern_free;
354         }
355
356         snk_nodes = malloc(sizeof(rte_node_t) * nb_sinks);
357         if (snk_nodes == NULL) {
358                 printf("Failed to reserve memory for snk nodes\n");
359                 goto src_free;
360         }
361
362         node_map = malloc(sizeof(rte_node_t *) * stages +
363                           sizeof(rte_node_t) * nodes_per_stage * stages);
364         if (node_map == NULL) {
365                 printf("Failed to reserve memory for node map\n");
366                 goto snk_free;
367         }
368
369         /* Setup the Graph */
370         for (i = 0; i < stages; i++) {
371                 node_map[i] =
372                         (rte_node_t *)(node_map + stages) + nodes_per_stage * i;
373                 for (j = 0; j < nodes_per_stage; j++) {
374                         total_percent = 0;
375                         for (k = 0; k < nodes_per_stage; k++)
376                                 total_percent += edge_map[i][j][k];
377                         if (!total_percent)
378                                 continue;
379                         node_patterns[graph_data->nb_nodes] =
380                                 malloc(RTE_NODE_NAMESIZE);
381                         if (node_patterns[graph_data->nb_nodes] == NULL) {
382                                 printf("Failed to create memory for pattern\n");
383                                 goto pattern_name_free;
384                         }
385
386                         /* Clone a worker node */
387                         snprintf(nname, sizeof(nname), "%d-%d", i, j);
388                         node_map[i][j] =
389                                 graph_node_get(TEST_GRAPH_WRK_NAME, nname);
390                         if (node_map[i][j] == RTE_NODE_ID_INVALID) {
391                                 printf("Failed to create node[%s]\n", nname);
392                                 graph_data->nb_nodes++;
393                                 goto pattern_name_free;
394                         }
395                         snprintf(node_patterns[graph_data->nb_nodes],
396                                  RTE_NODE_NAMESIZE, "%s",
397                                  rte_node_id_to_name(node_map[i][j]));
398                         node_data =
399                                 &graph_data->node_data[graph_data->nb_nodes];
400                         node_data->node_id = node_map[i][j];
401                         node_data->is_sink = false;
402                         graph_data->nb_nodes++;
403                 }
404         }
405
406         for (i = 0; i < stages - 1; i++) {
407                 for (j = 0; j < nodes_per_stage; j++) {
408                         /* Count edges i.e connections of worker node to next */
409                         node_data =
410                                 graph_get_node_data(graph_data, node_map[i][j]);
411                         edges = graph_node_count_edges(i, j, nodes_per_stage,
412                                                        edge_map, ename,
413                                                        node_data, node_map);
414                         if (edges == RTE_EDGE_ID_INVALID) {
415                                 printf("Invalid edge configuration\n");
416                                 goto pattern_name_free;
417                         }
418                         if (!edges)
419                                 continue;
420
421                         /* Connect a node in stage 'i' to nodes
422                          * in stage 'i + 1' with edges.
423                          */
424                         count = rte_node_edge_update(
425                                 node_map[i][j], 0,
426                                 (const char **)(uintptr_t)ename, edges);
427                         for (k = 0; k < edges; k++)
428                                 free(ename[k]);
429                         if (count != edges) {
430                                 printf("Couldn't add edges %d %d\n", edges,
431                                        count);
432                                 goto pattern_name_free;
433                         }
434                 }
435         }
436
437         /* Setup Source nodes */
438         for (i = 0; i < nb_srcs; i++) {
439                 edges = 0;
440                 total_percent = 0;
441                 node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
442                 if (node_patterns[graph_data->nb_nodes] == NULL) {
443                         printf("Failed to create memory for pattern\n");
444                         goto pattern_name_free;
445                 }
446                 /* Clone a source node */
447                 snprintf(nname, sizeof(nname), "%d", i);
448                 src_nodes[i] =
449                         graph_node_get(burst_one ? TEST_GRAPH_SRC_BRST_ONE_NAME
450                                                  : TEST_GRAPH_SRC_NAME,
451                                        nname);
452                 if (src_nodes[i] == RTE_NODE_ID_INVALID) {
453                         printf("Failed to create node[%s]\n", nname);
454                         graph_data->nb_nodes++;
455                         goto pattern_name_free;
456                 }
457                 snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
458                          "%s", rte_node_id_to_name(src_nodes[i]));
459                 node_data = &graph_data->node_data[graph_data->nb_nodes];
460                 node_data->node_id = src_nodes[i];
461                 node_data->is_sink = false;
462                 graph_data->nb_nodes++;
463
464                 /* Prepare next node list  to connect to */
465                 for (j = 0; j < nodes_per_stage; j++) {
466                         if (!src_map[i][j])
467                                 continue;
468                         ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
469                         snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
470                                  rte_node_id_to_name(node_map[0][j]));
471                         node_data->next_nodes[edges] = node_map[0][j];
472                         node_data->next_percentage[edges] = src_map[i][j];
473                         edges++;
474                         total_percent += src_map[i][j];
475                 }
476
477                 if (!edges)
478                         continue;
479                 if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
480                         printf("Invalid edge configuration\n");
481                         for (j = 0; j < edges; j++)
482                                 free(ename[j]);
483                         goto pattern_name_free;
484                 }
485
486                 /* Connect to list of next nodes using edges */
487                 count = rte_node_edge_update(src_nodes[i], 0,
488                                              (const char **)(uintptr_t)ename,
489                                              edges);
490                 for (k = 0; k < edges; k++)
491                         free(ename[k]);
492                 if (count != edges) {
493                         printf("Couldn't add edges %d %d\n", edges, count);
494                         goto pattern_name_free;
495                 }
496         }
497
498         /* Setup Sink nodes */
499         for (i = 0; i < nb_sinks; i++) {
500                 node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
501                 if (node_patterns[graph_data->nb_nodes] == NULL) {
502                         printf("Failed to create memory for pattern\n");
503                         goto pattern_name_free;
504                 }
505
506                 /* Clone a sink node */
507                 snprintf(nname, sizeof(nname), "%d", i);
508                 snk_nodes[i] = graph_node_get(TEST_GRAPH_SNK_NAME, nname);
509                 if (snk_nodes[i] == RTE_NODE_ID_INVALID) {
510                         printf("Failed to create node[%s]\n", nname);
511                         graph_data->nb_nodes++;
512                         goto pattern_name_free;
513                 }
514                 snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
515                          "%s", rte_node_id_to_name(snk_nodes[i]));
516                 node_data = &graph_data->node_data[graph_data->nb_nodes];
517                 node_data->node_id = snk_nodes[i];
518                 node_data->is_sink = true;
519                 graph_data->nb_nodes++;
520         }
521
522         /* Connect last stage worker nodes to sink nodes */
523         for (i = 0; i < nodes_per_stage; i++) {
524                 edges = 0;
525                 total_percent = 0;
526                 node_data = graph_get_node_data(graph_data,
527                                                 node_map[stages - 1][i]);
528                 /* Prepare list of sink nodes to connect to */
529                 for (j = 0; j < nb_sinks; j++) {
530                         if (!snk_map[i][j])
531                                 continue;
532                         ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
533                         snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
534                                  rte_node_id_to_name(snk_nodes[j]));
535                         node_data->next_nodes[edges] = snk_nodes[j];
536                         node_data->next_percentage[edges] = snk_map[i][j];
537                         edges++;
538                         total_percent += snk_map[i][j];
539                 }
540                 if (!edges)
541                         continue;
542                 if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
543                         printf("Invalid edge configuration\n");
544                         for (j = 0; j < edges; j++)
545                                 free(ename[i]);
546                         goto pattern_name_free;
547                 }
548
549                 /* Connect a worker node to a list of sink nodes */
550                 count = rte_node_edge_update(node_map[stages - 1][i], 0,
551                                              (const char **)(uintptr_t)ename,
552                                              edges);
553                 for (k = 0; k < edges; k++)
554                         free(ename[k]);
555                 if (count != edges) {
556                         printf("Couldn't add edges %d %d\n", edges, count);
557                         goto pattern_name_free;
558                 }
559         }
560
561         /* Create a Graph */
562         gconf.socket_id = SOCKET_ID_ANY;
563         gconf.nb_node_patterns = graph_data->nb_nodes;
564         gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
565
566         graph_id = rte_graph_create(gname, &gconf);
567         if (graph_id == RTE_GRAPH_ID_INVALID) {
568                 printf("Graph creation failed with error = %d\n", rte_errno);
569                 goto pattern_name_free;
570         }
571         graph_data->graph_id = graph_id;
572
573         for (i = 0; i < graph_data->nb_nodes; i++)
574                 free(node_patterns[i]);
575         free(snk_nodes);
576         free(src_nodes);
577         free(node_patterns);
578         return 0;
579
580 pattern_name_free:
581         for (i = 0; i < graph_data->nb_nodes; i++)
582                 free(node_patterns[i]);
583 snk_free:
584         free(snk_nodes);
585 src_free:
586         free(src_nodes);
587 pattern_free:
588         free(node_patterns);
589 data_free:
590         free(graph_data->node_data);
591 memzone_free:
592         rte_memzone_free(mz);
593         return -ENOMEM;
594 }
595
596 /* Worker thread function */
597 static int
598 _graph_perf_wrapper(void *args)
599 {
600         struct graph_lcore_data *data = args;
601         struct rte_graph *graph;
602
603         /* Lookup graph */
604         graph = rte_graph_lookup(rte_graph_id_to_name(data->graph_id));
605
606         /* Graph walk until done */
607         while (!data->done)
608                 rte_graph_walk(graph);
609
610         return 0;
611 }
612
613 static int
614 measure_perf_get(rte_graph_t graph_id)
615 {
616         const char *pattern = rte_graph_id_to_name(graph_id);
617         uint32_t lcore_id = rte_get_next_lcore(-1, 1, 0);
618         struct rte_graph_cluster_stats_param param;
619         struct rte_graph_cluster_stats *stats;
620         struct graph_lcore_data *data;
621
622         data = rte_zmalloc("Graph_perf", sizeof(struct graph_lcore_data),
623                            RTE_CACHE_LINE_SIZE);
624         data->graph_id = graph_id;
625         data->done = 0;
626
627         /* Run graph worker thread function */
628         rte_eal_remote_launch(_graph_perf_wrapper, data, lcore_id);
629
630         /* Collect stats for few msecs */
631         if (rte_graph_has_stats_feature()) {
632                 memset(&param, 0, sizeof(param));
633                 param.f = stdout;
634                 param.socket_id = SOCKET_ID_ANY;
635                 param.graph_patterns = &pattern;
636                 param.nb_graph_patterns = 1;
637
638                 stats = rte_graph_cluster_stats_create(&param);
639                 if (stats == NULL) {
640                         printf("Failed to create stats\n");
641                         return -ENOMEM;
642                 }
643
644                 rte_delay_ms(3E2);
645                 rte_graph_cluster_stats_get(stats, true);
646                 rte_delay_ms(1E3);
647                 rte_graph_cluster_stats_get(stats, false);
648                 rte_graph_cluster_stats_destroy(stats);
649         } else
650                 rte_delay_ms(1E3);
651
652         data->done = 1;
653         rte_eal_wait_lcore(lcore_id);
654
655         return 0;
656 }
657
658 static inline void
659 graph_fini(void)
660 {
661         const struct rte_memzone *mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
662         struct test_graph_perf *graph_data;
663
664         if (mz == NULL)
665                 return;
666         graph_data = mz->addr;
667
668         rte_graph_destroy(graph_data->graph_id);
669         free(graph_data->node_data);
670         rte_memzone_free(rte_memzone_lookup(TEST_GRAPH_PERF_MZ));
671 }
672
673 static int
674 measure_perf(void)
675 {
676         const struct rte_memzone *mz;
677         struct test_graph_perf *graph_data;
678
679         mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
680         graph_data = mz->addr;
681
682         return measure_perf_get(graph_data->graph_id);
683 }
684
685 static inline int
686 graph_hr_4s_1n_1src_1snk(void)
687 {
688         return measure_perf();
689 }
690
691 static inline int
692 graph_hr_4s_1n_1src_1snk_brst_one(void)
693 {
694         return measure_perf();
695 }
696
697 static inline int
698 graph_hr_4s_1n_2src_1snk(void)
699 {
700         return measure_perf();
701 }
702
703 static inline int
704 graph_hr_4s_1n_1src_2snk(void)
705 {
706         return measure_perf();
707 }
708
709 static inline int
710 graph_tree_4s_4n_1src_4snk(void)
711 {
712         return measure_perf();
713 }
714
715 static inline int
716 graph_reverse_tree_3s_4n_1src_1snk(void)
717 {
718         return measure_perf();
719 }
720
721 static inline int
722 graph_parallel_tree_5s_4n_4src_4snk(void)
723 {
724         return measure_perf();
725 }
726
727 /* Graph Topology
728  * nodes per stage:     1
729  * stages:              4
730  * src:                 1
731  * sink:                1
732  */
733 static inline int
734 graph_init_hr(void)
735 {
736         uint8_t edge_map[][1][1] = {
737                 { {100} },
738                 { {100} },
739                 { {100} },
740                 { {100} },
741         };
742         uint8_t src_map[][1] = { {100} };
743         uint8_t snk_map[][1] = { {100} };
744
745         return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
746                           STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
747                           snk_map, edge_map, 0);
748 }
749
750 /* Graph Topology
751  * nodes per stage:     1
752  * stages:              4
753  * src:                 1
754  * sink:                1
755  */
756 static inline int
757 graph_init_hr_brst_one(void)
758 {
759         uint8_t edge_map[][1][1] = {
760                 { {100} },
761                 { {100} },
762                 { {100} },
763                 { {100} },
764         };
765         uint8_t src_map[][1] = { {100} };
766         uint8_t snk_map[][1] = { {100} };
767
768         return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
769                           STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
770                           snk_map, edge_map, 1);
771 }
772
773 /* Graph Topology
774  * nodes per stage:     1
775  * stages:              4
776  * src:                 2
777  * sink:                1
778  */
779 static inline int
780 graph_init_hr_multi_src(void)
781 {
782         uint8_t edge_map[][1][1] = {
783                 { {100} },
784                 { {100} },
785                 { {100} },
786                 { {100} },
787         };
788         uint8_t src_map[][1] = {
789                 {100}, {100}
790         };
791         uint8_t snk_map[][1] = { {100} };
792
793         return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
794                           STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
795                           snk_map, edge_map, 0);
796 }
797
798 /* Graph Topology
799  * nodes per stage:     1
800  * stages:              4
801  * src:                 1
802  * sink:                2
803  */
804 static inline int
805 graph_init_hr_multi_snk(void)
806 {
807         uint8_t edge_map[][1][1] = {
808                 { {100} },
809                 { {100} },
810                 { {100} },
811                 { {100} },
812         };
813         uint8_t src_map[][1] = { {100} };
814         uint8_t snk_map[][2] = { {50, 50} };
815
816         return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
817                           STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
818                           snk_map, edge_map, 0);
819 }
820
821 /* Graph Topology
822  * nodes per stage:     4
823  * stages:              4
824  * src:                 1
825  * sink:                4
826  */
827 static inline int
828 graph_init_tree(void)
829 {
830         uint8_t edge_map[][4][4] = {
831                 {
832                         {100, 0, 0, 0},
833                         {0, 0, 0, 0},
834                         {0, 0, 0, 0},
835                         {0, 0, 0, 0}
836                 },
837                 {
838                         {50, 0, 0, 0},
839                         {50, 0, 0, 0},
840                         {0, 0, 0, 0},
841                         {0, 0, 0, 0}
842                 },
843                 {
844                         {33, 33, 0, 0},
845                         {34, 34, 0, 0},
846                         {33, 33, 0, 0},
847                         {0, 0, 0, 0}
848                 },
849                 {
850                         {25, 25, 25, 0},
851                         {25, 25, 25, 0},
852                         {25, 25, 25, 0},
853                         {25, 25, 25, 0}
854                 }
855         };
856         uint8_t src_map[][4] = { {100, 0, 0, 0} };
857         uint8_t snk_map[][4] = {
858                 {100, 0, 0, 0},
859                 {0, 100, 0, 0},
860                 {0, 0, 100, 0},
861                 {0, 0, 0, 100}
862         };
863
864         return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
865                           STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
866                           snk_map, edge_map, 0);
867 }
868
869 /* Graph Topology
870  * nodes per stage:     4
871  * stages:              3
872  * src:                 1
873  * sink:                1
874  */
875 static inline int
876 graph_init_reverse_tree(void)
877 {
878         uint8_t edge_map[][4][4] = {
879                 {
880                         {25, 25, 25, 25},
881                         {25, 25, 25, 25},
882                         {25, 25, 25, 25},
883                         {25, 25, 25, 25}
884                 },
885                 {
886                         {33, 33, 33, 33},
887                         {33, 33, 33, 33},
888                         {34, 34, 34, 34},
889                         {0, 0, 0, 0}
890                 },
891                 {
892                         {50, 50, 50, 0},
893                         {50, 50, 50, 0},
894                         {0, 0, 0, 0},
895                         {0, 0, 0, 0}
896                 },
897         };
898         uint8_t src_map[][4] = { {25, 25, 25, 25} };
899         uint8_t snk_map[][1] = { {100}, {100}, {0}, {0} };
900
901         return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
902                           STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
903                           snk_map, edge_map, 0);
904 }
905
906 /* Graph Topology
907  * nodes per stage:     4
908  * stages:              5
909  * src:                 4
910  * sink:                4
911  */
912 static inline int
913 graph_init_parallel_tree(void)
914 {
915         uint8_t edge_map[][4][4] = {
916                 {
917                         {100, 0, 0, 0},
918                         {0, 100, 0, 0},
919                         {0, 0, 100, 0},
920                         {0, 0, 0, 100}
921                 },
922                 {
923                         {100, 0, 0, 0},
924                         {0, 100, 0, 0},
925                         {0, 0, 100, 0},
926                         {0, 0, 0, 100}
927                 },
928                 {
929                         {100, 0, 0, 0},
930                         {0, 100, 0, 0},
931                         {0, 0, 100, 0},
932                         {0, 0, 0, 100}
933                 },
934                 {
935                         {100, 0, 0, 0},
936                         {0, 100, 0, 0},
937                         {0, 0, 100, 0},
938                         {0, 0, 0, 100}
939                 },
940                 {
941                         {100, 0, 0, 0},
942                         {0, 100, 0, 0},
943                         {0, 0, 100, 0},
944                         {0, 0, 0, 100}
945                 },
946         };
947         uint8_t src_map[][4] = {
948                 {100, 0, 0, 0},
949                 {0, 100, 0, 0},
950                 {0, 0, 100, 0},
951                 {0, 0, 0, 100}
952         };
953         uint8_t snk_map[][4] = {
954                 {100, 0, 0, 0},
955                 {0, 100, 0, 0},
956                 {0, 0, 100, 0},
957                 {0, 0, 0, 100}
958         };
959
960         return graph_init("graph_parallel", SOURCES(src_map), SINKS(snk_map),
961                           STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
962                           snk_map, edge_map, 0);
963 }
964
965 /** Graph Creation cheat sheet
966  *  edge_map -> dictates graph flow from worker stage 0 to worker stage n-1.
967  *  src_map  -> dictates source nodes enqueue percentage to worker stage 0.
968  *  snk_map  -> dictates stage n-1 enqueue percentage to sink.
969  *
970  *  Layout:
971  *  edge_map[<nb_stages>][<nodes_per_stg>][<nodes_in_nxt_stg = nodes_per_stg>]
972  *  src_map[<nb_sources>][<nodes_in_stage0 = nodes_per_stage>]
973  *  snk_map[<nodes_in_stage(n-1) = nodes_per_stage>][<nb_sinks>]
974  *
975  *  The last array dictates the percentage of received objs to enqueue to next
976  *  stage.
977  *
978  *  Note: edge_map[][0][] will always be unused as it will receive from source
979  *
980  *  Example:
981  *      Graph:
982  *      http://bit.ly/2PqbqOy
983  *      Each stage(n) connects to all nodes in the next stage in decreasing
984  *      order.
985  *      Since we can't resize the edge_map dynamically we get away by creating
986  *      dummy nodes and assigning 0 percentages.
987  *      Max nodes across all stages = 4
988  *      stages = 3
989  *      nb_src = 1
990  *      nb_snk = 1
991  *                         // Stages
992  *      edge_map[][4][4] = {
993  *              // Nodes per stage
994  *              {
995  *                  {25, 25, 25, 25},
996  *                  {25, 25, 25, 25},
997  *                  {25, 25, 25, 25},
998  *                  {25, 25, 25, 25}
999  *              },      // This will be unused.
1000  *              {
1001  *                  // Nodes enabled in current stage + prev stage enq %
1002  *                  {33, 33, 33, 33},
1003  *                  {33, 33, 33, 33},
1004  *                  {34, 34, 34, 34},
1005  *                  {0, 0, 0, 0}
1006  *              },
1007  *              {
1008  *                  {50, 50, 50, 0},
1009  *                  {50, 50, 50, 0},
1010  *                  {0, 0, 0, 0},
1011  *                  {0, 0, 0, 0}
1012  *              },
1013  *      };
1014  *      Above, each stage tells how much it should receive from previous except
1015  *      from stage_0.
1016  *
1017  *      src_map[][4] = { {25, 25, 25, 25} };
1018  *      Here, we tell each source the % it has to send to stage_0 nodes. In
1019  *      case we want 2 source node we can declare as
1020  *      src_map[][4] = { {25, 25, 25, 25}, {25, 25, 25, 25} };
1021  *
1022  *      snk_map[][1] = { {100}, {100}, {0}, {0} }
1023  *      Here, we tell stage - 1 nodes how much to enqueue to sink_0.
1024  *      If we have 2 sinks we can do as follows
1025  *      snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
1026  */
1027
1028 static struct unit_test_suite graph_perf_testsuite = {
1029         .suite_name = "Graph library performance test suite",
1030         .setup = graph_perf_setup,
1031         .teardown = graph_perf_teardown,
1032         .unit_test_cases = {
1033                 TEST_CASE_ST(graph_init_hr, graph_fini,
1034                              graph_hr_4s_1n_1src_1snk),
1035                 TEST_CASE_ST(graph_init_hr_brst_one, graph_fini,
1036                              graph_hr_4s_1n_1src_1snk_brst_one),
1037                 TEST_CASE_ST(graph_init_hr_multi_src, graph_fini,
1038                              graph_hr_4s_1n_2src_1snk),
1039                 TEST_CASE_ST(graph_init_hr_multi_snk, graph_fini,
1040                              graph_hr_4s_1n_1src_2snk),
1041                 TEST_CASE_ST(graph_init_tree, graph_fini,
1042                              graph_tree_4s_4n_1src_4snk),
1043                 TEST_CASE_ST(graph_init_reverse_tree, graph_fini,
1044                              graph_reverse_tree_3s_4n_1src_1snk),
1045                 TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
1046                              graph_parallel_tree_5s_4n_4src_4snk),
1047                 TEST_CASES_END(), /**< NULL terminate unit test array */
1048         },
1049 };
1050
1051 static int
1052 test_graph_perf_func(void)
1053 {
1054         return unit_test_suite_runner(&graph_perf_testsuite);
1055 }
1056
1057 REGISTER_TEST_COMMAND(graph_perf_autotest, test_graph_perf_func);