LCOV - code coverage report
Current view: top level - util/hist - fd_histf.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 49 52 94.2 %
Date: 2024-11-13 11:58:15 Functions: 11 7476 0.1 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_util_hist_fd_histf_h
       2             : #define HEADER_fd_src_util_hist_fd_histf_h
       3             : 
       4             : /* Simple fast fixed-size exponential histograms.  Histograms are
       5             :    bucketed exponentially up to a maximum value, with an overflow bucket
       6             :    for any other measurements. */
       7             : 
       8             : #include <math.h>
       9             : #include "../bits/fd_bits.h"
      10             : #include "../log/fd_log.h"
      11             : #if FD_HAS_AVX
      12             : #include "../simd/fd_avx.h"
      13             : #endif
      14             : 
      15      363708 : #define FD_HISTF_BUCKET_CNT 16UL
      16             : 
      17           3 : #define FD_HISTF_ALIGN      (32UL)
      18           3 : #define FD_HISTF_FOOTPRINT  (FD_ULONG_ALIGN_UP( FD_HISTF_BUCKET_CNT*sizeof(ulong)+(FD_HISTF_BUCKET_CNT+1UL)*sizeof(long), FD_HISTF_ALIGN ))
      19             : /* Static assertion FOOTPRINT==sizeof in test */
      20             : 
      21             : struct __attribute__((aligned(FD_HISTF_ALIGN))) fd_histf_private {
      22             :   ulong counts[ FD_HISTF_BUCKET_CNT ];
      23             :   /* A value x belongs to bucket i if
      24             :      left_edge[i] <= x - 2^63 < left_edge[i+1].
      25             : 
      26             :      For AVX2, there's no unsiged comparison instruction.  We follow
      27             :      what wv_gt does and implement it by subtracting 2^63 from each
      28             :      operand.  Rather than perform the subtraction at each comparison,
      29             :      we pre-subtract here. */
      30             :   long  left_edge[ FD_HISTF_BUCKET_CNT+1 ];
      31             :   ulong sum; /* the sum of all the samples, useful for computing mean */
      32             : };
      33             : 
      34             : typedef struct fd_histf_private fd_histf_t;
      35             : 
      36             : FD_PROTOTYPES_BEGIN
      37             : 
      38           0 : FD_FN_CONST static inline ulong fd_histf_align    ( void ) { return FD_HISTF_ALIGN;     }
      39           0 : FD_FN_CONST static inline ulong fd_histf_footprint( void ) { return FD_HISTF_FOOTPRINT; }
      40             : 
      41             : /* fd_histf_new takes ownership of the memory region pointed to by mem
      42             :    (which is assumed to be non-NULL with the appropriate alignment and
      43             :    footprint) and formats it as a fd_hist.  The histogram will be
      44             :    initialized with buckets roughly exponentially spaced between
      45             :    min_value and max_value.  min_value must be > 0. Returns mem (which
      46             :    will be formatted for use).
      47             : 
      48             :    Every histogram has special buckets for underflow values (strictly
      49             :    less than min_val) and overflow values (larger than or equal to the
      50             :    max_value).
      51             : 
      52             :       [ 0, min_value )
      53             :       [ min_value,             approx. min_value * z   )
      54             :       [ approx. min_value * z, approx. min_value * z^2 )
      55             :       ...
      56             :       [ approx. min_value * z^13, max_value )
      57             :       [ max_value, inf )
      58             : 
      59             :    z is chosen so that max_value is approximately min_value * z^14 The
      60             :    approximations come from the fact that all bucket edges are integers,
      61             :    and no bucket is empty.
      62             : 
      63             :    If max_value < min_value+14, then max_value will be increased to
      64             :    min_value+14 so that no buckets are empty.  Note that this histogram
      65             :    contains strictly more information than what was requested, so an
      66             :    end-user could postprocess and reduce the number of bins again
      67             :    without losing any information.
      68             : 
      69             :    For example, if min_value is 1 and max_value is 100, the buckets
      70             :    will be
      71             : 
      72             :        0: [  0,   1)
      73             :        1: [  1,   2)
      74             :        2: [  2,   3)
      75             :        3: [  3,   4)
      76             :        4: [  4,   5)
      77             :        5: [  5,   7)
      78             :        6: [  7,   9)
      79             :        7: [  9,  12)
      80             :        8: [ 12,  16)
      81             :        9: [ 16,  22)
      82             :       10: [ 22,  30)
      83             :       11: [ 30,  41)
      84             :       12: [ 41,  55)
      85             :       13: [ 55,  74)
      86             :       14: [ 74, 100)
      87             :       15: [100, inf) */
      88             : 
      89             : static inline void *
      90             : fd_histf_new( void * mem,
      91             :               ulong  min_value,
      92        2616 :               ulong  max_value ) {
      93        2616 :   if( FD_UNLIKELY( max_value<=min_value ) ) return NULL;
      94             : 
      95        2616 :   min_value = fd_ulong_max( min_value, 1UL );
      96        2616 :   max_value = fd_ulong_max( max_value, min_value + FD_HISTF_BUCKET_CNT - 2UL );
      97             : 
      98        2616 :   fd_histf_t * hist = (fd_histf_t*)mem;
      99        2616 :   fd_memset( hist->counts, 0, FD_HISTF_BUCKET_CNT*sizeof(ulong) );
     100        2616 :   hist->sum = 0UL;
     101        2616 :   ulong left_edge[ FD_HISTF_BUCKET_CNT ]; /* without the -2^63 shift */
     102        2616 :   left_edge[ 0 ] = 0;
     103        2616 :   left_edge[ 1 ] = min_value;
     104       36624 :   for( ulong i=2UL; i<(FD_HISTF_BUCKET_CNT-1UL); i++ ) {
     105       34008 : #if FD_HAS_DOUBLE
     106       34008 :     ulong le = (ulong)(0.5  + (double)left_edge[ i-1UL ] * pow ( (double)max_value / (double)left_edge[ i-1UL ], 1.0 /(double)(FD_HISTF_BUCKET_CNT - i) ) );
     107             : #else
     108             :     ulong le = (ulong)(0.5f + (float )left_edge[ i-1UL ] * powf( (float )max_value / (float )left_edge[ i-1UL ], 1.0f/(float )(FD_HISTF_BUCKET_CNT - i) ) );
     109             : #endif
     110       34008 :     le = fd_ulong_max( le, left_edge[ i-1UL ] + 1UL ); /* Make sure bucket is not empty */
     111       34008 :     left_edge[ i ] = le;
     112       34008 :   }
     113        2616 :   left_edge[ FD_HISTF_BUCKET_CNT - 1UL ] = max_value;
     114             : 
     115       44472 :   for( ulong i=0UL; i<FD_HISTF_BUCKET_CNT; i++ ) hist->left_edge[ i ] = (long)(left_edge[ i ] - (1UL<<63));
     116        2616 :   hist->left_edge[ FD_HISTF_BUCKET_CNT ] = LONG_MAX;
     117             : 
     118        2616 :   return (void*)hist;
     119        2616 : }
     120             : 
     121           6 : static inline fd_histf_t * fd_histf_join  ( void       * _hist ) { return (fd_histf_t *)_hist; }
     122           6 : static inline void       * fd_histf_leave ( fd_histf_t * _hist ) { return (void       *)_hist; }
     123           6 : static inline void       * fd_histf_delete( void       * _hist ) { return (void       *)_hist; }
     124             : 
     125             : /* Return the number of buckets in the histogram, including the overflow
     126             :    bucket. */
     127           0 : FD_FN_PURE static inline ulong fd_histf_bucket_cnt( fd_histf_t * hist ) { (void)hist; return FD_HISTF_BUCKET_CNT; }
     128             : 
     129             : /* Add a sample to the histogram.  If the sample is larger than or equal
     130             :    to the max_value it will be added to a special overflow bucket. */
     131             : static inline void
     132             : fd_histf_sample( fd_histf_t * hist,
     133  3001484784 :                  ulong        value ) {
     134  3001484784 :   hist->sum += value;
     135  3001484784 :   long shifted_v = (long)(value - (1UL<<63));
     136  3001484784 : #if FD_HAS_AVX
     137  3001484784 :   wl_t x = wl_bcast( shifted_v );
     138             :   /* !(x-2^63 < left_edge[i]) & (x-2^63 < left_edge[i+1])  <=>
     139             :      left_edge[i] <= x-2^63 < left_edge[i+1] */
     140  3001484784 :   wc_t select0 = wc_andnot( wl_lt( x, wl_ld ( hist->left_edge      ) ),
     141  3001484784 :                             wl_lt( x, wl_ldu( hist->left_edge+ 1UL ) ) );
     142  3001484784 :   wc_t select1 = wc_andnot( wl_lt( x, wl_ld ( hist->left_edge+ 4UL ) ),
     143  3001484784 :                             wl_lt( x, wl_ldu( hist->left_edge+ 5UL ) ) );
     144  3001484784 :   wc_t select2 = wc_andnot( wl_lt( x, wl_ld ( hist->left_edge+ 8UL ) ),
     145  3001484784 :                             wl_lt( x, wl_ldu( hist->left_edge+ 9UL ) ) );
     146  3001484784 :   wc_t select3 = wc_andnot( wl_lt( x, wl_ld ( hist->left_edge+12UL ) ),
     147  3001484784 :                             wl_lt( x, wl_ldu( hist->left_edge+13UL ) ) );
     148             :   /* In exactly one of these, we have a -1 (aka ULONG_MAX).  We'll
     149             :      subtract that from the counts, effectively adding 1. */
     150  3001484784 :   wv_st( hist->counts,       wv_sub( wv_ld( hist->counts      ), wc_to_wv_raw( select0 ) ) );
     151  3001484784 :   wv_st( hist->counts+ 4UL,  wv_sub( wv_ld( hist->counts+ 4UL ), wc_to_wv_raw( select1 ) ) );
     152  3001484784 :   wv_st( hist->counts+ 8UL,  wv_sub( wv_ld( hist->counts+ 8UL ), wc_to_wv_raw( select2 ) ) );
     153  3001484784 :   wv_st( hist->counts+12UL,  wv_sub( wv_ld( hist->counts+12UL ), wc_to_wv_raw( select3 ) ) );
     154             : #else
     155             :   for( ulong i=0UL; i<16UL; i++ ) hist->counts[ i ] += (ulong)( (hist->left_edge[ i ] <= shifted_v) & (shifted_v < hist->left_edge[ i+1UL ]) );
     156             : #endif
     157  3001484784 : }
     158             : 
     159             : /* fd_histf_cnt gets the count of samples in a particular bucket of the
     160             :    historgram.
     161             : 
     162             :    fd_histf_{left,right} get the sample values that map to bucket b,
     163             :    with a half-open interval [left, right).
     164             : 
     165             :    fd_histf_sum gets the sum of all samples that have been added.  I.e.
     166             :    fd_histf_sum() / sum(fd_histf_cnt(j) for j in [0, 16)) is the average
     167             :    sample value.
     168             : 
     169             :    For these functions, b, the bucket index is in [0, 16). */
     170         591 : FD_FN_PURE static inline ulong fd_histf_cnt  ( fd_histf_t const * hist, ulong b ) { return        hist->counts   [ b     ];           }
     171          45 : FD_FN_PURE static inline ulong fd_histf_left ( fd_histf_t const * hist, ulong b ) { return (ulong)hist->left_edge[ b     ]+(1UL<<63); }
     172          45 : FD_FN_PURE static inline ulong fd_histf_right( fd_histf_t const * hist, ulong b ) { return (ulong)hist->left_edge[ b+1UL ]+(1UL<<63); }
     173          93 : FD_FN_PURE static inline ulong fd_histf_sum  ( fd_histf_t const * hist          ) { return        hist->sum;                          }
     174             : 
     175             : FD_PROTOTYPES_END
     176             : 
     177             : #endif /* HEADER_fd_src_util_hist_fd_histf_h */

Generated by: LCOV version 1.14