Skip to content

bpo-40137: Micro-optimize _PyType_GetModuleByDef() #25393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions Include/internal/pycore_moduleobject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef Py_INTERNAL_MODULEOBJECT_H
#define Py_INTERNAL_MODULEOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif

typedef struct {
PyObject_HEAD
PyObject *md_dict;
struct PyModuleDef *md_def;
void *md_state;
PyObject *md_weaklist;
PyObject *md_name; /* for logging purposes after md_dict is cleared */
} PyModuleObject;

static inline PyModuleDef* _PyModule_GetDef(PyObject *m) {
assert(PyModule_Check(m));
return ((PyModuleObject *)m)->md_def;
}

static inline void* _PyModule_GetState(PyObject* m) {
assert(PyModule_Check(m));
return ((PyModuleObject *)m)->md_state;
}


#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_MODULEOBJECT_H */
1 change: 1 addition & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_interp.h \
$(srcdir)/Include/internal/pycore_list.h \
$(srcdir)/Include/internal/pycore_long.h \
$(srcdir)/Include/internal/pycore_moduleobject.h \
$(srcdir)/Include/internal/pycore_object.h \
$(srcdir)/Include/internal/pycore_pathconfig.h \
$(srcdir)/Include/internal/pycore_pyarena.h \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Micro-optimize _PyType_GetModuleByDef() to make it 2.3 ns faster (43.2 ns +-
0.7 ns -> 40.9 ns +- 1.0 ns). Patch by Victor Stinner.
3 changes: 2 additions & 1 deletion Modules/_abc.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* ABCMeta implementation */

#include "Python.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "clinic/_abc.c.h"

/*[clinic input]
Expand All @@ -27,7 +28,7 @@ typedef struct {
static inline _abcmodule_state*
get_abc_state(PyObject *module)
{
void *state = PyModule_GetState(module);
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (_abcmodule_state *)state;
}
Expand Down
6 changes: 3 additions & 3 deletions Modules/_functoolsmodule.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Python.h"
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_object.h" // _PyObject_GC_TRACK
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_tuple.h" // _PyTuple_ITEMS()
Expand Down Expand Up @@ -35,7 +36,7 @@ typedef struct _functools_state {
static inline _functools_state *
get_functools_state(PyObject *module)
{
void *state = PyModule_GetState(module);
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (_functools_state *)state;
}
Expand All @@ -52,8 +53,7 @@ get_functools_state_by_type(PyTypeObject *type)
if (module == NULL) {
return NULL;
}
_functools_state *state = get_functools_state(module);
return state;
return get_functools_state(module);
}

static PyObject *
Expand Down
4 changes: 2 additions & 2 deletions Modules/_operator.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

#include "Python.h"

#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "clinic/_operator.c.h"

typedef struct {
Expand All @@ -12,7 +12,7 @@ typedef struct {
static inline _operator_state*
get_operator_state(PyObject *module)
{
void *state = PyModule_GetState(module);
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (_operator_state *)state;
}
Expand Down
3 changes: 2 additions & 1 deletion Modules/_pickle.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#endif

#include "Python.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "structmember.h" // PyMemberDef

PyDoc_STRVAR(pickle_module_doc,
Expand Down Expand Up @@ -182,7 +183,7 @@ static struct PyModuleDef _picklemodule;
static PickleState *
_Pickle_GetState(PyObject *module)
{
return (PickleState *)PyModule_GetState(module);
return (PickleState *)_PyModule_GetState(module);
}

/* Find the module instance imported in the currently running sub-interpreter
Expand Down
3 changes: 2 additions & 1 deletion Modules/_queuemodule.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "Python.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "structmember.h" // PyMemberDef
#include <stddef.h> // offsetof()

Expand All @@ -10,7 +11,7 @@ typedef struct {
static simplequeue_state *
simplequeue_get_state(PyObject *module)
{
simplequeue_state *state = PyModule_GetState(module);
simplequeue_state *state = _PyModule_GetState(module);
assert(state);
return state;
}
Expand Down
5 changes: 3 additions & 2 deletions Modules/_randommodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
/* ---------------------------------------------------------------*/

#include "Python.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
#ifdef HAVE_PROCESS_H
# include <process.h> // getpid()
#endif
Expand All @@ -86,7 +87,7 @@ typedef struct {
static inline _randomstate*
get_random_state(PyObject *module)
{
void *state = PyModule_GetState(module);
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (_randomstate *)state;
}
Expand Down Expand Up @@ -538,7 +539,7 @@ random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

if (PyTuple_GET_SIZE(args) == 1)
arg = PyTuple_GET_ITEM(args, 0);

tmp = random_seed(self, arg);
if (tmp == NULL) {
Py_DECREF(self);
Expand Down
3 changes: 2 additions & 1 deletion Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define PY_SSIZE_T_CLEAN

#include "Python.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "structmember.h" // PyMemberDef
#include <ctype.h>

Expand All @@ -24,7 +25,7 @@ typedef struct {
static inline _structmodulestate*
get_struct_state(PyObject *module)
{
void *state = PyModule_GetState(module);
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (_structmodulestate *)state;
}
Expand Down
3 changes: 2 additions & 1 deletion Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "structmember.h" // PyMemberDef
#include <stddef.h> // offsetof()

Expand Down Expand Up @@ -63,7 +64,7 @@ typedef struct {
static array_state *
get_array_state(PyObject *module)
{
return (array_state *)PyModule_GetState(module);
return (array_state *)_PyModule_GetState(module);
}

#define find_array_state_by_type(tp) \
Expand Down
3 changes: 2 additions & 1 deletion Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "Python.h"
#include "pycore_fileutils.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
#ifdef MS_WINDOWS
/* include <windows.h> early to avoid conflict with pycore_condvar.h:

Expand Down Expand Up @@ -994,7 +995,7 @@ typedef struct {
static inline _posixstate*
get_posix_state(PyObject *module)
{
void *state = PyModule_GetState(module);
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (_posixstate *)state;
}
Expand Down
14 changes: 3 additions & 11 deletions Objects/moduleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "Python.h"
#include "pycore_interp.h" // PyInterpreterState.importlib
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "structmember.h" // PyMemberDef

static Py_ssize_t max_module_number;
Expand All @@ -12,15 +13,6 @@ _Py_IDENTIFIER(__doc__);
_Py_IDENTIFIER(__name__);
_Py_IDENTIFIER(__spec__);

typedef struct {
PyObject_HEAD
PyObject *md_dict;
struct PyModuleDef *md_def;
void *md_state;
PyObject *md_weaklist;
PyObject *md_name; /* for logging purposes after md_dict is cleared */
} PyModuleObject;

static PyMemberDef module_members[] = {
{"__dict__", T_OBJECT, offsetof(PyModuleObject, md_dict), READONLY},
{0}
Expand Down Expand Up @@ -556,7 +548,7 @@ PyModule_GetDef(PyObject* m)
PyErr_BadArgument();
return NULL;
}
return ((PyModuleObject *)m)->md_def;
return _PyModule_GetDef(m);
}

void*
Expand All @@ -566,7 +558,7 @@ PyModule_GetState(PyObject* m)
PyErr_BadArgument();
return NULL;
}
return ((PyModuleObject *)m)->md_state;
return _PyModule_GetState(m);
}

void
Expand Down
29 changes: 23 additions & 6 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "pycore_call.h"
#include "pycore_compile.h" // _Py_Mangle()
#include "pycore_initconfig.h"
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h" // _PyThreadState_GET()
Expand Down Expand Up @@ -3591,10 +3592,25 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
{
assert(PyType_Check(type));
assert(type->tp_mro);
int i;
for (i = 0; i < PyTuple_GET_SIZE(type->tp_mro); i++) {
PyObject *super = PyTuple_GET_ITEM(type->tp_mro, i);
if (!PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) {

// Fast path for type->tp_mro[0]=type. Calling _PyType_GetModuleByDef() on
// the defining type is the most common type.
assert(PyTuple_GET_ITEM(type->tp_mro, 0) == type);
// A static type cannot inherit from a heap type, since heap types are
// created at runtime. _PyType_GetModuleByDef() is used on heap types
// created by PyType_FromModuleAndSpec(), and on their subclasses.
assert(_PyType_HasFeature((PyTypeObject *)type, Py_TPFLAGS_HEAPTYPE));
PyHeapTypeObject *ht = (PyHeapTypeObject*)type;
if (ht->ht_module && _PyModule_GetDef(ht->ht_module) == def) {
return ht->ht_module;
}

// Slow path
PyObject *mro = type->tp_mro;
Py_ssize_t len = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 1; i < len; i++) {
PyObject *super = PyTuple_GET_ITEM(mro, i);
if (!_PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) {
/* Currently, there's no way for static types to inherit
* from heap types, but to allow that possibility,
* we `continue` rather than `break`.
Expand All @@ -3603,11 +3619,12 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
*/
continue;
}
PyHeapTypeObject *ht = (PyHeapTypeObject*)super;
if (ht->ht_module && PyModule_GetDef(ht->ht_module) == def) {
ht = (PyHeapTypeObject*)super;
if (ht->ht_module && _PyModule_GetDef(ht->ht_module) == def) {
return ht->ht_module;
}
}

PyErr_Format(
PyExc_TypeError,
"_PyType_GetModuleByDef: No superclass of '%s' has the given module",
Expand Down
1 change: 1 addition & 0 deletions PCbuild/pythoncore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
<ClInclude Include="..\Include\internal\pycore_interp.h" />
<ClInclude Include="..\Include\internal\pycore_list.h" />
<ClInclude Include="..\Include\internal\pycore_long.h" />
<ClInclude Include="..\Include\internal\pycore_moduleobject.h" />
<ClInclude Include="..\Include\internal\pycore_object.h" />
<ClInclude Include="..\Include\internal\pycore_pathconfig.h" />
<ClInclude Include="..\Include\internal\pycore_pyarena.h" />
Expand Down
3 changes: 3 additions & 0 deletions PCbuild/pythoncore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,9 @@
<ClInclude Include="..\Include\internal\pycore_long.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_moduleobject.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_object.h">
<Filter>Include\internal</Filter>
</ClInclude>
Expand Down
9 changes: 6 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,8 @@ def detect_simple_extensions(self):
#

# array objects
self.add(Extension('array', ['arraymodule.c']))
self.add(Extension('array', ['arraymodule.c'],
extra_compile_args=['-DPy_BUILD_CORE_MODULE']))

# Context Variables
self.add(Extension('_contextvars', ['_contextvarsmodule.c']))
Expand Down Expand Up @@ -933,7 +934,8 @@ def detect_simple_extensions(self):
# _abc speedups
self.add(Extension("_abc", ["_abc.c"]))
# _queue module
self.add(Extension("_queue", ["_queuemodule.c"]))
self.add(Extension("_queue", ["_queuemodule.c"],
extra_compile_args=['-DPy_BUILD_CORE_MODULE']))
# _statistics module
self.add(Extension("_statistics", ["_statisticsmodule.c"]))

Expand Down Expand Up @@ -2711,7 +2713,8 @@ class DummyProcess:
'install_lib': PyBuildInstallLib},
# The struct module is defined here, because build_ext won't be
# called unless there's at least one extension module defined.
ext_modules=[Extension('_struct', ['_struct.c'])],
ext_modules=[Extension('_struct', ['_struct.c'],
extra_compile_args=['-DPy_BUILD_CORE_MODULE'])],

# If you change the scripts installed here, you also need to
# check the PyBuildScripts command above, and change the links
Expand Down