decay.h revision 1.1 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