diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 520a74b8a61fde..09c56ed2f19ddd 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -60,6 +60,7 @@ struct _ceval_runtime_state { /* Request for checking signals. */ _Py_atomic_int signals_pending; struct _gil_runtime_state gil; + struct int opcode_cache_minruns; }; /* interpreter state */ @@ -312,6 +313,9 @@ PyAPI_FUNC(void) _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime); PyAPI_FUNC(void) _PyGILState_Reinit(_PyRuntimeState *runtime); +PyAPI_FUNC(void) _PyEval_SetOpcodeCacheMinRuns(int minruns); +PyAPI_FUNC(int) _PyEval_GetOpcodeCacheMinRuns(void); + #ifdef __cplusplus } #endif diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index 8d221232eb6ce7..598347e1ffc625 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -30,6 +30,8 @@ def dash_R(ns, test_name, test_func): if not hasattr(sys, 'gettotalrefcount'): raise Exception("Tracking reference leaks requires a debug build " "of Python") + minruns = sys._getopcacheminruns() + sys._setopcacheminruns(1) # Avoid false positives due to various caches # filling slowly with random data: @@ -104,6 +106,7 @@ def get_pooled_int(value): rc_before = rc_after fd_before = fd_after + sys._setopcacheminruns(minruns) if not ns.quiet: print(file=sys.stderr) diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index a43b7666cd1e35..d8f74557cb2923 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -243,7 +243,6 @@ def _runtest_inner2(ns, test_name): gc.garbage.clear() support.reap_children() - return refleak diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-03-23-29-35.bpo-37146.5B7ZiD.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-03-23-29-35.bpo-37146.5B7ZiD.rst new file mode 100644 index 00000000000000..81ba8e7e84dc08 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-03-23-29-35.bpo-37146.5B7ZiD.rst @@ -0,0 +1,3 @@ +Added ``sys._setopcacheminruns`` and ``sys._getopcacheminruns`` to allow +configuring the minimum number of function executions to activate the global +opcode cache. diff --git a/Python/ceval.c b/Python/ceval.c index 0a4af915d6ffe0..9767785ab6b3c3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -103,7 +103,6 @@ static long dxp[256]; #endif /* per opcode cache */ -#define OPCACHE_MIN_RUNS 1024 /* create opcache when code executed this time */ #define OPCACHE_STATS 0 /* Enable stats */ #if OPCACHE_STATS @@ -1152,9 +1151,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ f->f_executing = 1; - if (co->co_opcache_flag < OPCACHE_MIN_RUNS) { + int opcacheminruns = _PyEval_GetOpcodeCacheMinRuns(); + if (co->co_opcache_flag < opcacheminruns) { co->co_opcache_flag++; - if (co->co_opcache_flag == OPCACHE_MIN_RUNS) { + if (co->co_opcache_flag == opcacheminruns) { if (_PyCode_InitOpcache(co) < 0) { return NULL; } @@ -5632,3 +5632,15 @@ maybe_dtrace_line(PyFrameObject *frame, } *instr_prev = frame->f_lasti; } + +void +_PyEval_SetOpcodeCacheMinRuns(int minruns) +{ + _PyRuntime.ceval.opcode_cache_minruns = minruns; +} + +int +_PyEval_GetOpcodeCacheMinRuns() +{ + return _PyRuntime.ceval.opcode_cache_minruns; +} diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 6f248ff18d9d7f..b5635cf3fbd5ed 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -337,6 +337,67 @@ sys_getcheckinterval(PyObject *module, PyObject *Py_UNUSED(ignored)) return sys_getcheckinterval_impl(module); } +PyDoc_STRVAR(sys__setopcacheminruns__doc__, +"_setopcacheminruns($module, minruns, /)\n" +"--\n" +"\n" +"Set the minimum value of runs to activate opcode cache."); + +#define SYS__SETOPCACHEMINRUNS_METHODDEF \ + {"_setopcacheminruns", (PyCFunction)sys__setopcacheminruns, METH_O, sys__setopcacheminruns__doc__}, + +static PyObject * +sys__setopcacheminruns_impl(PyObject *module, int minruns); + +static PyObject * +sys__setopcacheminruns(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int minruns; + + if (PyFloat_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + } + minruns = _PyLong_AsInt(arg); + if (minruns == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = sys__setopcacheminruns_impl(module, minruns); + +exit: + return return_value; +} + +PyDoc_STRVAR(sys__getopcacheminruns__doc__, +"_getopcacheminruns($module, /)\n" +"--\n" +"\n" +"Return the current minimum value of runs to activate opcode cache."); + +#define SYS__GETOPCACHEMINRUNS_METHODDEF \ + {"_getopcacheminruns", (PyCFunction)sys__getopcacheminruns, METH_NOARGS, sys__getopcacheminruns__doc__}, + +static int +sys__getopcacheminruns_impl(PyObject *module); + +static PyObject * +sys__getopcacheminruns(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = sys__getopcacheminruns_impl(module); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromLong((long)_return_value); + +exit: + return return_value; +} + PyDoc_STRVAR(sys_setswitchinterval__doc__, "setswitchinterval($module, interval, /)\n" "--\n" @@ -1082,4 +1143,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=43c4fde7b5783d8d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7cf1810bb97ca9a4 input=a9049054013a1b77]*/ diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index fca2ee65519104..2474698944c8cc 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -737,6 +737,8 @@ _Py_PreInitializeFromPyArgv(const PyPreConfig *src_config, const _PyArgv *args) return status; } + runtime->ceval.opcodeconfig.minruns = 1024; + runtime->pre_initialized = 1; return _PyStatus_OK(); } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 97bff94d8b4196..a464e0b38c7aee 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1030,6 +1030,44 @@ sys_getcheckinterval_impl(PyObject *module) return PyLong_FromLong(interp->check_interval); } +/*[clinic input] +sys._setopcacheminruns + + minruns: int + / + +Set the minimum value of runs to activate opcode cache. +[clinic start generated code]*/ + +static PyObject * +sys__setopcacheminruns_impl(PyObject *module, int minruns) +/*[clinic end generated code: output=e29d082b62b2aa8a input=169a982eee6595d5]*/ +{ + if (minruns <= 0) { + PyErr_SetString(PyExc_ValueError, + "opcache min runs must be strictly positive"); + return NULL; + } + _PyEval_SetOpcodeCacheMinRuns(minruns); + Py_RETURN_NONE; +} + + +/*[clinic input] +sys._getopcacheminruns -> int + +Return the current minimum value of runs to activate opcode cache. +[clinic start generated code]*/ + +static int +sys__getopcacheminruns_impl(PyObject *module) +/*[clinic end generated code: output=f25d23b7afd8512c input=93784822a3e883c9]*/ +{ + return _PyEval_GetOpcodeCacheMinRuns(); +} + + + /*[clinic input] sys.setswitchinterval @@ -1936,6 +1974,8 @@ static PyMethodDef sys_methods[] = { SYS_MDEBUG_METHODDEF SYS_SETCHECKINTERVAL_METHODDEF SYS_GETCHECKINTERVAL_METHODDEF + SYS__SETOPCACHEMINRUNS_METHODDEF + SYS__GETOPCACHEMINRUNS_METHODDEF SYS_SETSWITCHINTERVAL_METHODDEF SYS_GETSWITCHINTERVAL_METHODDEF SYS_SETDLOPENFLAGS_METHODDEF