Skip to content

Commit 158c102

Browse files
committed
bpo-40137: Micro-optimize _PyType_GetModuleByDef()
Make _PyType_GetModuleByDef() 2.3 ns faster. Benchmark: 43.2 ns +- 0.7 ns -> 40.9 ns +- 1.0 ns: 1.05x faster ./python -m pyperf timeit \ --duplicate=4096 -s "from functools import lru_cache; f = lru_cache(lambda: 42)" \ "f()" Changes: * _PyType_GetModuleByDef(): add fast-path for tp_mro[0] and use _PyType_HasFeature(). * Add a new pycore_moduleobject.h internal C API header file. * Add _PyModule_GetDef() and _PyModule_GetState() which can be inlined without LTO and don't check invalid argument at runtime. * Replace PyModule_GetState() with _PyModule_GetState() in _abc, _array, _operator, _pickle, _queue, _random, _struct and os extension modules. The _array, _queue and _struct extensions are now built with Py_BUILD_CORE_MODULE macro defined.
1 parent 8fa1489 commit 158c102

File tree

17 files changed

+93
-33
lines changed

17 files changed

+93
-33
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#ifndef Py_INTERNAL_MODULEOBJECT_H
2+
#define Py_INTERNAL_MODULEOBJECT_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
typedef struct {
12+
PyObject_HEAD
13+
PyObject *md_dict;
14+
struct PyModuleDef *md_def;
15+
void *md_state;
16+
PyObject *md_weaklist;
17+
PyObject *md_name; /* for logging purposes after md_dict is cleared */
18+
} PyModuleObject;
19+
20+
static inline PyModuleDef* _PyModule_GetDef(PyObject *m) {
21+
assert(PyModule_Check(m));
22+
return ((PyModuleObject *)m)->md_def;
23+
}
24+
25+
static inline void* _PyModule_GetState(PyObject* m) {
26+
assert(PyModule_Check(m));
27+
return ((PyModuleObject *)m)->md_state;
28+
}
29+
30+
31+
#ifdef __cplusplus
32+
}
33+
#endif
34+
#endif /* !Py_INTERNAL_MODULEOBJECT_H */

‎Makefile.pre.in‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,7 @@ PYTHON_HEADERS= \
11601160
$(srcdir)/Include/internal/pycore_interp.h \
11611161
$(srcdir)/Include/internal/pycore_list.h \
11621162
$(srcdir)/Include/internal/pycore_long.h \
1163+
$(srcdir)/Include/internal/pycore_moduleobject.h \
11631164
$(srcdir)/Include/internal/pycore_object.h \
11641165
$(srcdir)/Include/internal/pycore_pathconfig.h \
11651166
$(srcdir)/Include/internal/pycore_pyarena.h \
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Micro-optimize _PyType_GetModuleByDef() to make it 2.3 ns faster (43.2 ns +-
2+
0.7 ns -> 40.9 ns +- 1.0 ns). Patch by Victor Stinner.

‎Modules/_abc.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* ABCMeta implementation */
22

33
#include "Python.h"
4+
#include "pycore_moduleobject.h" // _PyModule_GetState()
45
#include "clinic/_abc.c.h"
56

67
/*[clinic input]
@@ -27,7 +28,7 @@ typedef struct {
2728
static inline _abcmodule_state*
2829
get_abc_state(PyObject *module)
2930
{
30-
void *state = PyModule_GetState(module);
31+
void *state = _PyModule_GetState(module);
3132
assert(state != NULL);
3233
return (_abcmodule_state *)state;
3334
}

‎Modules/_functoolsmodule.c‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Python.h"
22
#include "pycore_long.h" // _PyLong_GetZero()
3+
#include "pycore_moduleobject.h" // _PyModule_GetState()
34
#include "pycore_object.h" // _PyObject_GC_TRACK
45
#include "pycore_pystate.h" // _PyThreadState_GET()
56
#include "pycore_tuple.h" // _PyTuple_ITEMS()
@@ -35,7 +36,7 @@ typedef struct _functools_state {
3536
static inline _functools_state *
3637
get_functools_state(PyObject *module)
3738
{
38-
void *state = PyModule_GetState(module);
39+
void *state = _PyModule_GetState(module);
3940
assert(state != NULL);
4041
return (_functools_state *)state;
4142
}
@@ -52,8 +53,7 @@ get_functools_state_by_type(PyTypeObject *type)
5253
if (module == NULL) {
5354
return NULL;
5455
}
55-
_functools_state *state = get_functools_state(module);
56-
return state;
56+
return get_functools_state(module);
5757
}
5858

5959
static PyObject *

‎Modules/_operator.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
#include "Python.h"
3-
3+
#include "pycore_moduleobject.h" // _PyModule_GetState()
44
#include "clinic/_operator.c.h"
55

66
typedef struct {
@@ -12,7 +12,7 @@ typedef struct {
1212
static inline _operator_state*
1313
get_operator_state(PyObject *module)
1414
{
15-
void *state = PyModule_GetState(module);
15+
void *state = _PyModule_GetState(module);
1616
assert(state != NULL);
1717
return (_operator_state *)state;
1818
}

‎Modules/_pickle.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#endif
1010

1111
#include "Python.h"
12+
#include "pycore_moduleobject.h" // _PyModule_GetState()
1213
#include "structmember.h" // PyMemberDef
1314

1415
PyDoc_STRVAR(pickle_module_doc,
@@ -182,7 +183,7 @@ static struct PyModuleDef _picklemodule;
182183
static PickleState *
183184
_Pickle_GetState(PyObject *module)
184185
{
185-
return (PickleState *)PyModule_GetState(module);
186+
return (PickleState *)_PyModule_GetState(module);
186187
}
187188

188189
/* Find the module instance imported in the currently running sub-interpreter

‎Modules/_queuemodule.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "Python.h"
2+
#include "pycore_moduleobject.h" // _PyModule_GetState()
23
#include "structmember.h" // PyMemberDef
34
#include <stddef.h> // offsetof()
45

@@ -10,7 +11,7 @@ typedef struct {
1011
static simplequeue_state *
1112
simplequeue_get_state(PyObject *module)
1213
{
13-
simplequeue_state *state = PyModule_GetState(module);
14+
simplequeue_state *state = _PyModule_GetState(module);
1415
assert(state);
1516
return state;
1617
}

‎Modules/_randommodule.c‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
/* ---------------------------------------------------------------*/
6868

6969
#include "Python.h"
70+
#include "pycore_moduleobject.h" // _PyModule_GetState()
7071
#ifdef HAVE_PROCESS_H
7172
# include <process.h> // getpid()
7273
#endif
@@ -86,7 +87,7 @@ typedef struct {
8687
static inline _randomstate*
8788
get_random_state(PyObject *module)
8889
{
89-
void *state = PyModule_GetState(module);
90+
void *state = _PyModule_GetState(module);
9091
assert(state != NULL);
9192
return (_randomstate *)state;
9293
}
@@ -538,7 +539,7 @@ random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
538539

539540
if (PyTuple_GET_SIZE(args) == 1)
540541
arg = PyTuple_GET_ITEM(args, 0);
541-
542+
542543
tmp = random_seed(self, arg);
543544
if (tmp == NULL) {
544545
Py_DECREF(self);

‎Modules/_struct.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define PY_SSIZE_T_CLEAN
77

88
#include "Python.h"
9+
#include "pycore_moduleobject.h" // _PyModule_GetState()
910
#include "structmember.h" // PyMemberDef
1011
#include <ctype.h>
1112

@@ -24,7 +25,7 @@ typedef struct {
2425
static inline _structmodulestate*
2526
get_struct_state(PyObject *module)
2627
{
27-
void *state = PyModule_GetState(module);
28+
void *state = _PyModule_GetState(module);
2829
assert(state != NULL);
2930
return (_structmodulestate *)state;
3031
}

0 commit comments

Comments
 (0)