Home | History | Annotate | Line # | Download | only in internal
      1      1.1  christos #ifndef JEMALLOC_INTERNAL_TICKER_H
      2      1.1  christos #define JEMALLOC_INTERNAL_TICKER_H
      3      1.1  christos 
      4  1.1.1.2  christos #include "jemalloc/internal/prng.h"
      5      1.1  christos #include "jemalloc/internal/util.h"
      6      1.1  christos 
      7      1.1  christos /**
      8      1.1  christos  * A ticker makes it easy to count-down events until some limit.  You
      9      1.1  christos  * ticker_init the ticker to trigger every nticks events.  You then notify it
     10      1.1  christos  * that an event has occurred with calls to ticker_tick (or that nticks events
     11      1.1  christos  * have occurred with a call to ticker_ticks), which will return true (and reset
     12      1.1  christos  * the counter) if the countdown hit zero.
     13      1.1  christos  */
     14  1.1.1.2  christos typedef struct ticker_s ticker_t;
     15  1.1.1.2  christos struct ticker_s {
     16      1.1  christos 	int32_t tick;
     17      1.1  christos 	int32_t nticks;
     18  1.1.1.2  christos };
     19      1.1  christos 
     20      1.1  christos static inline void
     21      1.1  christos ticker_init(ticker_t *ticker, int32_t nticks) {
     22      1.1  christos 	ticker->tick = nticks;
     23      1.1  christos 	ticker->nticks = nticks;
     24      1.1  christos }
     25      1.1  christos 
     26      1.1  christos static inline void
     27      1.1  christos ticker_copy(ticker_t *ticker, const ticker_t *other) {
     28      1.1  christos 	*ticker = *other;
     29      1.1  christos }
     30      1.1  christos 
     31      1.1  christos static inline int32_t
     32      1.1  christos ticker_read(const ticker_t *ticker) {
     33      1.1  christos 	return ticker->tick;
     34      1.1  christos }
     35      1.1  christos 
     36      1.1  christos /*
     37      1.1  christos  * Not intended to be a public API.  Unfortunately, on x86, neither gcc nor
     38      1.1  christos  * clang seems smart enough to turn
     39      1.1  christos  *   ticker->tick -= nticks;
     40      1.1  christos  *   if (unlikely(ticker->tick < 0)) {
     41      1.1  christos  *     fixup ticker
     42      1.1  christos  *     return true;
     43      1.1  christos  *   }
     44      1.1  christos  *   return false;
     45      1.1  christos  * into
     46      1.1  christos  *   subq %nticks_reg, (%ticker_reg)
     47      1.1  christos  *   js fixup ticker
     48      1.1  christos  *
     49      1.1  christos  * unless we force "fixup ticker" out of line.  In that case, gcc gets it right,
     50      1.1  christos  * but clang now does worse than before.  So, on x86 with gcc, we force it out
     51      1.1  christos  * of line, but otherwise let the inlining occur.  Ordinarily this wouldn't be
     52      1.1  christos  * worth the hassle, but this is on the fast path of both malloc and free (via
     53      1.1  christos  * tcache_event).
     54      1.1  christos  */
     55      1.1  christos #if defined(__GNUC__) && !defined(__clang__)				\
     56      1.1  christos     && (defined(__x86_64__) || defined(__i386__))
     57      1.1  christos JEMALLOC_NOINLINE
     58      1.1  christos #endif
     59      1.1  christos static bool
     60      1.1  christos ticker_fixup(ticker_t *ticker) {
     61      1.1  christos 	ticker->tick = ticker->nticks;
     62      1.1  christos 	return true;
     63      1.1  christos }
     64      1.1  christos 
     65      1.1  christos static inline bool
     66      1.1  christos ticker_ticks(ticker_t *ticker, int32_t nticks) {
     67      1.1  christos 	ticker->tick -= nticks;
     68      1.1  christos 	if (unlikely(ticker->tick < 0)) {
     69      1.1  christos 		return ticker_fixup(ticker);
     70      1.1  christos 	}
     71      1.1  christos 	return false;
     72      1.1  christos }
     73      1.1  christos 
     74      1.1  christos static inline bool
     75      1.1  christos ticker_tick(ticker_t *ticker) {
     76      1.1  christos 	return ticker_ticks(ticker, 1);
     77      1.1  christos }
     78      1.1  christos 
     79  1.1.1.2  christos /*
     80  1.1.1.2  christos  * Try to tick.  If ticker would fire, return true, but rely on
     81  1.1.1.2  christos  * slowpath to reset ticker.
     82  1.1.1.2  christos  */
     83  1.1.1.2  christos static inline bool
     84  1.1.1.2  christos ticker_trytick(ticker_t *ticker) {
     85  1.1.1.2  christos 	--ticker->tick;
     86  1.1.1.2  christos 	if (unlikely(ticker->tick < 0)) {
     87  1.1.1.2  christos 		return true;
     88  1.1.1.2  christos 	}
     89  1.1.1.2  christos 	return false;
     90  1.1.1.2  christos }
     91  1.1.1.2  christos 
     92  1.1.1.2  christos /*
     93  1.1.1.2  christos  * The ticker_geom_t is much like the ticker_t, except that instead of ticker
     94  1.1.1.2  christos  * having a constant countdown, it has an approximate one; each tick has
     95  1.1.1.2  christos  * approximately a 1/nticks chance of triggering the count.
     96  1.1.1.2  christos  *
     97  1.1.1.2  christos  * The motivation is in triggering arena decay.  With a naive strategy, each
     98  1.1.1.2  christos  * thread would maintain a ticker per arena, and check if decay is necessary
     99  1.1.1.2  christos  * each time that the arena's ticker fires.  This has two costs:
    100  1.1.1.2  christos  * - Since under reasonable assumptions both threads and arenas can scale
    101  1.1.1.2  christos  *   linearly with the number of CPUs, maintaining per-arena data in each thread
    102  1.1.1.2  christos  *   scales quadratically with the number of CPUs.
    103  1.1.1.2  christos  * - These tickers are often a cache miss down tcache flush pathways.
    104  1.1.1.2  christos  *
    105  1.1.1.2  christos  * By giving each tick a 1/nticks chance of firing, we still maintain the same
    106  1.1.1.2  christos  * average number of ticks-until-firing per arena, with only a single ticker's
    107  1.1.1.2  christos  * worth of metadata.
    108  1.1.1.2  christos  */
    109  1.1.1.2  christos 
    110  1.1.1.2  christos /* See ticker.c for an explanation of these constants. */
    111  1.1.1.2  christos #define TICKER_GEOM_NBITS 6
    112  1.1.1.2  christos #define TICKER_GEOM_MUL 61
    113  1.1.1.2  christos extern const uint8_t ticker_geom_table[1 << TICKER_GEOM_NBITS];
    114  1.1.1.2  christos 
    115  1.1.1.2  christos /* Not actually any different from ticker_t; just for type safety. */
    116  1.1.1.2  christos typedef struct ticker_geom_s ticker_geom_t;
    117  1.1.1.2  christos struct ticker_geom_s {
    118  1.1.1.2  christos 	int32_t tick;
    119  1.1.1.2  christos 	int32_t nticks;
    120  1.1.1.2  christos };
    121  1.1.1.2  christos 
    122  1.1.1.2  christos /*
    123  1.1.1.2  christos  * Just pick the average delay for the first counter.  We're more concerned with
    124  1.1.1.2  christos  * the behavior over long periods of time rather than the exact timing of the
    125  1.1.1.2  christos  * initial ticks.
    126  1.1.1.2  christos  */
    127  1.1.1.2  christos #define TICKER_GEOM_INIT(nticks) {nticks, nticks}
    128  1.1.1.2  christos 
    129  1.1.1.2  christos static inline void
    130  1.1.1.2  christos ticker_geom_init(ticker_geom_t *ticker, int32_t nticks) {
    131  1.1.1.2  christos 	/*
    132  1.1.1.2  christos 	 * Make sure there's no overflow possible.  This shouldn't really be a
    133  1.1.1.2  christos 	 * problem for reasonable nticks choices, which are all static and
    134  1.1.1.2  christos 	 * relatively small.
    135  1.1.1.2  christos 	 */
    136  1.1.1.2  christos 	assert((uint64_t)nticks * (uint64_t)255 / (uint64_t)TICKER_GEOM_MUL
    137  1.1.1.2  christos 	    <= (uint64_t)INT32_MAX);
    138  1.1.1.2  christos 	ticker->tick = nticks;
    139  1.1.1.2  christos 	ticker->nticks = nticks;
    140  1.1.1.2  christos }
    141  1.1.1.2  christos 
    142  1.1.1.2  christos static inline int32_t
    143  1.1.1.2  christos ticker_geom_read(const ticker_geom_t *ticker) {
    144  1.1.1.2  christos 	return ticker->tick;
    145  1.1.1.2  christos }
    146  1.1.1.2  christos 
    147  1.1.1.2  christos /* Same deal as above. */
    148  1.1.1.2  christos #if defined(__GNUC__) && !defined(__clang__)				\
    149  1.1.1.2  christos     && (defined(__x86_64__) || defined(__i386__))
    150  1.1.1.2  christos JEMALLOC_NOINLINE
    151  1.1.1.2  christos #endif
    152  1.1.1.2  christos static bool
    153  1.1.1.2  christos ticker_geom_fixup(ticker_geom_t *ticker, uint64_t *prng_state) {
    154  1.1.1.2  christos 	uint64_t idx = prng_lg_range_u64(prng_state, TICKER_GEOM_NBITS);
    155  1.1.1.2  christos 	ticker->tick = (uint32_t)(
    156  1.1.1.2  christos 	    (uint64_t)ticker->nticks * (uint64_t)ticker_geom_table[idx]
    157  1.1.1.2  christos 	    / (uint64_t)TICKER_GEOM_MUL);
    158  1.1.1.2  christos 	return true;
    159  1.1.1.2  christos }
    160  1.1.1.2  christos 
    161  1.1.1.2  christos static inline bool
    162  1.1.1.2  christos ticker_geom_ticks(ticker_geom_t *ticker, uint64_t *prng_state, int32_t nticks) {
    163  1.1.1.2  christos 	ticker->tick -= nticks;
    164  1.1.1.2  christos 	if (unlikely(ticker->tick < 0)) {
    165  1.1.1.2  christos 		return ticker_geom_fixup(ticker, prng_state);
    166  1.1.1.2  christos 	}
    167  1.1.1.2  christos 	return false;
    168  1.1.1.2  christos }
    169  1.1.1.2  christos 
    170  1.1.1.2  christos static inline bool
    171  1.1.1.2  christos ticker_geom_tick(ticker_geom_t *ticker, uint64_t *prng_state) {
    172  1.1.1.2  christos 	return ticker_geom_ticks(ticker, prng_state, 1);
    173  1.1.1.2  christos }
    174  1.1.1.2  christos 
    175      1.1  christos #endif /* JEMALLOC_INTERNAL_TICKER_H */
    176