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