Skip to content

Commit f4593e0

Browse files
committed
*EXPERIMENTAL* speedup of slot_sq_item. This sped up the following
test dramatically: class T(tuple): __dynamic__ = 1 t = T(range(1000)) for i in range(1000): tt = tuple(t) The speedup was about 5x compared to the previous state of CVS (1.7 vs. 8.8, in arbitrary time units). But it's still more than twice as slow as as the same test with __dynamic__ = 0 (0.8). I'm not sure that I really want to go through the trouble of this kind of speedup for every slot. Even doing it just for the most popular slots will be a major effort (the new slot_sq_item is 40+ lines, while the old one was one line with a powerful macro -- unfortunately the speedup comes from expanding the macro and doing things in a way specific to the slot signature). An alternative that I'm currently considering is sketched in PLAN.txt: trap setattr on type objects. But this will require keeping track of all derived types using weak references.
1 parent 1b0e549 commit f4593e0

3 files changed

Lines changed: 96 additions & 40 deletions

File tree

Include/descrobject.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,40 @@ struct wrapperbase {
2020
char *doc;
2121
};
2222

23+
/* Various kinds of descriptor objects */
24+
25+
#define PyDescr_COMMON \
26+
PyObject_HEAD \
27+
PyTypeObject *d_type; \
28+
PyObject *d_name
29+
30+
typedef struct {
31+
PyDescr_COMMON;
32+
} PyDescrObject;
33+
34+
typedef struct {
35+
PyDescr_COMMON;
36+
PyMethodDef *d_method;
37+
} PyMethodDescrObject;
38+
39+
typedef struct {
40+
PyDescr_COMMON;
41+
struct PyMemberDef *d_member;
42+
} PyMemberDescrObject;
43+
44+
typedef struct {
45+
PyDescr_COMMON;
46+
PyGetSetDef *d_getset;
47+
} PyGetSetDescrObject;
48+
49+
typedef struct {
50+
PyDescr_COMMON;
51+
struct wrapperbase *d_base;
52+
void *d_wrapped; /* This can be any function pointer */
53+
} PyWrapperDescrObject;
54+
55+
extern DL_IMPORT(PyTypeObject) PyWrapperDescr_Type;
56+
2357
extern DL_IMPORT(PyObject *) PyDescr_NewMethod(PyTypeObject *, PyMethodDef *);
2458
extern DL_IMPORT(PyObject *) PyDescr_NewMember(PyTypeObject *,
2559
struct PyMemberDef *);

Objects/descrobject.c

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,6 @@
33
#include "Python.h"
44
#include "structmember.h" /* Why is this not included in Python.h? */
55

6-
/* Various kinds of descriptor objects */
7-
8-
#define COMMON \
9-
PyObject_HEAD \
10-
PyTypeObject *d_type; \
11-
PyObject *d_name
12-
13-
typedef struct {
14-
COMMON;
15-
} PyDescrObject;
16-
17-
typedef struct {
18-
COMMON;
19-
PyMethodDef *d_method;
20-
} PyMethodDescrObject;
21-
22-
typedef struct {
23-
COMMON;
24-
PyMemberDef *d_member;
25-
} PyMemberDescrObject;
26-
27-
typedef struct {
28-
COMMON;
29-
PyGetSetDef *d_getset;
30-
} PyGetSetDescrObject;
31-
32-
typedef struct {
33-
COMMON;
34-
struct wrapperbase *d_base;
35-
void *d_wrapped; /* This can be any function pointer */
36-
} PyWrapperDescrObject;
37-
386
static void
397
descr_dealloc(PyDescrObject *descr)
408
{
@@ -481,7 +449,7 @@ static PyTypeObject PyGetSetDescr_Type = {
481449
(descrsetfunc)getset_set, /* tp_descr_set */
482450
};
483451

484-
static PyTypeObject PyWrapperDescr_Type = {
452+
PyTypeObject PyWrapperDescr_Type = {
485453
PyObject_HEAD_INIT(&PyType_Type)
486454
0,
487455
"wrapper_descriptor",

Objects/typeobject.c

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,12 +2113,16 @@ wrap_sq_item(PyObject *self, PyObject *args, void *wrapped)
21132113
PyObject *arg;
21142114
int i;
21152115

2116-
if (!PyArg_ParseTuple(args, "O", &arg))
2117-
return NULL;
2118-
i = getindex(self, arg);
2119-
if (i == -1 && PyErr_Occurred())
2120-
return NULL;
2121-
return (*func)(self, i);
2116+
if (PyTuple_GET_SIZE(args) == 1) {
2117+
arg = PyTuple_GET_ITEM(args, 0);
2118+
i = getindex(self, arg);
2119+
if (i == -1 && PyErr_Occurred())
2120+
return NULL;
2121+
return (*func)(self, i);
2122+
}
2123+
PyArg_ParseTuple(args, "O", &arg);
2124+
assert(PyErr_Occurred());
2125+
return NULL;
21222126
}
21232127

21242128
static struct wrapperbase tab_getitem_int[] = {
@@ -2825,7 +2829,57 @@ slot_sq_length(PyObject *self)
28252829

28262830
SLOT1(slot_sq_concat, "__add__", PyObject *, "O")
28272831
SLOT1(slot_sq_repeat, "__mul__", int, "i")
2828-
SLOT1(slot_sq_item, "__getitem__", int, "i")
2832+
2833+
/* Super-optimized version of slot_sq_item.
2834+
Other slots could do the same... */
2835+
static PyObject *
2836+
slot_sq_item(PyObject *self, int i)
2837+
{
2838+
static PyObject *getitem_str;
2839+
PyObject *func, *args = NULL, *ival = NULL, *retval = NULL;
2840+
descrgetfunc f;
2841+
2842+
if (getitem_str == NULL) {
2843+
getitem_str = PyString_InternFromString("__getitem__");
2844+
if (getitem_str == NULL)
2845+
return NULL;
2846+
}
2847+
func = _PyType_Lookup(self->ob_type, getitem_str);
2848+
if (func != NULL) {
2849+
if (func->ob_type == &PyWrapperDescr_Type) {
2850+
PyWrapperDescrObject *wrapper =
2851+
(PyWrapperDescrObject *)func;
2852+
if (wrapper->d_base->wrapper == wrap_sq_item) {
2853+
intargfunc f;
2854+
f = (intargfunc)(wrapper->d_wrapped);
2855+
return f(self, i);
2856+
}
2857+
}
2858+
if ((f = func->ob_type->tp_descr_get) == NULL)
2859+
Py_INCREF(func);
2860+
else
2861+
func = f(func, self, (PyObject *)(self->ob_type));
2862+
ival = PyInt_FromLong(i);
2863+
if (ival != NULL) {
2864+
args = PyTuple_New(1);
2865+
if (args != NULL) {
2866+
PyTuple_SET_ITEM(args, 0, ival);
2867+
retval = PyObject_Call(func, args, NULL);
2868+
Py_XDECREF(args);
2869+
Py_XDECREF(func);
2870+
return retval;
2871+
}
2872+
}
2873+
}
2874+
else {
2875+
PyErr_SetObject(PyExc_AttributeError, getitem_str);
2876+
}
2877+
Py_XDECREF(args);
2878+
Py_XDECREF(ival);
2879+
Py_XDECREF(func);
2880+
return NULL;
2881+
}
2882+
28292883
SLOT2(slot_sq_slice, "__getslice__", int, int, "ii")
28302884

28312885
static int

0 commit comments

Comments
 (0)