diff options
author | Mark Shannon <mark@hotpy.org> | 2021-06-14 10:04:09 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-14 10:04:09 (GMT) |
commit | eecbc7c3900a7f40d8498b151db543a202c72f74 (patch) | |
tree | 6be5d67366f8df3e24c3dbed0786ec3c4a29bf1b /Python/ceval.c | |
parent | fafcfff9262ae9dee03a00006638dfcbcfc23a7b (diff) | |
download | cpython-eecbc7c3900a7f40d8498b151db543a202c72f74.zip cpython-eecbc7c3900a7f40d8498b151db543a202c72f74.tar.gz cpython-eecbc7c3900a7f40d8498b151db543a202c72f74.tar.bz2 |
bpo-44338: Port LOAD_GLOBAL to PEP 659 adaptive interpreter (GH-26638)
* Add specializations of LOAD_GLOBAL.
* Add more stats.
* Remove old opcache; it is no longer used.
* Add NEWS
Diffstat (limited to 'Python/ceval.c')
-rw-r--r-- | Python/ceval.c | 314 |
1 files changed, 83 insertions, 231 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index c42404c..25d077c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -13,7 +13,7 @@ #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_call.h" // _PyObject_FastCallDictTstate() #include "pycore_ceval.h" // _PyEval_SignalAsyncExc() -#include "pycore_code.h" // _PyCode_InitOpcache() +#include "pycore_code.h" #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_moduleobject.h" @@ -109,7 +109,6 @@ static long dxp[256]; /* per opcode cache */ static int opcache_min_runs = 1024; /* create opcache when code executed this many times */ #define OPCODE_CACHE_MAX_TRIES 20 -#define OPCACHE_STATS 0 /* Enable stats */ // This function allows to deactivate the opcode cache. As different cache mechanisms may hold // references, this can mess with the reference leak detector functionality so the cache needs @@ -120,22 +119,6 @@ _PyEval_DeactivateOpCache(void) opcache_min_runs = 0; } -#if OPCACHE_STATS -static size_t opcache_code_objects = 0; -static size_t opcache_code_objects_extra_mem = 0; - -static size_t opcache_global_opts = 0; -static size_t opcache_global_hits = 0; -static size_t opcache_global_misses = 0; - -static size_t opcache_attr_opts = 0; -static size_t opcache_attr_hits = 0; -static size_t opcache_attr_misses = 0; -static size_t opcache_attr_deopts = 0; -static size_t opcache_attr_total = 0; -#endif - - #ifndef NDEBUG /* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and PyEval_RestoreThread(). Detect if tstate memory was freed. It can happen @@ -360,48 +343,8 @@ PyEval_InitThreads(void) void _PyEval_Fini(void) { -#if OPCACHE_STATS - fprintf(stderr, "-- Opcode cache number of objects = %zd\n", - opcache_code_objects); - - fprintf(stderr, "-- Opcode cache total extra mem = %zd\n", - opcache_code_objects_extra_mem); - - fprintf(stderr, "\n"); - - fprintf(stderr, "-- Opcode cache LOAD_GLOBAL hits = %zd (%d%%)\n", - opcache_global_hits, - (int) (100.0 * opcache_global_hits / - (opcache_global_hits + opcache_global_misses))); - - fprintf(stderr, "-- Opcode cache LOAD_GLOBAL misses = %zd (%d%%)\n", - opcache_global_misses, - (int) (100.0 * opcache_global_misses / - (opcache_global_hits + opcache_global_misses))); - - fprintf(stderr, "-- Opcode cache LOAD_GLOBAL opts = %zd\n", - opcache_global_opts); - - fprintf(stderr, "\n"); - - fprintf(stderr, "-- Opcode cache LOAD_ATTR hits = %zd (%d%%)\n", - opcache_attr_hits, - (int) (100.0 * opcache_attr_hits / - opcache_attr_total)); - - fprintf(stderr, "-- Opcode cache LOAD_ATTR misses = %zd (%d%%)\n", - opcache_attr_misses, - (int) (100.0 * opcache_attr_misses / - opcache_attr_total)); - - fprintf(stderr, "-- Opcode cache LOAD_ATTR opts = %zd\n", - opcache_attr_opts); - - fprintf(stderr, "-- Opcode cache LOAD_ATTR deopts = %zd\n", - opcache_attr_deopts); - - fprintf(stderr, "-- Opcode cache LOAD_ATTR total = %zd\n", - opcache_attr_total); +#if SPECIALIZATION_STATS + _Py_PrintSpecializationStats(); #endif } @@ -1448,108 +1391,11 @@ eval_frame_handle_pending(PyThreadState *tstate) GETLOCAL(i) = value; \ Py_XDECREF(tmp); } while (0) - /* macros for opcode cache */ -#define OPCACHE_CHECK() \ - do { \ - co_opcache = NULL; \ - if (co->co_opcache != NULL) { \ - unsigned char co_opcache_offset = \ - co->co_opcache_map[next_instr - first_instr]; \ - if (co_opcache_offset > 0) { \ - assert(co_opcache_offset <= co->co_opcache_size); \ - co_opcache = &co->co_opcache[co_opcache_offset - 1]; \ - assert(co_opcache != NULL); \ - } \ - } \ - } while (0) - -#define OPCACHE_DEOPT() \ - do { \ - if (co_opcache != NULL) { \ - co_opcache->optimized = -1; \ - unsigned char co_opcache_offset = \ - co->co_opcache_map[next_instr - first_instr]; \ - assert(co_opcache_offset <= co->co_opcache_size); \ - co->co_opcache_map[co_opcache_offset] = 0; \ - co_opcache = NULL; \ - } \ - } while (0) - -#define OPCACHE_DEOPT_LOAD_ATTR() \ - do { \ - if (co_opcache != NULL) { \ - OPCACHE_STAT_ATTR_DEOPT(); \ - OPCACHE_DEOPT(); \ - } \ - } while (0) - -#define OPCACHE_MAYBE_DEOPT_LOAD_ATTR() \ - do { \ - if (co_opcache != NULL && --co_opcache->optimized <= 0) { \ - OPCACHE_DEOPT_LOAD_ATTR(); \ - } \ - } while (0) - -#if OPCACHE_STATS - -#define OPCACHE_STAT_GLOBAL_HIT() \ - do { \ - if (co->co_opcache != NULL) opcache_global_hits++; \ - } while (0) - -#define OPCACHE_STAT_GLOBAL_MISS() \ - do { \ - if (co->co_opcache != NULL) opcache_global_misses++; \ - } while (0) - -#define OPCACHE_STAT_GLOBAL_OPT() \ - do { \ - if (co->co_opcache != NULL) opcache_global_opts++; \ - } while (0) - -#define OPCACHE_STAT_ATTR_HIT() \ - do { \ - if (co->co_opcache != NULL) opcache_attr_hits++; \ - } while (0) - -#define OPCACHE_STAT_ATTR_MISS() \ - do { \ - if (co->co_opcache != NULL) opcache_attr_misses++; \ - } while (0) - -#define OPCACHE_STAT_ATTR_OPT() \ - do { \ - if (co->co_opcache!= NULL) opcache_attr_opts++; \ - } while (0) - -#define OPCACHE_STAT_ATTR_DEOPT() \ - do { \ - if (co->co_opcache != NULL) opcache_attr_deopts++; \ - } while (0) - -#define OPCACHE_STAT_ATTR_TOTAL() \ - do { \ - if (co->co_opcache != NULL) opcache_attr_total++; \ - } while (0) - -#else /* OPCACHE_STATS */ - -#define OPCACHE_STAT_GLOBAL_HIT() -#define OPCACHE_STAT_GLOBAL_MISS() -#define OPCACHE_STAT_GLOBAL_OPT() - -#define OPCACHE_STAT_ATTR_HIT() -#define OPCACHE_STAT_ATTR_MISS() -#define OPCACHE_STAT_ATTR_OPT() -#define OPCACHE_STAT_ATTR_DEOPT() -#define OPCACHE_STAT_ATTR_TOTAL() - #define JUMP_TO_INSTRUCTION(op) goto PREDICT_ID(op) #define GET_CACHE() \ _GetSpecializedCacheEntryForInstruction(first_instr, INSTR_OFFSET(), oparg) -#endif #define DEOPT_IF(cond, instname) if (cond) { goto instname ## _miss; } @@ -1582,7 +1428,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) _Py_CODEUNIT *first_instr; PyObject *names; PyObject *consts; - _PyOpcache *co_opcache; #ifdef LLTRACE _Py_IDENTIFIER(__ltrace__); @@ -1690,21 +1535,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) f->f_stackdepth = -1; f->f_state = FRAME_EXECUTING; - if (co->co_opcache_flag < opcache_min_runs) { - co->co_opcache_flag++; - if (co->co_opcache_flag == opcache_min_runs) { - if (_PyCode_InitOpcache(co) < 0) { - goto exit_eval_frame; - } -#if OPCACHE_STATS - opcache_code_objects_extra_mem += - PyBytes_Size(co->co_code) / sizeof(_Py_CODEUNIT) + - sizeof(_PyOpcache) * co->co_opcache_size; - opcache_code_objects++; -#endif - } - } - #ifdef LLTRACE { int r = _PyDict_ContainsId(GLOBALS(), &PyId___ltrace__); @@ -2974,30 +2804,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } case TARGET(LOAD_GLOBAL): { - PyObject *name; + PREDICTED(LOAD_GLOBAL); + PyObject *name = GETITEM(names, oparg); PyObject *v; if (PyDict_CheckExact(GLOBALS()) && PyDict_CheckExact(BUILTINS())) { - OPCACHE_CHECK(); - if (co_opcache != NULL && co_opcache->optimized > 0) { - _PyOpcache_LoadGlobal *lg = &co_opcache->u.lg; - - if (lg->globals_ver == - ((PyDictObject *)GLOBALS())->ma_version_tag - && lg->builtins_ver == - ((PyDictObject *)BUILTINS())->ma_version_tag) - { - PyObject *ptr = lg->ptr; - OPCACHE_STAT_GLOBAL_HIT(); - assert(ptr != NULL); - Py_INCREF(ptr); - PUSH(ptr); - DISPATCH(); - } - } - - name = GETITEM(names, oparg); v = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), (PyDictObject *)BUILTINS(), name); @@ -3010,25 +2822,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) } goto error; } - - if (co_opcache != NULL) { - _PyOpcache_LoadGlobal *lg = &co_opcache->u.lg; - - if (co_opcache->optimized == 0) { - /* Wasn't optimized before. */ - OPCACHE_STAT_GLOBAL_OPT(); - } else { - OPCACHE_STAT_GLOBAL_MISS(); - } - - co_opcache->optimized = 1; - lg->globals_ver = - ((PyDictObject *)GLOBALS())->ma_version_tag; - lg->builtins_ver = - ((PyDictObject *)BUILTINS())->ma_version_tag; - lg->ptr = v; /* borrowed */ - } - Py_INCREF(v); } else { @@ -3059,6 +2852,61 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) DISPATCH(); } + case TARGET(LOAD_GLOBAL_ADAPTIVE): { + SpecializedCacheEntry *cache = GET_CACHE(); + if (cache->adaptive.counter == 0) { + PyObject *name = GETITEM(names, cache->adaptive.original_oparg); + next_instr--; + if (_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name, cache) < 0) { + goto error; + } + DISPATCH(); + } + else { + STAT_INC(LOAD_GLOBAL, deferred); + cache->adaptive.counter--; + oparg = cache->adaptive.original_oparg; + JUMP_TO_INSTRUCTION(LOAD_GLOBAL); + } + } + + case TARGET(LOAD_GLOBAL_MODULE): { + DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + SpecializedCacheEntry *caches = GET_CACHE(); + _PyAdaptiveEntry *cache0 = &caches[0].adaptive; + _PyLoadGlobalCache *cache1 = &caches[-1].load_global; + DEOPT_IF(dict->ma_keys->dk_version != cache1->module_keys_version, LOAD_GLOBAL); + PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + cache0->index; + PyObject *res = ep->me_value; + DEOPT_IF(res == NULL, LOAD_GLOBAL); + record_cache_hit(cache0); + STAT_INC(LOAD_GLOBAL, hit); + Py_INCREF(res); + PUSH(res); + DISPATCH(); + } + + case TARGET(LOAD_GLOBAL_BUILTIN): { + DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); + DEOPT_IF(!PyDict_CheckExact(BUILTINS()), LOAD_GLOBAL); + PyDictObject *mdict = (PyDictObject *)GLOBALS(); + PyDictObject *bdict = (PyDictObject *)BUILTINS(); + SpecializedCacheEntry *caches = GET_CACHE(); + _PyAdaptiveEntry *cache0 = &caches[0].adaptive; + _PyLoadGlobalCache *cache1 = &caches[-1].load_global; + DEOPT_IF(mdict->ma_keys->dk_version != cache1->module_keys_version, LOAD_GLOBAL); + DEOPT_IF(bdict->ma_keys->dk_version != cache1->builtin_keys_version, LOAD_GLOBAL); + PyDictKeyEntry *ep = DK_ENTRIES(bdict->ma_keys) + cache0->index; + PyObject *res = ep->me_value; + DEOPT_IF(res == NULL, LOAD_GLOBAL); + record_cache_hit(cache0); + STAT_INC(LOAD_GLOBAL, hit); + Py_INCREF(res); + PUSH(res); + DISPATCH(); + } + case TARGET(DELETE_FAST): { PyObject *v = GETLOCAL(oparg); if (v != NULL) { @@ -3464,7 +3312,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) DISPATCH(); } else { - STAT_INC(loadattr_deferred); + STAT_INC(LOAD_ATTR, deferred); cache->adaptive.counter--; oparg = cache->adaptive.original_oparg; JUMP_TO_INSTRUCTION(LOAD_ATTR); @@ -3487,9 +3335,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, LOAD_ATTR); res = dict->ma_values[cache0->index]; DEOPT_IF(res == NULL, LOAD_ATTR); - STAT_INC(loadattr_hit); + STAT_INC(LOAD_ATTR, hit); record_cache_hit(cache0); - STAT_INC(loadattr_hit); + STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); SET_TOP(res); Py_DECREF(owner); @@ -3510,7 +3358,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + cache0->index; res = ep->me_value; DEOPT_IF(res == NULL, LOAD_ATTR); - STAT_INC(loadattr_hit); + STAT_INC(LOAD_ATTR, hit); record_cache_hit(cache0); Py_INCREF(res); SET_TOP(res); @@ -3538,7 +3386,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) DEOPT_IF(ep->me_key != name, LOAD_ATTR); res = ep->me_value; DEOPT_IF(res == NULL, LOAD_ATTR); - STAT_INC(loadattr_hit); + STAT_INC(LOAD_ATTR, hit); record_cache_hit(cache0); Py_INCREF(res); SET_TOP(res); @@ -3558,7 +3406,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) char *addr = (char *)owner + cache0->index; res = *(PyObject **)addr; DEOPT_IF(res == NULL, LOAD_ATTR); - STAT_INC(loadattr_hit); + STAT_INC(LOAD_ATTR, hit); record_cache_hit(cache0); Py_INCREF(res); SET_TOP(res); @@ -4445,22 +4293,26 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) or goto error. */ Py_UNREACHABLE(); -/* Cache misses */ +/* Specialization misses */ -LOAD_ATTR_miss: - { - STAT_INC(loadattr_miss); - _PyAdaptiveEntry *cache = &GET_CACHE()->adaptive; - record_cache_miss(cache); - if (too_many_cache_misses(cache)) { - next_instr[-1] = _Py_MAKECODEUNIT(LOAD_ATTR_ADAPTIVE, _Py_OPARG(next_instr[-1])); - STAT_INC(loadattr_deopt); - cache_backoff(cache); - } - oparg = cache->original_oparg; - JUMP_TO_INSTRUCTION(LOAD_ATTR); +#define MISS_WITH_CACHE(opname) \ +opname ## _miss: \ + { \ + STAT_INC(opname, miss); \ + _PyAdaptiveEntry *cache = &GET_CACHE()->adaptive; \ + record_cache_miss(cache); \ + if (too_many_cache_misses(cache)) { \ + next_instr[-1] = _Py_MAKECODEUNIT(opname ## _ADAPTIVE, _Py_OPARG(next_instr[-1])); \ + STAT_INC(opname, deopt); \ + cache_backoff(cache); \ + } \ + oparg = cache->original_oparg; \ + JUMP_TO_INSTRUCTION(opname); \ } +MISS_WITH_CACHE(LOAD_ATTR) +MISS_WITH_CACHE(LOAD_GLOBAL) + error: /* Double-check exception status. */ #ifdef NDEBUG |