1 1.1 christos #ifndef JEMALLOC_INTERNAL_DECAY_H 2 1.1 christos #define JEMALLOC_INTERNAL_DECAY_H 3 1.1 christos 4 1.1 christos #include "jemalloc/internal/smoothstep.h" 5 1.1 christos 6 1.1 christos #define DECAY_UNBOUNDED_TIME_TO_PURGE ((uint64_t)-1) 7 1.1 christos 8 1.1 christos /* 9 1.1 christos * The decay_t computes the number of pages we should purge at any given time. 10 1.1 christos * Page allocators inform a decay object when pages enter a decay-able state 11 1.1 christos * (i.e. dirty or muzzy), and query it to determine how many pages should be 12 1.1 christos * purged at any given time. 13 1.1 christos * 14 1.1 christos * This is mostly a single-threaded data structure and doesn't care about 15 1.1 christos * synchronization at all; it's the caller's responsibility to manage their 16 1.1 christos * synchronization on their own. There are two exceptions: 17 1.1 christos * 1) It's OK to racily call decay_ms_read (i.e. just the simplest state query). 18 1.1 christos * 2) The mtx and purging fields live (and are initialized) here, but are 19 1.1 christos * logically owned by the page allocator. This is just a convenience (since 20 1.1 christos * those fields would be duplicated for both the dirty and muzzy states 21 1.1 christos * otherwise). 22 1.1 christos */ 23 1.1 christos typedef struct decay_s decay_t; 24 1.1 christos struct decay_s { 25 1.1 christos /* Synchronizes all non-atomic fields. */ 26 1.1 christos malloc_mutex_t mtx; 27 1.1 christos /* 28 1.1 christos * True if a thread is currently purging the extents associated with 29 1.1 christos * this decay structure. 30 1.1 christos */ 31 1.1 christos bool purging; 32 1.1 christos /* 33 1.1 christos * Approximate time in milliseconds from the creation of a set of unused 34 1.1 christos * dirty pages until an equivalent set of unused dirty pages is purged 35 1.1 christos * and/or reused. 36 1.1 christos */ 37 1.1 christos atomic_zd_t time_ms; 38 1.1 christos /* time / SMOOTHSTEP_NSTEPS. */ 39 1.1 christos nstime_t interval; 40 1.1 christos /* 41 1.1 christos * Time at which the current decay interval logically started. We do 42 1.1 christos * not actually advance to a new epoch until sometime after it starts 43 1.1 christos * because of scheduling and computation delays, and it is even possible 44 1.1 christos * to completely skip epochs. In all cases, during epoch advancement we 45 1.1 christos * merge all relevant activity into the most recently recorded epoch. 46 1.1 christos */ 47 1.1 christos nstime_t epoch; 48 1.1 christos /* Deadline randomness generator. */ 49 1.1 christos uint64_t jitter_state; 50 1.1 christos /* 51 1.1 christos * Deadline for current epoch. This is the sum of interval and per 52 1.1 christos * epoch jitter which is a uniform random variable in [0..interval). 53 1.1 christos * Epochs always advance by precise multiples of interval, but we 54 1.1 christos * randomize the deadline to reduce the likelihood of arenas purging in 55 1.1 christos * lockstep. 56 1.1 christos */ 57 1.1 christos nstime_t deadline; 58 1.1 christos /* 59 1.1 christos * The number of pages we cap ourselves at in the current epoch, per 60 1.1 christos * decay policies. Updated on an epoch change. After an epoch change, 61 1.1 christos * the caller should take steps to try to purge down to this amount. 62 1.1 christos */ 63 1.1 christos size_t npages_limit; 64 1.1 christos /* 65 1.1 christos * Number of unpurged pages at beginning of current epoch. During epoch 66 1.1 christos * advancement we use the delta between arena->decay_*.nunpurged and 67 1.1 christos * ecache_npages_get(&arena->ecache_*) to determine how many dirty pages, 68 1.1 christos * if any, were generated. 69 1.1 christos */ 70 1.1 christos size_t nunpurged; 71 1.1 christos /* 72 1.1 christos * Trailing log of how many unused dirty pages were generated during 73 1.1 christos * each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last 74 1.1 christos * element is the most recent epoch. Corresponding epoch times are 75 1.1 christos * relative to epoch. 76 1.1 christos * 77 1.1 christos * Updated only on epoch advance, triggered by 78 1.1 christos * decay_maybe_advance_epoch, below. 79 1.1 christos */ 80 1.1 christos size_t backlog[SMOOTHSTEP_NSTEPS]; 81 1.1 christos 82 1.1 christos /* Peak number of pages in associated extents. Used for debug only. */ 83 1.1 christos uint64_t ceil_npages; 84 1.1 christos }; 85 1.1 christos 86 1.1 christos /* 87 1.1 christos * The current decay time setting. This is the only public access to a decay_t 88 1.1 christos * that's allowed without holding mtx. 89 1.1 christos */ 90 1.1 christos static inline ssize_t 91 1.1 christos decay_ms_read(const decay_t *decay) { 92 1.1 christos return atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED); 93 1.1 christos } 94 1.1 christos 95 1.1 christos /* 96 1.1 christos * See the comment on the struct field -- the limit on pages we should allow in 97 1.1 christos * this decay state this epoch. 98 1.1 christos */ 99 1.1 christos static inline size_t 100 1.1 christos decay_npages_limit_get(const decay_t *decay) { 101 1.1 christos return decay->npages_limit; 102 1.1 christos } 103 1.1 christos 104 1.1 christos /* How many unused dirty pages were generated during the last epoch. */ 105 1.1 christos static inline size_t 106 1.1 christos decay_epoch_npages_delta(const decay_t *decay) { 107 1.1 christos return decay->backlog[SMOOTHSTEP_NSTEPS - 1]; 108 1.1 christos } 109 1.1 christos 110 1.1 christos /* 111 1.1 christos * Current epoch duration, in nanoseconds. Given that new epochs are started 112 1.1 christos * somewhat haphazardly, this is not necessarily exactly the time between any 113 1.1 christos * two calls to decay_maybe_advance_epoch; see the comments on fields in the 114 1.1 christos * decay_t. 115 1.1 christos */ 116 1.1 christos static inline uint64_t 117 1.1 christos decay_epoch_duration_ns(const decay_t *decay) { 118 1.1 christos return nstime_ns(&decay->interval); 119 1.1 christos } 120 1.1 christos 121 1.1 christos static inline bool 122 1.1 christos decay_immediately(const decay_t *decay) { 123 1.1 christos ssize_t decay_ms = decay_ms_read(decay); 124 1.1 christos return decay_ms == 0; 125 1.1 christos } 126 1.1 christos 127 1.1 christos static inline bool 128 1.1 christos decay_disabled(const decay_t *decay) { 129 1.1 christos ssize_t decay_ms = decay_ms_read(decay); 130 1.1 christos return decay_ms < 0; 131 1.1 christos } 132 1.1 christos 133 1.1 christos /* Returns true if decay is enabled and done gradually. */ 134 1.1 christos static inline bool 135 1.1 christos decay_gradually(const decay_t *decay) { 136 1.1 christos ssize_t decay_ms = decay_ms_read(decay); 137 1.1 christos return decay_ms > 0; 138 1.1 christos } 139 1.1 christos 140 1.1 christos /* 141 1.1 christos * Returns true if the passed in decay time setting is valid. 142 1.1 christos * < -1 : invalid 143 1.1 christos * -1 : never decay 144 1.1 christos * 0 : decay immediately 145 1.1 christos * > 0 : some positive decay time, up to a maximum allowed value of 146 1.1 christos * NSTIME_SEC_MAX * 1000, which corresponds to decaying somewhere in the early 147 1.1 christos * 27th century. By that time, we expect to have implemented alternate purging 148 1.1 christos * strategies. 149 1.1 christos */ 150 1.1 christos bool decay_ms_valid(ssize_t decay_ms); 151 1.1 christos 152 1.1 christos /* 153 1.1 christos * As a precondition, the decay_t must be zeroed out (as if with memset). 154 1.1 christos * 155 1.1 christos * Returns true on error. 156 1.1 christos */ 157 1.1 christos bool decay_init(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms); 158 1.1 christos 159 1.1 christos /* 160 1.1 christos * Given an already-initialized decay_t, reinitialize it with the given decay 161 1.1 christos * time. The decay_t must have previously been initialized (and should not then 162 1.1 christos * be zeroed). 163 1.1 christos */ 164 1.1 christos void decay_reinit(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms); 165 1.1 christos 166 1.1 christos /* 167 1.1 christos * Compute how many of 'npages_new' pages we would need to purge in 'time'. 168 1.1 christos */ 169 1.1 christos uint64_t decay_npages_purge_in(decay_t *decay, nstime_t *time, 170 1.1 christos size_t npages_new); 171 1.1 christos 172 1.1 christos /* Returns true if the epoch advanced and there are pages to purge. */ 173 1.1 christos bool decay_maybe_advance_epoch(decay_t *decay, nstime_t *new_time, 174 1.1 christos size_t current_npages); 175 1.1 christos 176 1.1 christos /* 177 1.1 christos * Calculates wait time until a number of pages in the interval 178 1.1 christos * [0.5 * npages_threshold .. 1.5 * npages_threshold] should be purged. 179 1.1 christos * 180 1.1 christos * Returns number of nanoseconds or DECAY_UNBOUNDED_TIME_TO_PURGE in case of 181 1.1 christos * indefinite wait. 182 1.1 christos */ 183 1.1 christos uint64_t decay_ns_until_purge(decay_t *decay, size_t npages_current, 184 1.1 christos uint64_t npages_threshold); 185 1.1 christos 186 1.1 christos #endif /* JEMALLOC_INTERNAL_DECAY_H */ 187