LCOV - code coverage report
Current view: top level - tango - bench_frag_tx.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 168 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 1 0.0 %

          Line data    Source code
       1             : #include "fd_tango.h"
       2             : 
       3             : #if FD_HAS_X86
       4             : #include <x86intrin.h>
       5             : #endif
       6             : 
       7             : #if FD_HAS_HOSTED && FD_HAS_AVX
       8             : 
       9             : /* FIXME: UPDATE TO TIME BASED HOUSEKEEPING */
      10             : 
      11             : #include <math.h> /* For expm1f */
      12             : 
      13             : FD_STATIC_ASSERT( FD_CHUNK_SZ==64UL, unit_test );
      14             : 
      15           0 : #define RX_MAX (128UL) /* Max _reliable_ (arb unreliable) */
      16             : 
      17             : static uchar  fctl_mem[ FD_FCTL_FOOTPRINT( RX_MAX ) ] __attribute__((aligned(FD_FCTL_ALIGN)));
      18             : static char * _fseq[ RX_MAX ];
      19             : 
      20             : #define FD_CNC_DIAG_IN_BACKP   (0UL)
      21             : #define FD_CNC_DIAG_BACKP_CNT  (1UL)
      22             : 
      23             : #define FD_FSEQ_DIAG_PUB_CNT   (0UL)
      24             : #define FD_FSEQ_DIAG_PUB_SZ    (1UL)
      25             : #define FD_FSEQ_DIAG_FILT_CNT  (2UL)
      26             : #define FD_FSEQ_DIAG_FILT_SZ   (3UL)
      27             : #define FD_FSEQ_DIAG_OVRNP_CNT (4UL)
      28             : #define FD_FSEQ_DIAG_OVRNR_CNT (5UL)
      29             : #define FD_FSEQ_DIAG_SLOW_CNT  (6UL)
      30             : 
      31             : int
      32             : main( int     argc,
      33           0 :       char ** argv ) {
      34           0 :   fd_boot( &argc, &argv );
      35             : 
      36           0 :   char const * _cnc    = fd_env_strip_cmdline_cstr ( &argc, &argv, "--cnc",    NULL, NULL                 ); /* (req) cnc */
      37           0 :   char const * _mcache = fd_env_strip_cmdline_cstr ( &argc, &argv, "--mcache", NULL, NULL                 ); /* (req) mcache */
      38           0 :   char const * _dcache = fd_env_strip_cmdline_cstr ( &argc, &argv, "--dcache", NULL, NULL                 ); /* (req) dcache */
      39           0 :   char const * _fseqs  = fd_env_strip_cmdline_cstr ( &argc, &argv, "--fseqs",  NULL, ""                   ); /* (opt) rx fseqs */
      40           0 :   char const * _init   = fd_env_strip_cmdline_cstr ( &argc, &argv, "--init",   NULL, NULL                 ); /* (opt) init seq */
      41           0 :   ulong        tx_idx  = fd_env_strip_cmdline_ulong( &argc, &argv, "--tx-idx", NULL, 0UL                  ); /* (opt) origin */
      42           0 :   uint         seed    = fd_env_strip_cmdline_uint ( &argc, &argv, "--seed",   NULL, (uint)fd_tickcount() ); /* (opt) rng seed */
      43           0 :   int          lazy    = fd_env_strip_cmdline_int  ( &argc, &argv, "--lazy",   NULL, 7                    ); /* (opt) lazyiness */
      44             : 
      45           0 :   if( FD_UNLIKELY( !_cnc                         ) ) FD_LOG_ERR(( "--cnc not specified" ));
      46           0 :   if( FD_UNLIKELY( !_mcache                      ) ) FD_LOG_ERR(( "--mcache not specified" ));
      47           0 :   if( FD_UNLIKELY( !_dcache                      ) ) FD_LOG_ERR(( "--dcache not specified" ));
      48           0 :   if( FD_UNLIKELY( tx_idx>=FD_FRAG_META_ORIG_MAX ) ) FD_LOG_ERR(( "--tx-idx too large" ));
      49             : 
      50           0 :   ulong rx_cnt = fd_cstr_tokenize( _fseq, RX_MAX, (char *)_fseqs, ',' ); /* Note: argv isn't const to okay to cast away const */
      51           0 :   if( FD_UNLIKELY( rx_cnt>RX_MAX ) ) FD_LOG_ERR(( "--rx-cnt too large for this unit-test" ));
      52             : 
      53             :   /* burst_avg is the average number of synthetic payload bytes in a
      54             :      packet burst (0 min, exponentially distributed).  A burst's data
      55             :      bytes are sent as zero or more packets containing pkt_payload_max
      56             :      bytes followed by a one packet containing [1,pkt_payload_max]
      57             :      bytes.  All the packets in a burst are sent in rapid succession.
      58             :      Packets have a constant number of bytes overhead for various
      59             :      headers and footers (pkt_framing).
      60             : 
      61             :      E.g. pkt_framing = 70, pkt_payload_max = 1472 would describe an
      62             :      Ethernet link that runs UDP / IP4 / VLAN tagged non-Jumbo Ethernet
      63             :      packets.  pkt_framing = 70 comes from from 8 byte preamble + 14
      64             :      byte Ethernet header + 4 byte VLAN tag + 20 byte IP4 header
      65             :      (without options) + 8 byte UDP header + 4 byte frame-check-sequence
      66             :      + 12 byte interframe gap, 1472 comes from a 1500 byte MTU (maximum
      67             :      Ethernet payload size) - 20 byte IP4 header (without options) - 8
      68             :      byte UDP header. */
      69             : 
      70           0 : # define RANDOMIZE_BURSTS 0
      71           0 :   float burst_avg       = fd_env_strip_cmdline_float( &argc, &argv, "--burst-avg",       NULL, 1472.f );
      72           0 :   ulong pkt_payload_max = fd_env_strip_cmdline_ulong( &argc, &argv, "--pkt-payload-max", NULL, 1472UL );
      73           0 :   ulong pkt_framing     = fd_env_strip_cmdline_ulong( &argc, &argv, "--pkt-framing",     NULL,   70UL );
      74             : 
      75           0 :   FD_LOG_NOTICE(( "Configuring synthetic load (--burst-avg %g B --pkt-framing %lu B --pkt-payload-max %lu B,",
      76           0 :                   (double)burst_avg, pkt_framing, pkt_payload_max ));
      77             : 
      78             :   /* 2.1e17 ~ LONG_MAX / 43.668 is such that an exponentially
      79             :      distributed random from the util/rng API with an average in
      80             :      (0,2.1e17) will practically never overflow when rounded to a long. */
      81             : 
      82           0 :   if( FD_UNLIKELY( !((0.f<burst_avg) & (burst_avg<2.1e17f)) ) ) FD_LOG_ERR(( "--burst-avg out of range" ));
      83             : 
      84           0 :   if( FD_UNLIKELY( !pkt_payload_max ) ) FD_LOG_ERR(( "Zero --pkt-payload-max" ));
      85             : 
      86           0 :   ulong pkt_max = pkt_framing + pkt_payload_max;
      87           0 :   if( FD_UNLIKELY( (pkt_max<pkt_framing) | (pkt_max>(ulong)USHORT_MAX) ) )
      88           0 :     FD_LOG_ERR(( "Too large --pkt-framing + --pkt-payload-max" ));
      89             : 
      90           0 :   FD_LOG_NOTICE(( "Creating rng --seed %u", seed ));
      91             : 
      92           0 :   fd_rng_t _rng[1]; fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, seed, 0UL ) );
      93             : 
      94           0 :   FD_LOG_NOTICE(( "Joining to --cnc %s", _cnc ));
      95             : 
      96           0 :   fd_cnc_t * cnc = fd_cnc_join( fd_wksp_map( _cnc ) );
      97           0 :   if( FD_UNLIKELY( !cnc                      ) ) FD_LOG_ERR(( "join failed" ));
      98           0 :   if( FD_UNLIKELY( fd_cnc_app_sz( cnc )<16UL ) ) FD_LOG_ERR(( "cnc app sz must be at least 16" ));
      99             : 
     100           0 :   ulong * cnc_diag = (ulong *)fd_cnc_app_laddr( cnc );
     101           0 :   int     in_backp = 1;
     102           0 :   FD_VOLATILE( cnc_diag[ FD_CNC_DIAG_IN_BACKP  ] ) = 1UL;
     103           0 :   FD_VOLATILE( cnc_diag[ FD_CNC_DIAG_BACKP_CNT ] ) = 0UL;
     104             : 
     105           0 :   FD_LOG_NOTICE(( "Joining to --mcache %s", _mcache ));
     106             : 
     107           0 :   fd_frag_meta_t * mcache = fd_mcache_join( fd_wksp_map( _mcache ) );
     108           0 :   if( FD_UNLIKELY( !mcache ) ) FD_LOG_ERR(( "join failed" ));
     109             : 
     110           0 :   ulong   depth = fd_mcache_depth    ( mcache );
     111           0 :   ulong * sync  = fd_mcache_seq_laddr( mcache );
     112             : 
     113           0 :   ulong seq = _init ? fd_cstr_to_ulong( _init ) : fd_mcache_seq_query( sync );
     114             : 
     115           0 :   FD_LOG_NOTICE(( "Joining to --dcache %s", _dcache ));
     116             : 
     117           0 :   uchar * dcache = fd_dcache_join( fd_wksp_map( _dcache ) );
     118           0 :   if( FD_UNLIKELY( !dcache ) ) FD_LOG_ERR(( "join failed" ));
     119             : 
     120           0 :   void * base = fd_wksp_containing( dcache );
     121           0 :   if( FD_UNLIKELY( !base ) ) FD_LOG_ERR(( "fd_wksp_containing failed" ));
     122             : 
     123           0 :   if( FD_UNLIKELY( !fd_dcache_compact_is_safe( base, dcache, pkt_max, depth ) ) )
     124           0 :     FD_LOG_ERR(( "--dcache not compatible with wksp base, --pkt-framing, --pkt-payload-max and --mcache depth" ));
     125             : 
     126           0 :   ulong chunk0 = fd_dcache_compact_chunk0( base, dcache );
     127           0 :   ulong wmark  = fd_dcache_compact_wmark ( base, dcache, pkt_max );
     128           0 :   ulong chunk  = chunk0; /* FIXME: command line / auto recover this from dcache app region for clean dcache recovery too */
     129             : 
     130           0 :   FD_LOG_NOTICE(( "Configuring flow control (rx_cnt %lu)", rx_cnt ));
     131             : 
     132           0 :   fd_fctl_t * fctl = fd_fctl_join( fd_fctl_new( fctl_mem, rx_cnt ) );
     133           0 :   if( FD_UNLIKELY( !fctl ) ) FD_LOG_ERR(( "join failed" ));
     134             : 
     135           0 :   for( ulong rx_idx=0UL; rx_idx<rx_cnt; rx_idx++ ) {
     136             : 
     137           0 :     FD_LOG_NOTICE(( "Joining to reliable rx %lu fseq %s", rx_idx, _fseq[ rx_idx ] ));
     138           0 :     ulong * fseq = fd_fseq_join( fd_wksp_map( _fseq[ rx_idx ] ) );
     139           0 :     if( FD_UNLIKELY( !fseq ) ) FD_LOG_ERR(( "join failed" ));
     140           0 :     ulong * fseq_diag = (ulong *)fd_fseq_app_laddr( fseq );
     141             : 
     142           0 :     if( FD_UNLIKELY( !fd_fctl_cfg_rx_add( fctl, depth, fseq, &fseq_diag[ FD_FSEQ_DIAG_SLOW_CNT ] ) ) )
     143           0 :       FD_LOG_ERR(( "fd_fctl_cfg_rx_add failed" ));
     144             : 
     145           0 :     FD_VOLATILE( fseq_diag[ FD_FSEQ_DIAG_SLOW_CNT ] ) = 0UL;
     146           0 :   }
     147             : 
     148             :   /* cr_burst is 1 because we only send at most 1 fragment metadata
     149             :      between checking cr_avail.  We use defaults for cr_max, cr_resume
     150             :      and cr_refill. */
     151           0 :   if( FD_UNLIKELY( !fd_fctl_cfg_done( fctl, 1UL, 0UL, 0UL, 0UL ) ) ) FD_LOG_ERR(( "fd_fctl_cfg_done failed" ));
     152           0 :   FD_LOG_NOTICE(( "cr_burst %lu cr_max %lu cr_resume %lu cr_refill %lu",
     153           0 :                   fd_fctl_cr_burst( fctl ), fd_fctl_cr_max( fctl ), fd_fctl_cr_resume( fctl ), fd_fctl_cr_refill( fctl ) ));
     154             : 
     155           0 :   ulong cr_avail = 0UL;
     156             : 
     157           0 :   FD_LOG_NOTICE(( "Running --tx-idx %lu --init %lu (%s) --lazy %i", tx_idx, seq, _init ? "manual" : "auto", lazy ));
     158             : 
     159           0 :   ulong async_min = 1UL << lazy;
     160           0 :   ulong async_rem = 1UL; /* Do housekeeping on the first iteration */
     161             : 
     162           0 :   long  then = fd_log_wallclock();
     163           0 :   ulong iter = 0UL;
     164             : 
     165           0 :   int ctl_som = 1;
     166             : 
     167           0 :   ulong burst_rem;
     168             : # if RANDOMIZE_BURSTS
     169             :   do { burst_rem = (ulong)(long)(0.5f + burst_avg*fd_rng_float_exp( rng )); } while( FD_UNLIKELY( !burst_rem ) );
     170             : # else
     171           0 :   ulong _burst_avg = (ulong)(ulong)burst_avg;
     172           0 :   burst_rem = _burst_avg;
     173           0 : # endif
     174             : 
     175           0 :   fd_cnc_signal( cnc, FD_CNC_SIGNAL_RUN );
     176           0 :   for(;;) {
     177             : 
     178             :     /* Do housekeeping in the background */
     179           0 :     if( FD_UNLIKELY( !async_rem ) ) {
     180             : 
     181             :       /* Send synchronization info */
     182           0 :       fd_mcache_seq_update( sync, seq );
     183             : 
     184             :       /* Send diagnostic info */
     185           0 :       long now = fd_log_wallclock();
     186           0 :       fd_cnc_heartbeat( cnc, now );
     187             : 
     188           0 :       long dt = now - then;
     189           0 :       if( FD_UNLIKELY( dt > (long)1e9 ) ) {
     190           0 :         float mfps = (1e3f*(float)iter) / (float)dt;
     191           0 :         FD_LOG_NOTICE(( "%7.3f Mfrag/s tx (in_backp %lu backp_cnt %lu)", (double)mfps,
     192           0 :                         FD_VOLATILE_CONST( cnc_diag[ FD_CNC_DIAG_IN_BACKP  ] ),
     193           0 :                         FD_VOLATILE_CONST( cnc_diag[ FD_CNC_DIAG_BACKP_CNT ] ) ));
     194           0 :         for( ulong rx_idx=0UL; rx_idx<rx_cnt; rx_idx++ ) {
     195           0 :           ulong * slow = fd_fctl_rx_slow_laddr( fctl, rx_idx );
     196           0 :           FD_LOG_NOTICE(( "slow%lu %lu", rx_idx, FD_VOLATILE_CONST( *slow ) ));
     197           0 :           FD_VOLATILE( *slow ) = 0UL;
     198           0 :         }
     199           0 :         FD_VOLATILE( cnc_diag[ FD_CNC_DIAG_BACKP_CNT ] ) = 0UL;
     200           0 :         then = now;
     201           0 :         iter = 0UL;
     202           0 :       }
     203             : 
     204             :       /* Receive command-and-control signals */
     205           0 :       ulong s = fd_cnc_signal_query( cnc );
     206           0 :       if( FD_UNLIKELY( s!=FD_CNC_SIGNAL_RUN ) ) {
     207           0 :         if( FD_LIKELY( s==FD_CNC_SIGNAL_HALT ) ) break;
     208           0 :         char buf[ FD_CNC_SIGNAL_CSTR_BUF_MAX ];
     209           0 :         FD_LOG_WARNING(( "Unexpected signal %s (%lu) received; trying to resume", fd_cnc_signal_cstr( s, buf ), s ));
     210           0 :         fd_cnc_signal( cnc, FD_CNC_SIGNAL_RUN );
     211           0 :       }
     212             : 
     213             :       /* Receive flow control credits */
     214           0 :       cr_avail = fd_fctl_tx_cr_update( fctl, cr_avail, seq );
     215           0 :       if( FD_UNLIKELY( in_backp ) ) {
     216           0 :         if( FD_LIKELY( cr_avail ) ) {
     217           0 :           FD_VOLATILE( cnc_diag[ FD_CNC_DIAG_IN_BACKP ] ) = 0UL;
     218           0 :           in_backp = 0;
     219           0 :         }
     220           0 :       }
     221             : 
     222             :       /* Reload housekeeping timer */
     223           0 :       async_rem = fd_tempo_async_reload( rng, async_min );
     224           0 :     }
     225           0 :     async_rem--;
     226             : 
     227             :     /* Check if we are backpressured */
     228           0 :     if( FD_UNLIKELY( !cr_avail ) ) {
     229           0 :       if( FD_UNLIKELY( !in_backp ) ) {
     230           0 :         FD_VOLATILE( cnc_diag[ FD_CNC_DIAG_IN_BACKP  ] ) = 0UL;
     231           0 :         FD_VOLATILE( cnc_diag[ FD_CNC_DIAG_BACKP_CNT ] ) = FD_VOLATILE_CONST( cnc_diag[ FD_CNC_DIAG_BACKP_CNT ] ) + 1UL;
     232           0 :         in_backp = 1;
     233           0 :       }
     234           0 :       FD_SPIN_PAUSE();
     235           0 :       continue;
     236           0 :     }
     237             : 
     238             :     /* We are in the process of "receiving" a fragment from the NIC.
     239             :        Compute the details of the synthetic fragment and fill the data
     240             :        region with a suitable test pattern as fast as we can.  Note that
     241             :        the dcache region pointed by chunk through the double cache line
     242             :        region marked by sz is currently not marked as visible and has
     243             :        room for up to "MTU" worth of double chunks free.  So we can
     244             :        write it at our hearts content.  Since we align frag starts to
     245             :        double chunk boundaries, we are safe to tail clobber (which
     246             :        further speeds up test pattern generation as this amortizes loop
     247             :        overhead, flattens tail branches and only write to the dcache in
     248             :        complete double cache lines).  We don't care if this is written
     249             :        atomic or not. */
     250             : 
     251           0 :     ulong frag_sz = fd_ulong_min( burst_rem, pkt_payload_max );
     252           0 :     burst_rem -= frag_sz;
     253           0 :     int ctl_eom = !burst_rem;
     254           0 :     int ctl_err = 0;
     255             : 
     256           0 :     ulong sig    = seq; /* Test pattern */
     257             :   /*ulong chunk  = ... already at location where next packet will be written ...; */
     258           0 :     ulong sz     = pkt_framing + frag_sz;
     259           0 :     ulong ctl    = fd_frag_meta_ctl( tx_idx, ctl_som, ctl_eom, ctl_err );
     260           0 :     ulong tsorig = 0UL;
     261           0 :     ulong tspub  = 0UL;
     262             : 
     263           0 :     uchar * p   = (uchar *)fd_chunk_to_laddr( base, chunk );
     264           0 :     __m256i avx = _mm256_set1_epi64x( (long)seq );
     265           0 :     for( ulong off=0UL; off<sz; off+=128UL ) {
     266           0 :       _mm256_store_si256( (__m256i *)(p     ), avx );
     267           0 :       _mm256_store_si256( (__m256i *)(p+32UL), avx );
     268           0 :       _mm256_store_si256( (__m256i *)(p+64UL), avx );
     269           0 :       _mm256_store_si256( (__m256i *)(p+96UL), avx );
     270           0 :       p += 128UL;
     271           0 :     }
     272             : 
     273             :     /* We just "finished receiving" the next fragment of the burst from
     274             :        the "NIC".  Publish to consumers as frag seq.  This implicitly
     275             :        unpublishes frag seq-depth (cyclic) at the same time. */
     276             : 
     277           0 : #   define PUBLISH_STYLE 0
     278             : 
     279           0 : #   if PUBLISH_STYLE==0 /* Incompatible with WAIT_STYLE==2 */
     280             : 
     281           0 :     fd_mcache_publish( mcache, depth, seq, sig, chunk, sz, ctl, tsorig, tspub );
     282             : 
     283             : #   elif PUBLISH_STYLE==1 /* Incompatible with WAIT_STYLE==2 */
     284             : 
     285             :     fd_mcache_publish_sse( mcache, depth, seq, sig, chunk, sz, ctl, tsorig, tspub );
     286             : 
     287             : #   else /* Compatible with all wait styles, requires target with atomic
     288             :             aligned AVX load/store support */
     289             : 
     290             :     fd_mcache_publish_avx( mcache, depth, seq, sig, chunk, sz, ctl, tsorig, tspub );
     291             : 
     292             : #   endif
     293             : 
     294             :     /* Wind up for the next iteration */
     295             : 
     296           0 :     chunk = fd_dcache_compact_next( chunk, sz, chunk0, wmark );
     297           0 :     seq   = fd_seq_inc( seq, 1UL );
     298           0 :     cr_avail--;
     299           0 :     iter++;
     300             : #   if RANDOMIZE_BURSTS
     301             :     if( FD_UNLIKELY( ctl_eom ) )
     302             :       do burst_rem = (ulong)(long)(0.5f + burst_avg*fd_rng_float_exp( rng )); while( FD_UNLIKELY( !burst_rem ) );
     303             : #   else
     304           0 :     burst_rem = fd_ulong_if( ctl_eom, _burst_avg, burst_rem );
     305           0 : #   endif
     306           0 :     ctl_som = ctl_eom;
     307           0 :   }
     308             : 
     309           0 :   FD_LOG_NOTICE(( "Cleaning up" ));
     310             : 
     311           0 :   while( rx_cnt ) fd_wksp_unmap( fd_fctl_rx_seq_laddr( fctl, --rx_cnt ) );
     312           0 :   fd_fctl_delete( fd_fctl_leave( fctl ) );
     313           0 :   fd_wksp_unmap( fd_dcache_leave( dcache ) );
     314           0 :   fd_wksp_unmap( fd_mcache_leave( mcache ) );
     315           0 :   fd_cnc_signal( cnc, FD_CNC_SIGNAL_BOOT );
     316           0 :   fd_wksp_unmap( fd_cnc_leave( cnc ) );
     317           0 :   fd_rng_delete( fd_rng_leave( rng ) );
     318             : 
     319           0 :   FD_LOG_NOTICE(( "pass" ));
     320           0 :   fd_halt();
     321           0 :   return 0;
     322           0 : }
     323             : 
     324             : #else
     325             : 
     326             : int
     327             : main( int     argc,
     328             :       char ** argv ) {
     329             :   fd_boot( &argc, &argv );
     330             :   FD_LOG_WARNING(( "skip: unit test requires FD_HAS_HOSTED and FD_HAS_AVX capabilities" ));
     331             :   fd_halt();
     332             :   return 0;
     333             : }
     334             : 
     335             : #endif

Generated by: LCOV version 1.14