LCOV - code coverage report
Current view: top level - app/test - test_distributor_perf.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 1 92 1.1 %
Date: 2025-02-01 18:54:23 Functions: 1 7 14.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 58 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2010-2017 Intel Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #include "test.h"
       6                 :            : 
       7                 :            : #include <unistd.h>
       8                 :            : #include <string.h>
       9                 :            : #include <rte_mempool.h>
      10                 :            : #include <rte_cycles.h>
      11                 :            : #include <rte_common.h>
      12                 :            : #include <rte_mbuf.h>
      13                 :            : 
      14                 :            : #ifdef RTE_EXEC_ENV_WINDOWS
      15                 :            : static int
      16                 :            : test_distributor_perf(void)
      17                 :            : {
      18                 :            :         printf("distributor perf not supported on Windows, skipping test\n");
      19                 :            :         return TEST_SKIPPED;
      20                 :            : }
      21                 :            : 
      22                 :            : #else
      23                 :            : 
      24                 :            : #include <rte_distributor.h>
      25                 :            : #include <rte_pause.h>
      26                 :            : 
      27                 :            : #define ITER_POWER_CL 25 /* log 2 of how many iterations  for Cache Line test */
      28                 :            : #define ITER_POWER 21 /* log 2 of how many iterations we do when timing. */
      29                 :            : #define BURST 64
      30                 :            : #define BIG_BATCH 1024
      31                 :            : 
      32                 :            : /* static vars - zero initialized by default */
      33                 :            : static volatile int quit;
      34                 :            : static volatile RTE_ATOMIC(unsigned int) worker_idx;
      35                 :            : 
      36                 :            : struct __rte_cache_aligned worker_stats {
      37                 :            :         volatile unsigned handled_packets;
      38                 :            : };
      39                 :            : static struct worker_stats worker_stats[RTE_MAX_LCORE];
      40                 :            : 
      41                 :            : /*
      42                 :            :  * worker thread used for testing the time to do a round-trip of a cache
      43                 :            :  * line between two cores and back again
      44                 :            :  */
      45                 :            : static int
      46                 :          0 : flip_bit(volatile uint64_t *arg)
      47                 :            : {
      48                 :            :         uint64_t old_val = 0;
      49         [ #  # ]:          0 :         while (old_val != 2) {
      50         [ #  # ]:          0 :                 while (!*arg)
      51                 :            :                         rte_pause();
      52                 :          0 :                 old_val = *arg;
      53                 :          0 :                 *arg = 0;
      54                 :            :         }
      55                 :          0 :         return 0;
      56                 :            : }
      57                 :            : 
      58                 :            : /*
      59                 :            :  * test case to time the number of cycles to round-trip a cache line between
      60                 :            :  * two cores and back again.
      61                 :            :  */
      62                 :            : static void
      63                 :          0 : time_cache_line_switch(void)
      64                 :            : {
      65                 :            :         /* allocate a full cache line for data, we use only first byte of it */
      66                 :            :         uint64_t data[RTE_CACHE_LINE_SIZE*3 / sizeof(uint64_t)];
      67                 :            : 
      68                 :          0 :         unsigned int i, workerid = rte_get_next_lcore(rte_lcore_id(), 0, 0);
      69                 :            :         volatile uint64_t *pdata = &data[0];
      70                 :          0 :         *pdata = 1;
      71                 :          0 :         rte_eal_remote_launch((lcore_function_t *)flip_bit, &data[0], workerid);
      72         [ #  # ]:          0 :         while (*pdata)
      73                 :            :                 rte_pause();
      74                 :            : 
      75                 :            :         const uint64_t start_time = rte_rdtsc();
      76         [ #  # ]:          0 :         for (i = 0; i < (1 << ITER_POWER_CL); i++) {
      77         [ #  # ]:          0 :                 while (*pdata)
      78                 :            :                         rte_pause();
      79                 :          0 :                 *pdata = 1;
      80                 :            :         }
      81                 :            :         const uint64_t end_time = rte_rdtsc();
      82                 :            : 
      83         [ #  # ]:          0 :         while (*pdata)
      84                 :            :                 rte_pause();
      85                 :          0 :         *pdata = 2;
      86                 :          0 :         rte_eal_wait_lcore(workerid);
      87                 :            :         printf("==== Cache line switch test ===\n");
      88                 :          0 :         printf("Time for %u iterations = %"PRIu64" ticks\n", (1<<ITER_POWER_CL),
      89                 :            :                         end_time-start_time);
      90                 :          0 :         printf("Ticks per iteration = %"PRIu64"\n\n",
      91                 :            :                         (end_time-start_time) >> ITER_POWER_CL);
      92                 :          0 : }
      93                 :            : 
      94                 :            : /*
      95                 :            :  * returns the total count of the number of packets handled by the worker
      96                 :            :  * functions given below.
      97                 :            :  */
      98                 :            : static unsigned
      99                 :            : total_packet_count(void)
     100                 :            : {
     101                 :            :         unsigned i, count = 0;
     102   [ #  #  #  #  :          0 :         for (i = 0; i < worker_idx; i++)
                   #  # ]
     103                 :          0 :                 count += worker_stats[i].handled_packets;
     104                 :            :         return count;
     105                 :            : }
     106                 :            : 
     107                 :            : /* resets the packet counts for a new test */
     108                 :            : static void
     109                 :            : clear_packet_count(void)
     110                 :            : {
     111                 :            :         memset(&worker_stats, 0, sizeof(worker_stats));
     112                 :            : }
     113                 :            : 
     114                 :            : /*
     115                 :            :  * This is the basic worker function for performance tests.
     116                 :            :  * it does nothing but return packets and count them.
     117                 :            :  */
     118                 :            : static int
     119                 :          0 : handle_work(void *arg)
     120                 :            : {
     121                 :            :         struct rte_distributor *d = arg;
     122                 :            :         unsigned int num = 0;
     123                 :            :         int i;
     124                 :          0 :         unsigned int id = rte_atomic_fetch_add_explicit(&worker_idx, 1, rte_memory_order_relaxed);
     125                 :            :         alignas(RTE_CACHE_LINE_SIZE) struct rte_mbuf *buf[8];
     126                 :            : 
     127         [ #  # ]:          0 :         for (i = 0; i < 8; i++)
     128                 :          0 :                 buf[i] = NULL;
     129                 :            : 
     130                 :          0 :         num = rte_distributor_get_pkt(d, id, buf, buf, num);
     131         [ #  # ]:          0 :         while (!quit) {
     132                 :          0 :                 worker_stats[id].handled_packets += num;
     133                 :          0 :                 num = rte_distributor_get_pkt(d, id, buf, buf, num);
     134                 :            :         }
     135                 :          0 :         worker_stats[id].handled_packets += num;
     136                 :          0 :         rte_distributor_return_pkt(d, id, buf, num);
     137                 :          0 :         return 0;
     138                 :            : }
     139                 :            : 
     140                 :            : /*
     141                 :            :  * This basic performance test just repeatedly sends in 32 packets at a time
     142                 :            :  * to the distributor and verifies at the end that we got them all in the worker
     143                 :            :  * threads and finally how long per packet the processing took.
     144                 :            :  */
     145                 :            : static inline int
     146         [ #  # ]:          0 : perf_test(struct rte_distributor *d, struct rte_mempool *p)
     147                 :            : {
     148                 :            :         unsigned int i;
     149                 :            :         uint64_t start, end;
     150                 :            :         struct rte_mbuf *bufs[BURST];
     151                 :            : 
     152                 :            :         clear_packet_count();
     153         [ #  # ]:          0 :         if (rte_mempool_get_bulk(p, (void *)bufs, BURST) != 0) {
     154                 :            :                 printf("Error getting mbufs from pool\n");
     155                 :          0 :                 return -1;
     156                 :            :         }
     157                 :            :         /* ensure we have different hash value for each pkt */
     158         [ #  # ]:          0 :         for (i = 0; i < BURST; i++)
     159                 :          0 :                 bufs[i]->hash.usr = i;
     160                 :            : 
     161                 :            :         start = rte_rdtsc();
     162         [ #  # ]:          0 :         for (i = 0; i < (1<<ITER_POWER); i++)
     163                 :          0 :                 rte_distributor_process(d, bufs, BURST);
     164                 :            :         end = rte_rdtsc();
     165                 :            : 
     166                 :            :         do {
     167                 :          0 :                 usleep(100);
     168                 :          0 :                 rte_distributor_process(d, NULL, 0);
     169         [ #  # ]:          0 :         } while (total_packet_count() < (BURST << ITER_POWER));
     170                 :            : 
     171                 :          0 :         rte_distributor_clear_returns(d);
     172                 :            : 
     173                 :          0 :         printf("Time per burst:  %"PRIu64"\n", (end - start) >> ITER_POWER);
     174                 :          0 :         printf("Time per packet: %"PRIu64"\n\n",
     175                 :            :                         ((end - start) >> ITER_POWER)/BURST);
     176                 :            :         rte_mempool_put_bulk(p, (void *)bufs, BURST);
     177                 :            : 
     178         [ #  # ]:          0 :         for (i = 0; i < rte_lcore_count() - 1; i++)
     179                 :            :                 printf("Worker %u handled %u packets\n", i,
     180                 :          0 :                                 worker_stats[i].handled_packets);
     181                 :            :         printf("Total packets: %u (%x)\n", total_packet_count(),
     182                 :            :                         total_packet_count());
     183                 :            :         printf("=== Perf test done ===\n\n");
     184                 :            : 
     185                 :          0 :         return 0;
     186                 :            : }
     187                 :            : 
     188                 :            : /* Useful function which ensures that all worker functions terminate */
     189                 :            : static void
     190                 :          0 : quit_workers(struct rte_distributor *d, struct rte_mempool *p)
     191                 :            : {
     192         [ #  # ]:          0 :         const unsigned int num_workers = rte_lcore_count() - 1;
     193                 :            :         unsigned int i;
     194                 :            :         struct rte_mbuf *bufs[RTE_MAX_LCORE];
     195                 :            : 
     196                 :            :         rte_mempool_get_bulk(p, (void *)bufs, num_workers);
     197                 :            : 
     198                 :          0 :         quit = 1;
     199         [ #  # ]:          0 :         for (i = 0; i < num_workers; i++) {
     200                 :          0 :                 bufs[i]->hash.usr = i << 1;
     201                 :          0 :                 rte_distributor_process(d, &bufs[i], 1);
     202                 :            :         }
     203                 :            : 
     204                 :            :         rte_mempool_put_bulk(p, (void *)bufs, num_workers);
     205                 :            : 
     206                 :          0 :         rte_distributor_process(d, NULL, 0);
     207                 :          0 :         rte_distributor_flush(d);
     208                 :          0 :         rte_eal_mp_wait_lcore();
     209                 :          0 :         quit = 0;
     210                 :          0 :         worker_idx = 0;
     211                 :          0 : }
     212                 :            : 
     213                 :            : static int
     214                 :          0 : test_distributor_perf(void)
     215                 :            : {
     216                 :            :         static struct rte_distributor *ds;
     217                 :            :         static struct rte_distributor *db;
     218                 :            :         static struct rte_mempool *p;
     219                 :            : 
     220         [ #  # ]:          0 :         if (rte_lcore_count() < 2) {
     221                 :            :                 printf("Not enough cores for distributor_perf_autotest, expecting at least 2\n");
     222                 :          0 :                 return TEST_SKIPPED;
     223                 :            :         }
     224                 :            : 
     225                 :            :         /* first time how long it takes to round-trip a cache line */
     226                 :          0 :         time_cache_line_switch();
     227                 :            : 
     228         [ #  # ]:          0 :         if (ds == NULL) {
     229                 :          0 :                 ds = rte_distributor_create("Test_perf", rte_socket_id(),
     230                 :          0 :                                 rte_lcore_count() - 1,
     231                 :            :                                 RTE_DIST_ALG_SINGLE);
     232         [ #  # ]:          0 :                 if (ds == NULL) {
     233                 :            :                         printf("Error creating distributor\n");
     234                 :          0 :                         return -1;
     235                 :            :                 }
     236                 :            :         } else {
     237                 :          0 :                 rte_distributor_clear_returns(ds);
     238                 :            :         }
     239                 :            : 
     240         [ #  # ]:          0 :         if (db == NULL) {
     241                 :          0 :                 db = rte_distributor_create("Test_burst", rte_socket_id(),
     242                 :          0 :                                 rte_lcore_count() - 1,
     243                 :            :                                 RTE_DIST_ALG_BURST);
     244         [ #  # ]:          0 :                 if (db == NULL) {
     245                 :            :                         printf("Error creating burst distributor\n");
     246                 :          0 :                         return -1;
     247                 :            :                 }
     248                 :            :         } else {
     249                 :          0 :                 rte_distributor_clear_returns(db);
     250                 :            :         }
     251                 :            : 
     252                 :          0 :         const unsigned nb_bufs = (511 * rte_lcore_count()) < BIG_BATCH ?
     253         [ #  # ]:          0 :                         (BIG_BATCH * 2) - 1 : (511 * rte_lcore_count());
     254         [ #  # ]:          0 :         if (p == NULL) {
     255                 :          0 :                 p = rte_pktmbuf_pool_create("DPT_MBUF_POOL", nb_bufs, BURST,
     256                 :          0 :                         0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
     257         [ #  # ]:          0 :                 if (p == NULL) {
     258                 :            :                         printf("Error creating mempool\n");
     259                 :          0 :                         return -1;
     260                 :            :                 }
     261                 :            :         }
     262                 :            : 
     263                 :            :         printf("=== Performance test of distributor (single mode) ===\n");
     264                 :          0 :         rte_eal_mp_remote_launch(handle_work, ds, SKIP_MAIN);
     265         [ #  # ]:          0 :         if (perf_test(ds, p) < 0)
     266                 :            :                 return -1;
     267                 :          0 :         quit_workers(ds, p);
     268                 :            : 
     269                 :            :         printf("=== Performance test of distributor (burst mode) ===\n");
     270                 :          0 :         rte_eal_mp_remote_launch(handle_work, db, SKIP_MAIN);
     271         [ #  # ]:          0 :         if (perf_test(db, p) < 0)
     272                 :            :                 return -1;
     273                 :          0 :         quit_workers(db, p);
     274                 :            : 
     275                 :          0 :         return 0;
     276                 :            : }
     277                 :            : 
     278                 :            : #endif /* !RTE_EXEC_ENV_WINDOWS */
     279                 :            : 
     280                 :        252 : REGISTER_PERF_TEST(distributor_perf_autotest, test_distributor_perf);

Generated by: LCOV version 1.14