Home | History | Annotate | Line # | Download | only in internal
      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