LCOV - code coverage report
Current view: top level - disco/pack - fd_pack_pacing.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 15 15 100.0 %
Date: 2025-10-27 04:40:00 Functions: 3 6 50.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_pack_fd_pack_pacing_h
       2             : #define HEADER_fd_src_disco_pack_fd_pack_pacing_h
       3             : 
       4             : #include "../../util/bits/fd_bits.h"
       5             : #include <math.h>
       6             : 
       7             : /* One of the keys to packing well is properly pacing CU consumption.
       8             :    Without pacing, pack will end up filling the block with non-ideal
       9             :    transactions.  Since at the current limits, the banks can execute a
      10             :    block worth of CUs in a fraction of the block time, without pacing,
      11             :    any lucrative transactions that arrive at towards the end of a block
      12             :    will have to be delayed until the next block (or another leader's
      13             :    block if it's the last in our rotation). */
      14             : 
      15             : 
      16             : struct fd_pack_pacing_private {
      17             :   /* Start and end time of block in ticks */
      18             :   long t_start;
      19             :   long t_end;
      20             :   /* Number of CUs in the block */
      21             :   float max_cus;
      22             : 
      23             :   float ticks_per_cu;
      24             :   float remaining_cus;
      25             : };
      26             : 
      27             : typedef struct fd_pack_pacing_private fd_pack_pacing_t;
      28             : 
      29             : 
      30             : /* fd_pack_pacing_init begins pacing for a slot which starts at now and
      31             :    ends at t_end (both measured in fd_tickcount() space) and will
      32             :    contain cus CUs.  cus in (0, 2^32). t_end - t_start should be about
      33             :    400ms or less, but must be in (0, 2^32) as well. */
      34             : static inline void
      35             : fd_pack_pacing_init( fd_pack_pacing_t * pacer,
      36             :                      long               t_start,
      37             :                      long               t_end,
      38             :                      float              ticks_per_ns,
      39          34 :                      ulong              max_cus ) {
      40             : 
      41          34 :   pacer->t_start = t_start;
      42          34 :   pacer->t_end   = t_end;
      43             : 
      44             :   /* Time per CU depends on the hardware, the transaction mix, what
      45             :      fraction of the transactions land, etc.  It's hard to just come up
      46             :      with a value, but a small sample says 9 ns/CU is in the right
      47             :      ballpark. */
      48          34 :   pacer->ticks_per_cu = 9.0f * ticks_per_ns;
      49             : 
      50             :   /* Originally, we had all the lines ending 5% before t_end, but the
      51             :      better thing to do is to adjust max_cus up so that the 1 bank line
      52             :      ends 5% before t_end. */
      53          34 :   pacer->max_cus = (float)max_cus + 0.05f * (float)(t_end-t_start)/pacer->ticks_per_cu;
      54          34 :   pacer->remaining_cus = pacer->max_cus;
      55          34 : }
      56             : 
      57             : /* fd_pack_pacing_update_consumed_cus notes that the instantaneous value
      58             :    of consumed CUs may have updated.  pacer must be a local join.
      59             :    consumed_cus should be below the value of max_cus but it's treated as
      60             :    max_cus if it's larger.  Now should be the time (in fd_tickcount
      61             :    space) at which the measurement was taken.  */
      62             : static inline void
      63             : fd_pack_pacing_update_consumed_cus( fd_pack_pacing_t * pacer,
      64             :                                     ulong              consumed_cus,
      65        1368 :                                     long               now ) {
      66             :   /* Keep this function separate so in the future we can learn the
      67             :      ticks_per_cu rate. */
      68        1368 :   (void)now;
      69             :   /* It's possible (but unlikely) that consumed_cus can be greater than
      70             :      max_cus, so clamp the value at 0 */
      71        1368 :   pacer->remaining_cus = fmaxf( pacer->max_cus-(float)consumed_cus, 0.0f );
      72        1368 : }
      73             : 
      74             : 
      75             : /* fd_pack_pacing_enabled_bank_cnt computes how many banks should be
      76             :    active at time `now` (in fd_tickcount space) given the most recent
      77             :    value specified for consumed CUs.  The returned value may be 0, which
      78             :    indicates that no banks should be active at the moment.  It may also
      79             :    be higher than the number of available banks, which should be
      80             :    interpreted as all banks being enabled. */
      81             : FD_FN_PURE static inline ulong
      82             : fd_pack_pacing_enabled_bank_cnt( fd_pack_pacing_t const * pacer,
      83        4669 :                                  long                     now ) {
      84             :   /* We want to use as few banks as possible to fill the block in 400
      85             :      milliseconds.  That way we pass up the best transaction because it
      86             :      conflicts with something actively running as infrequently as
      87             :      possible.  To do that, we draw lines through in the time-CU plane
      88             :      that pass through approximately (400 milliseconds, 48M CUs) with
      89             :      slope k*(single bank speed), where k varies between 1 and the
      90             :      number of bank tiles configured.  This splits the plane into
      91             :      several regions, and the region we are in tells us how many bank
      92             :      tiles to use.
      93             : 
      94             : 
      95             :        48M -                                                   / /|
      96             :            |                                                /  / /
      97             :            |                                             /   // |
      98             :        U   |                                          /    / / /
      99             :        s   |      0 banks active                   /     /  /  |
     100             :        e   |                                    /      /   /  /
     101             :        d   |                                 /    e  /    /   |
     102             :            |                              /  k  v  /     /   /
     103             :        C   |                           /   n  i  /      /    |
     104             :        U   |                        /    a  t  /       /    /
     105             :        s   |                     /     B  c  /        /     |
     106             :            |                  /     1   a  / 2 Banks /     /
     107             :            |               /             /   active / ...  |
     108             :        0   |--------------------------------------------------------
     109             :              0 ms                                                400ms
     110             :        */
     111             :   /* We want to be pretty careful with the math here.  We want to make
     112             :      sure we never divide by 0, so clamp the denominator at 1.  The
     113             :      numerator is non-negative.  Ticks_per_cu is between 1 and 100, so
     114             :      it'll always fit in a ulong. */
     115        4669 :   return (ulong)(pacer->remaining_cus/
     116        4669 :                  (float)(fd_long_max( 1L, pacer->t_end - now )) * pacer->ticks_per_cu );
     117        4669 : }
     118             : 
     119             : #endif /* HEADER_fd_src_disco_pack_fd_pack_pacing_h */

Generated by: LCOV version 1.14