summaryrefslogtreecommitdiffstats
path: root/Python/ceval.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-06-14 10:04:09 (GMT)
committerGitHub <noreply@github.com>2021-06-14 10:04:09 (GMT)
commiteecbc7c3900a7f40d8498b151db543a202c72f74 (patch)
tree6be5d67366f8df3e24c3dbed0786ec3c4a29bf1b /Python/ceval.c
parentfafcfff9262ae9dee03a00006638dfcbcfc23a7b (diff)
downloadcpython-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.c314
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