Skip to content

Commit b4b814b

Browse files
jdemeyermethane
authored andcommitted
bpo-37231: optimize calls of special methods (GH-13973)
1 parent 022ac0a commit b4b814b

File tree

2 files changed

+88
-72
lines changed

2 files changed

+88
-72
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The dispatching of type slots to special methods (for example calling
2+
``__mul__`` when doing ``x * y``) has been made faster.

Objects/typeobject.c

Lines changed: 86 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,16 +1440,19 @@ lookup_method(PyObject *self, _Py_Identifier *attrid, int *unbound)
14401440
return res;
14411441
}
14421442

1443-
static PyObject*
1444-
call_unbound(int unbound, PyObject *func, PyObject *self,
1445-
PyObject **args, Py_ssize_t nargs)
1443+
1444+
static inline PyObject*
1445+
vectorcall_unbound(int unbound, PyObject *func,
1446+
PyObject *const *args, Py_ssize_t nargs)
14461447
{
1447-
if (unbound) {
1448-
return _PyObject_FastCall_Prepend(func, self, args, nargs);
1449-
}
1450-
else {
1451-
return _PyObject_FastCall(func, args, nargs);
1448+
size_t nargsf = nargs;
1449+
if (!unbound) {
1450+
/* Skip self argument, freeing up args[0] to use for
1451+
* PY_VECTORCALL_ARGUMENTS_OFFSET */
1452+
args++;
1453+
nargsf = nargsf - 1 + PY_VECTORCALL_ARGUMENTS_OFFSET;
14521454
}
1455+
return _PyObject_Vectorcall(func, args, nargsf, NULL);
14531456
}
14541457

14551458
static PyObject*
@@ -1464,41 +1467,43 @@ call_unbound_noarg(int unbound, PyObject *func, PyObject *self)
14641467
}
14651468
}
14661469

1467-
/* A variation of PyObject_CallMethod* that uses lookup_maybe_method()
1468-
instead of PyObject_GetAttrString(). */
1470+
/* A variation of PyObject_CallMethod* that uses lookup_method()
1471+
instead of PyObject_GetAttrString().
1472+
1473+
args is an argument vector of length nargs. The first element in this
1474+
vector is the special object "self" which is used for the method lookup */
14691475
static PyObject *
1470-
call_method(PyObject *obj, _Py_Identifier *name,
1471-
PyObject **args, Py_ssize_t nargs)
1476+
vectorcall_method(_Py_Identifier *name,
1477+
PyObject *const *args, Py_ssize_t nargs)
14721478
{
1479+
assert(nargs >= 1);
14731480
int unbound;
1474-
PyObject *func, *retval;
1475-
1476-
func = lookup_method(obj, name, &unbound);
1481+
PyObject *self = args[0];
1482+
PyObject *func = lookup_method(self, name, &unbound);
14771483
if (func == NULL) {
14781484
return NULL;
14791485
}
1480-
retval = call_unbound(unbound, func, obj, args, nargs);
1486+
PyObject *retval = vectorcall_unbound(unbound, func, args, nargs);
14811487
Py_DECREF(func);
14821488
return retval;
14831489
}
14841490

1485-
/* Clone of call_method() that returns NotImplemented when the lookup fails. */
1486-
1491+
/* Clone of vectorcall_method() that returns NotImplemented
1492+
* when the lookup fails. */
14871493
static PyObject *
1488-
call_maybe(PyObject *obj, _Py_Identifier *name,
1489-
PyObject **args, Py_ssize_t nargs)
1494+
vectorcall_maybe(_Py_Identifier *name,
1495+
PyObject *const *args, Py_ssize_t nargs)
14901496
{
1497+
assert(nargs >= 1);
14911498
int unbound;
1492-
PyObject *func, *retval;
1493-
1494-
func = lookup_maybe_method(obj, name, &unbound);
1499+
PyObject *self = args[0];
1500+
PyObject *func = lookup_maybe_method(self, name, &unbound);
14951501
if (func == NULL) {
14961502
if (!PyErr_Occurred())
14971503
Py_RETURN_NOTIMPLEMENTED;
14981504
return NULL;
14991505
}
1500-
1501-
retval = call_unbound(unbound, func, obj, args, nargs);
1506+
PyObject *retval = vectorcall_unbound(unbound, func, args, nargs);
15021507
Py_DECREF(func);
15031508
return retval;
15041509
}
@@ -6084,17 +6089,18 @@ add_tp_new_wrapper(PyTypeObject *type)
60846089
static PyObject * \
60856090
FUNCNAME(PyObject *self) \
60866091
{ \
6092+
PyObject* stack[1] = {self}; \
60876093
_Py_static_string(id, OPSTR); \
6088-
return call_method(self, &id, NULL, 0); \
6094+
return vectorcall_method(&id, stack, 1); \
60896095
}
60906096

60916097
#define SLOT1(FUNCNAME, OPSTR, ARG1TYPE) \
60926098
static PyObject * \
60936099
FUNCNAME(PyObject *self, ARG1TYPE arg1) \
60946100
{ \
6095-
PyObject* stack[1] = {arg1}; \
6101+
PyObject* stack[2] = {self, arg1}; \
60966102
_Py_static_string(id, OPSTR); \
6097-
return call_method(self, &id, stack, 1); \
6103+
return vectorcall_method(&id, stack, 2); \
60986104
}
60996105

61006106
/* Boolean helper for SLOT1BINFULL().
@@ -6136,7 +6142,7 @@ method_is_overloaded(PyObject *left, PyObject *right, struct _Py_Identifier *nam
61366142
static PyObject * \
61376143
FUNCNAME(PyObject *self, PyObject *other) \
61386144
{ \
6139-
PyObject* stack[1]; \
6145+
PyObject* stack[2]; \
61406146
_Py_static_string(op_id, OPSTR); \
61416147
_Py_static_string(rop_id, ROPSTR); \
61426148
int do_other = Py_TYPE(self) != Py_TYPE(other) && \
@@ -6148,23 +6154,26 @@ FUNCNAME(PyObject *self, PyObject *other) \
61486154
if (do_other && \
61496155
PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self)) && \
61506156
method_is_overloaded(self, other, &rop_id)) { \
6151-
stack[0] = self; \
6152-
r = call_maybe(other, &rop_id, stack, 1); \
6157+
stack[0] = other; \
6158+
stack[1] = self; \
6159+
r = vectorcall_maybe(&rop_id, stack, 2); \
61536160
if (r != Py_NotImplemented) \
61546161
return r; \
61556162
Py_DECREF(r); \
61566163
do_other = 0; \
61576164
} \
6158-
stack[0] = other; \
6159-
r = call_maybe(self, &op_id, stack, 1); \
6165+
stack[0] = self; \
6166+
stack[1] = other; \
6167+
r = vectorcall_maybe(&op_id, stack, 2); \
61606168
if (r != Py_NotImplemented || \
61616169
Py_TYPE(other) == Py_TYPE(self)) \
61626170
return r; \
61636171
Py_DECREF(r); \
61646172
} \
61656173
if (do_other) { \
6166-
stack[0] = self; \
6167-
return call_maybe(other, &rop_id, stack, 1); \
6174+
stack[0] = other; \
6175+
stack[1] = self; \
6176+
return vectorcall_maybe(&rop_id, stack, 2); \
61686177
} \
61696178
Py_RETURN_NOTIMPLEMENTED; \
61706179
}
@@ -6175,7 +6184,8 @@ FUNCNAME(PyObject *self, PyObject *other) \
61756184
static Py_ssize_t
61766185
slot_sq_length(PyObject *self)
61776186
{
6178-
PyObject *res = call_method(self, &PyId___len__, NULL, 0);
6187+
PyObject* stack[1] = {self};
6188+
PyObject *res = vectorcall_method(&PyId___len__, stack, 1);
61796189
Py_ssize_t len;
61806190

61816191
if (res == NULL)
@@ -6202,22 +6212,20 @@ slot_sq_length(PyObject *self)
62026212
static PyObject *
62036213
slot_sq_item(PyObject *self, Py_ssize_t i)
62046214
{
6205-
PyObject *retval;
6206-
PyObject *args[1];
62076215
PyObject *ival = PyLong_FromSsize_t(i);
62086216
if (ival == NULL) {
62096217
return NULL;
62106218
}
6211-
args[0] = ival;
6212-
retval = call_method(self, &PyId___getitem__, args, 1);
6219+
PyObject *stack[2] = {self, ival};
6220+
PyObject *retval = vectorcall_method(&PyId___getitem__, stack, 2);
62136221
Py_DECREF(ival);
62146222
return retval;
62156223
}
62166224

62176225
static int
62186226
slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
62196227
{
6220-
PyObject *stack[2];
6228+
PyObject *stack[3];
62216229
PyObject *res;
62226230
PyObject *index_obj;
62236231

@@ -6226,13 +6234,14 @@ slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
62266234
return -1;
62276235
}
62286236

6229-
stack[0] = index_obj;
6237+
stack[0] = self;
6238+
stack[1] = index_obj;
62306239
if (value == NULL) {
6231-
res = call_method(self, &PyId___delitem__, stack, 1);
6240+
res = vectorcall_method(&PyId___delitem__, stack, 2);
62326241
}
62336242
else {
6234-
stack[1] = value;
6235-
res = call_method(self, &PyId___setitem__, stack, 2);
6243+
stack[2] = value;
6244+
res = vectorcall_method(&PyId___setitem__, stack, 3);
62366245
}
62376246
Py_DECREF(index_obj);
62386247

@@ -6259,8 +6268,8 @@ slot_sq_contains(PyObject *self, PyObject *value)
62596268
return -1;
62606269
}
62616270
if (func != NULL) {
6262-
PyObject *args[1] = {value};
6263-
res = call_unbound(unbound, func, self, args, 1);
6271+
PyObject *args[2] = {self, value};
6272+
res = vectorcall_unbound(unbound, func, args, 2);
62646273
Py_DECREF(func);
62656274
if (res != NULL) {
62666275
result = PyObject_IsTrue(res);
@@ -6282,16 +6291,17 @@ SLOT1(slot_mp_subscript, "__getitem__", PyObject *)
62826291
static int
62836292
slot_mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value)
62846293
{
6285-
PyObject *stack[2];
6294+
PyObject *stack[3];
62866295
PyObject *res;
62876296

6288-
stack[0] = key;
6297+
stack[0] = self;
6298+
stack[1] = key;
62896299
if (value == NULL) {
6290-
res = call_method(self, &PyId___delitem__, stack, 1);
6300+
res = vectorcall_method(&PyId___delitem__, stack, 2);
62916301
}
62926302
else {
6293-
stack[1] = value;
6294-
res = call_method(self, &PyId___setitem__, stack, 2);
6303+
stack[2] = value;
6304+
res = vectorcall_method(&PyId___setitem__, stack, 3);
62956305
}
62966306

62976307
if (res == NULL)
@@ -6324,8 +6334,8 @@ slot_nb_power(PyObject *self, PyObject *other, PyObject *modulus)
63246334
slot_nb_power, so check before calling self.__pow__. */
63256335
if (Py_TYPE(self)->tp_as_number != NULL &&
63266336
Py_TYPE(self)->tp_as_number->nb_power == slot_nb_power) {
6327-
PyObject* stack[2] = {other, modulus};
6328-
return call_method(self, &PyId___pow__, stack, 2);
6337+
PyObject* stack[3] = {self, other, modulus};
6338+
return vectorcall_method(&PyId___pow__, stack, 3);
63296339
}
63306340
Py_RETURN_NOTIMPLEMENTED;
63316341
}
@@ -6392,7 +6402,8 @@ static PyObject *
63926402
slot_nb_index(PyObject *self)
63936403
{
63946404
_Py_IDENTIFIER(__index__);
6395-
return call_method(self, &PyId___index__, NULL, 0);
6405+
PyObject *stack[1] = {self};
6406+
return vectorcall_method(&PyId___index__, stack, 1);
63966407
}
63976408

63986409

@@ -6414,9 +6425,9 @@ SLOT1(slot_nb_inplace_remainder, "__imod__", PyObject *)
64146425
static PyObject *
64156426
slot_nb_inplace_power(PyObject *self, PyObject * arg1, PyObject *arg2)
64166427
{
6417-
PyObject *stack[1] = {arg1};
6428+
PyObject *stack[2] = {self, arg1};
64186429
_Py_IDENTIFIER(__ipow__);
6419-
return call_method(self, &PyId___ipow__, stack, 1);
6430+
return vectorcall_method(&PyId___ipow__, stack, 2);
64206431
}
64216432
SLOT1(slot_nb_inplace_lshift, "__ilshift__", PyObject *)
64226433
SLOT1(slot_nb_inplace_rshift, "__irshift__", PyObject *)
@@ -6533,8 +6544,8 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
65336544
static PyObject *
65346545
slot_tp_getattro(PyObject *self, PyObject *name)
65356546
{
6536-
PyObject *stack[1] = {name};
6537-
return call_method(self, &PyId___getattribute__, stack, 1);
6547+
PyObject *stack[2] = {self, name};
6548+
return vectorcall_method(&PyId___getattribute__, stack, 2);
65386549
}
65396550

65406551
static PyObject *
@@ -6601,18 +6612,19 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
66016612
static int
66026613
slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value)
66036614
{
6604-
PyObject *stack[2];
6615+
PyObject *stack[3];
66056616
PyObject *res;
66066617
_Py_IDENTIFIER(__delattr__);
66076618
_Py_IDENTIFIER(__setattr__);
66086619

6609-
stack[0] = name;
6620+
stack[0] = self;
6621+
stack[1] = name;
66106622
if (value == NULL) {
6611-
res = call_method(self, &PyId___delattr__, stack, 1);
6623+
res = vectorcall_method(&PyId___delattr__, stack, 2);
66126624
}
66136625
else {
6614-
stack[1] = value;
6615-
res = call_method(self, &PyId___setattr__, stack, 2);
6626+
stack[2] = value;
6627+
res = vectorcall_method(&PyId___setattr__, stack, 3);
66166628
}
66176629
if (res == NULL)
66186630
return -1;
@@ -6641,8 +6653,8 @@ slot_tp_richcompare(PyObject *self, PyObject *other, int op)
66416653
Py_RETURN_NOTIMPLEMENTED;
66426654
}
66436655

6644-
PyObject *args[1] = {other};
6645-
res = call_unbound(unbound, func, self, args, 1);
6656+
PyObject *stack[2] = {self, other};
6657+
res = vectorcall_unbound(unbound, func, stack, 2);
66466658
Py_DECREF(func);
66476659
return res;
66486660
}
@@ -6685,7 +6697,8 @@ static PyObject *
66856697
slot_tp_iternext(PyObject *self)
66866698
{
66876699
_Py_IDENTIFIER(__next__);
6688-
return call_method(self, &PyId___next__, NULL, 0);
6700+
PyObject *stack[1] = {self};
6701+
return vectorcall_method(&PyId___next__, stack, 1);
66896702
}
66906703

66916704
static PyObject *
@@ -6713,18 +6726,19 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
67136726
static int
67146727
slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
67156728
{
6716-
PyObject* stack[2];
6729+
PyObject* stack[3];
67176730
PyObject *res;
67186731
_Py_IDENTIFIER(__delete__);
67196732
_Py_IDENTIFIER(__set__);
67206733

6721-
stack[0] = target;
6734+
stack[0] = self;
6735+
stack[1] = target;
67226736
if (value == NULL) {
6723-
res = call_method(self, &PyId___delete__, stack, 1);
6737+
res = vectorcall_method(&PyId___delete__, stack, 2);
67246738
}
67256739
else {
6726-
stack[1] = value;
6727-
res = call_method(self, &PyId___set__, stack, 2);
6740+
stack[2] = value;
6741+
res = vectorcall_method(&PyId___set__, stack, 3);
67286742
}
67296743
if (res == NULL)
67306744
return -1;

0 commit comments

Comments
 (0)