Skip to content

Instantly share code, notes, and snippets.

@markshannon
Last active July 31, 2021 09:32
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markshannon/556ccc0e99517c25a70e2fe551917c03 to your computer and use it in GitHub Desktop.
Save markshannon/556ccc0e99517c25a70e2fe551917c03 to your computer and use it in GitHub Desktop.

Data structures

typedef struct {
    uint32_t ch_version;
    uint16_t ch_index;
    uint8_t ch_counter;
    uint8_t ch_oparg;
} HotPyVersionAndCounters;

typedef union {
    PyObject *object;
    HotPyVersionAndCounters version;
} HotPyCacheEntry;

Instructions

TARGET(LOAD_GLOBAL_ADAPTIVE) {
    HotPyCacheEntry *cache = GET_CACHE();
    assert(cache->ch_kind == CACHE_KIND_UNUSED);
    HotPyVersionAndCounters *version = &cache[0].version;
    if (version->ch_counter == 0) {
        PyObject *name = GETITEM(names, version->ch_oparg);
        next_instr--;
        hotpy_adapt_load_global(next_instr, f, name, cache);
        assert(!_PyErr_Occurred(tstate));
    }
    else {
        version->ch_counter--;
        oparg = cache->ch_oparg;
        JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
    }
}

TARGET(LOAD_GLOBAL_FROM_MODULE) {
    PyObject *v;
    HotPyCacheEntry *cache = GET_CACHE();
    HotPyVersionAndCounters *version = &cache[0].version;
    PyObject *globals = f->f_globals;
    MISS_IF(!PyDict_CheckExact(globals), LOAD_GLOBAL);
    MISS_IF(((PyDictObject *)globals)->ma_keys->dk_version != version->ch_version, LOAD_GLOBAL);
    v = ((PyDictObject *)globals)->ma_values[version->ch_index];
    record_cache_hit(cache);
    if (v == NULL) {
        /* Error -- handle it in LOAD_GLOBAL */
        oparg = version->ch_oparg;
        JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
    }
    Py_INCREF(v);
    PUSH(v);
}

TARGET(LOAD_GLOBAL_FROM_BUILTIN) {
    PyObject *v;
    HotPyCacheEntry *cache = GET_CACHE();
    HotPyVersionAndCounters *version = &cache[0].version;
    PyObject *globals = f->f_globals;
    PyObject *builtins = f->f_builtins;
    MISS_IF(!PyDict_CheckExact(globals), LOAD_GLOBAL);
    MISS_IF(((PyDictObject *)globals)->ma_keys->dk_version != version.ch_version, LOAD_GLOBAL);
    MISS_IF(!PyDict_CheckExact(builtins), LOAD_GLOBAL);
    MISS_IF(((PyDictObject *)builtins)->ma_keys->dk_version != cache[1].version.ch_version, LOAD_GLOBAL);
    v = ((PyDictObject *)builtins)->ma_values[version->ch_index];
    if (v == NULL) {
        /* Error -- handle it in LOAD_GLOBAL */
        oparg = version->ch_oparg;
        JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
    }
    record_cache_hit(cache);
    Py_INCREF(v);
    PUSH(v);
}

Handling mis-specialization

The macro MISS_IF(condition, op) expands to:

    if (UNLIKELY(condition)) goto op ## miss;

And the code for handling misses looks like this:

LOAD_GLOBAL_miss:
    HotPyCacheEntry *cache = GET_CACHE();
    record_cache_miss(cache);
    if (too_many_cache_misses(cache)) {
        cache_deopt(next_instr-1, cache, LOAD_GLOBAL);
    }
    oparg = cache->version.ch_oparg;
    JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
}

Helper functions and macros

#define GET_CACHE() (&caches[((next_instr-first_instr)>>1) + oparg])
#define JUMP_TO_INSTRUCTION(op) goto target_ ## op

void record_cache_hit(HotPyCacheEntry *entry) {
    saturating_increment(&entry->version.ch_counter);
}

void record_cache_miss(HotPyCacheEntry *entry) {
    saturating_decrement(&entry->version.ch_counter);
}

int too_many_cache_misses(HotPyCacheEntry *entry) {
    return entry->version.ch_counter == saturating_minimum();
}

void cache_deopt(Instruction *i, HotPyCacheEntry *entry, int opcode) {
    i->opcode = opcode;
    entry->version.ch_counter = BACKOFF;
    i->oparg = entry->version.ch_oparg;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment