forked from memcached/memcached
-
Notifications
You must be signed in to change notification settings - Fork 0
/
slab_automove.c
169 lines (148 loc) · 5.59 KB
/
slab_automove.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* Copyright 2017 Facebook.
*
* Use and distribution licensed under the BSD license. See
* the LICENSE file for full text.
*/
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "memcached.h"
#include "slab_automove.h"
#include <stdlib.h>
#include <string.h>
#define MIN_PAGES_FOR_SOURCE 2
#define MIN_PAGES_FOR_RECLAIM 2.5
struct window_data {
uint64_t age;
uint64_t dirty;
float evicted_ratio;
uint64_t evicted_seen; // if evictions were seen at all this window
};
typedef struct {
struct window_data *window_data;
uint32_t window_size;
uint32_t window_cur;
double max_age_ratio;
item_stats_automove iam_before[MAX_NUMBER_OF_SLAB_CLASSES];
item_stats_automove iam_after[MAX_NUMBER_OF_SLAB_CLASSES];
slab_stats_automove sam_before[MAX_NUMBER_OF_SLAB_CLASSES];
slab_stats_automove sam_after[MAX_NUMBER_OF_SLAB_CLASSES];
} slab_automove;
void *slab_automove_init(struct settings *settings) {
uint32_t window_size = settings->slab_automove_window;
double max_age_ratio = settings->slab_automove_ratio;
slab_automove *a = calloc(1, sizeof(slab_automove));
if (a == NULL)
return NULL;
a->window_data = calloc(window_size * MAX_NUMBER_OF_SLAB_CLASSES, sizeof(struct window_data));
a->window_size = window_size;
a->max_age_ratio = max_age_ratio;
if (a->window_data == NULL) {
free(a);
return NULL;
}
// do a dry run to fill the before structs
fill_item_stats_automove(a->iam_before);
fill_slab_stats_automove(a->sam_before);
return (void *)a;
}
void slab_automove_free(void *arg) {
slab_automove *a = (slab_automove *)arg;
free(a->window_data);
free(a);
}
static void window_sum(struct window_data *wd, struct window_data *w, uint32_t size) {
int x;
for (x = 0; x < size; x++) {
struct window_data *d = &wd[x];
w->age += d->age;
w->dirty += d->dirty;
w->evicted_ratio += d->evicted_ratio;
w->evicted_seen += d->evicted_seen;
}
}
// TODO: if oldest is dirty, find next oldest.
// still need to base ratio off of absolute age
void slab_automove_run(void *arg, int *src, int *dst) {
slab_automove *a = (slab_automove *)arg;
int n;
struct window_data w_sum;
int oldest = -1;
uint64_t oldest_age = 0;
int youngest = -1;
uint64_t youngest_age = ~0;
bool youngest_evicting = false;
*src = -1;
*dst = -1;
// fill after structs
fill_item_stats_automove(a->iam_after);
fill_slab_stats_automove(a->sam_after);
// Loop once to get total_evicted for this window.
uint64_t evicted_total = 0;
for (n = POWER_SMALLEST; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) {
evicted_total += a->iam_after[n].evicted - a->iam_before[n].evicted;
}
a->window_cur++;
// iterate slabs
for (n = POWER_SMALLEST; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) {
int w_offset = n * a->window_size;
struct window_data *wd = &a->window_data[w_offset + (a->window_cur % a->window_size)];
memset(wd, 0, sizeof(struct window_data));
// if page delta, or evicted delta, mark window dirty
// (or outofmemory)
uint64_t evicted_delta = a->iam_after[n].evicted - a->iam_before[n].evicted;
if (evicted_delta > 0) {
// FIXME: the python script is using floats. we have ints.
wd->evicted_ratio = (float) evicted_delta / evicted_total;
wd->evicted_seen = 1;
wd->dirty = 1;
}
if (a->iam_after[n].outofmemory - a->iam_before[n].outofmemory > 0) {
wd->dirty = 1;
}
if (a->sam_after[n].total_pages - a->sam_before[n].total_pages > 0) {
wd->dirty = 1;
}
// set age into window
wd->age = a->iam_after[n].age;
// summarize the window-up-to-now.
memset(&w_sum, 0, sizeof(struct window_data));
window_sum(&a->window_data[w_offset], &w_sum, a->window_size);
// grab age as average of window total
uint64_t age = w_sum.age / a->window_size;
// if > N free chunks and not dirty, make decision.
if (a->sam_after[n].free_chunks > a->sam_after[n].chunks_per_page * MIN_PAGES_FOR_RECLAIM) {
if (w_sum.dirty == 0) {
*src = n;
*dst = 0;
youngest = oldest = -1;
break;
}
}
// if oldest and have enough pages, is oldest
if (age > oldest_age && a->sam_after[n].total_pages > MIN_PAGES_FOR_SOURCE) {
oldest = n;
oldest_age = age;
}
// grab evicted count from window
// if > half the window and youngest, mark as youngest
// or, if more than 25% of total evictions in the window.
if (age < youngest_age && (w_sum.evicted_seen > a->window_size / 2
|| w_sum.evicted_ratio / a->window_size > 0.25)) {
youngest = n;
youngest_age = age;
youngest_evicting = wd->evicted_seen ? true : false;
}
}
memcpy(a->iam_before, a->iam_after,
sizeof(item_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES);
memcpy(a->sam_before, a->sam_after,
sizeof(slab_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES);
// if we have a youngest and oldest, and oldest is outside the ratio,
// also, only make decisions if window has filled once.
if (youngest != -1 && oldest != -1 && a->window_cur > a->window_size) {
if (youngest_age < ((double)oldest_age * a->max_age_ratio) && youngest_evicting) {
*src = oldest;
*dst = youngest;
}
}
return;
}