Blame slab_automove.c

Packit 4e8bc4
/*  Copyright 2017 Facebook.
Packit 4e8bc4
 *
Packit 4e8bc4
 *  Use and distribution licensed under the BSD license.  See
Packit 4e8bc4
 *  the LICENSE file for full text.
Packit 4e8bc4
 */
Packit 4e8bc4
Packit 4e8bc4
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
Packit 4e8bc4
#include "memcached.h"
Packit 4e8bc4
#include "slab_automove.h"
Packit 4e8bc4
#include <stdlib.h>
Packit 4e8bc4
#include <string.h>
Packit 4e8bc4
Packit 4e8bc4
#define MIN_PAGES_FOR_SOURCE 2
Packit 4e8bc4
#define MIN_PAGES_FOR_RECLAIM 2.5
Packit 4e8bc4
Packit 4e8bc4
struct window_data {
Packit 4e8bc4
    uint64_t age;
Packit 4e8bc4
    uint64_t dirty;
Packit 4e8bc4
    uint64_t evicted;
Packit 4e8bc4
};
Packit 4e8bc4
Packit 4e8bc4
typedef struct {
Packit 4e8bc4
    struct window_data *window_data;
Packit 4e8bc4
    uint32_t window_size;
Packit 4e8bc4
    uint32_t window_cur;
Packit 4e8bc4
    double max_age_ratio;
Packit 4e8bc4
    item_stats_automove iam_before[MAX_NUMBER_OF_SLAB_CLASSES];
Packit 4e8bc4
    item_stats_automove iam_after[MAX_NUMBER_OF_SLAB_CLASSES];
Packit 4e8bc4
    slab_stats_automove sam_before[MAX_NUMBER_OF_SLAB_CLASSES];
Packit 4e8bc4
    slab_stats_automove sam_after[MAX_NUMBER_OF_SLAB_CLASSES];
Packit 4e8bc4
} slab_automove;
Packit 4e8bc4
Packit 4e8bc4
void *slab_automove_init(struct settings *settings) {
Packit 4e8bc4
    uint32_t window_size = settings->slab_automove_window;
Packit 4e8bc4
    double max_age_ratio = settings->slab_automove_ratio;
Packit 4e8bc4
    slab_automove *a = calloc(1, sizeof(slab_automove));
Packit 4e8bc4
    if (a == NULL)
Packit 4e8bc4
        return NULL;
Packit 4e8bc4
    a->window_data = calloc(window_size * MAX_NUMBER_OF_SLAB_CLASSES, sizeof(struct window_data));
Packit 4e8bc4
    a->window_size = window_size;
Packit 4e8bc4
    a->max_age_ratio = max_age_ratio;
Packit 4e8bc4
    if (a->window_data == NULL) {
Packit 4e8bc4
        free(a);
Packit 4e8bc4
        return NULL;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    // do a dry run to fill the before structs
Packit 4e8bc4
    fill_item_stats_automove(a->iam_before);
Packit 4e8bc4
    fill_slab_stats_automove(a->sam_before);
Packit 4e8bc4
Packit 4e8bc4
    return (void *)a;
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
void slab_automove_free(void *arg) {
Packit 4e8bc4
    slab_automove *a = (slab_automove *)arg;
Packit 4e8bc4
    free(a->window_data);
Packit 4e8bc4
    free(a);
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
static void window_sum(struct window_data *wd, struct window_data *w, uint32_t size) {
Packit 4e8bc4
    int x;
Packit 4e8bc4
    for (x = 0; x < size; x++) {
Packit 4e8bc4
        struct window_data *d = &wd[x];
Packit 4e8bc4
        w->age += d->age;
Packit 4e8bc4
        w->dirty += d->dirty;
Packit 4e8bc4
        w->evicted += d->evicted;
Packit 4e8bc4
    }
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
// TODO: if oldest is dirty, find next oldest.
Packit 4e8bc4
// still need to base ratio off of absolute age
Packit 4e8bc4
void slab_automove_run(void *arg, int *src, int *dst) {
Packit 4e8bc4
    slab_automove *a = (slab_automove *)arg;
Packit 4e8bc4
    int n;
Packit 4e8bc4
    struct window_data w_sum;
Packit 4e8bc4
    int oldest = -1;
Packit 4e8bc4
    uint64_t oldest_age = 0;
Packit 4e8bc4
    int youngest = -1;
Packit 4e8bc4
    uint64_t youngest_age = ~0;
Packit 4e8bc4
    bool youngest_evicting = false;
Packit 4e8bc4
    *src = -1;
Packit 4e8bc4
    *dst = -1;
Packit 4e8bc4
Packit 4e8bc4
    // fill after structs
Packit 4e8bc4
    fill_item_stats_automove(a->iam_after);
Packit 4e8bc4
    fill_slab_stats_automove(a->sam_after);
Packit 4e8bc4
    a->window_cur++;
Packit 4e8bc4
Packit 4e8bc4
    // iterate slabs
Packit 4e8bc4
    for (n = POWER_SMALLEST; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) {
Packit 4e8bc4
        int w_offset = n * a->window_size;
Packit 4e8bc4
        struct window_data *wd = &a->window_data[w_offset + (a->window_cur % a->window_size)];
Packit 4e8bc4
        memset(wd, 0, sizeof(struct window_data));
Packit 4e8bc4
        // summarize the window-up-to-now.
Packit 4e8bc4
        memset(&w_sum, 0, sizeof(struct window_data));
Packit 4e8bc4
        window_sum(&a->window_data[w_offset], &w_sum, a->window_size);
Packit 4e8bc4
Packit 4e8bc4
        // if page delta, or evicted delta, mark window dirty
Packit 4e8bc4
        // (or outofmemory)
Packit 4e8bc4
        if (a->iam_after[n].evicted - a->iam_before[n].evicted > 0 ||
Packit 4e8bc4
            a->iam_after[n].outofmemory - a->iam_before[n].outofmemory > 0) {
Packit 4e8bc4
            wd->evicted = 1;
Packit 4e8bc4
            wd->dirty = 1;
Packit 4e8bc4
        }
Packit 4e8bc4
        if (a->sam_after[n].total_pages - a->sam_before[n].total_pages > 0) {
Packit 4e8bc4
            wd->dirty = 1;
Packit 4e8bc4
        }
Packit 4e8bc4
Packit 4e8bc4
        // set age into window
Packit 4e8bc4
        wd->age = a->iam_after[n].age;
Packit 4e8bc4
Packit 4e8bc4
        // grab age as average of window total
Packit 4e8bc4
        uint64_t age = w_sum.age / a->window_size;
Packit 4e8bc4
Packit 4e8bc4
        // if > N free chunks and not dirty, make decision.
Packit 4e8bc4
        if (a->sam_after[n].free_chunks > a->sam_after[n].chunks_per_page * MIN_PAGES_FOR_RECLAIM) {
Packit 4e8bc4
            if (w_sum.dirty == 0) {
Packit 4e8bc4
                *src = n;
Packit 4e8bc4
                *dst = 0;
Packit 4e8bc4
                break;
Packit 4e8bc4
            }
Packit 4e8bc4
        }
Packit 4e8bc4
Packit 4e8bc4
        // if oldest and have enough pages, is oldest
Packit 4e8bc4
        if (age > oldest_age && a->sam_after[n].total_pages > MIN_PAGES_FOR_SOURCE) {
Packit 4e8bc4
            oldest = n;
Packit 4e8bc4
            oldest_age = age;
Packit 4e8bc4
        }
Packit 4e8bc4
Packit 4e8bc4
        // grab evicted count from window
Packit 4e8bc4
        // if > half the window and youngest, mark as youngest
Packit 4e8bc4
        if (age < youngest_age && w_sum.evicted > a->window_size / 2) {
Packit 4e8bc4
            youngest = n;
Packit 4e8bc4
            youngest_age = age;
Packit 4e8bc4
            youngest_evicting = wd->evicted ? true : false;
Packit 4e8bc4
        }
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    memcpy(a->iam_before, a->iam_after,
Packit 4e8bc4
            sizeof(item_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES);
Packit 4e8bc4
    memcpy(a->sam_before, a->sam_after,
Packit 4e8bc4
            sizeof(slab_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES);
Packit 4e8bc4
    // if we have a youngest and oldest, and oldest is outside the ratio,
Packit 4e8bc4
    // also, only make decisions if window has filled once.
Packit 4e8bc4
    if (youngest != -1 && oldest != -1 && a->window_cur > a->window_size) {
Packit 4e8bc4
        if (youngest_age < ((double)oldest_age * a->max_age_ratio) && youngest_evicting) {
Packit 4e8bc4
            *src = oldest;
Packit 4e8bc4
            *dst = youngest;
Packit 4e8bc4
        }
Packit 4e8bc4
    }
Packit 4e8bc4
    return;
Packit 4e8bc4
}