Skip to content

Commit 8f06e88

Browse files
committed
call_method() now uses _PyObject_FastCall()
Issue #29233: Replace the inefficient _PyObject_VaCallFunctionObjArgs() with _PyObject_FastCall() in call_method() and call_maybe(). Only a few functions call call_method() and call it with a fixed number of arguments. Avoid the complex and expensive _PyObject_VaCallFunctionObjArgs() function, replace it with an array allocated on the stack with the exact number of argumlents. It reduces the stack consumption, bytes per call, before => after: test_python_call: 1168 => 1152 (-16 B) test_python_getitem: 1344 => 1008 (-336 B) test_python_iterator: 1568 => 1232 (-336 B) Remove the _PyObject_VaCallFunctionObjArgs() function which became useless. Rename it to object_vacall() and make it private.
1 parent e3deae2 commit 8f06e88

3 files changed

Lines changed: 66 additions & 52 deletions

File tree

Include/abstract.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,6 @@ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId_SizeT(PyObject *obj,
323323
PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable,
324324
...);
325325

326-
#ifndef Py_LIMITED_API
327-
/* Similar PyObject_CallFunctionObjArgs(), but pass positional arguments
328-
as a va_list: list of PyObject* object. */
329-
PyAPI_FUNC(PyObject *) _PyObject_VaCallFunctionObjArgs(
330-
PyObject *callable,
331-
va_list vargs);
332-
#endif
333-
334326
/* Call the method named 'name' of object 'obj' with a variable number of
335327
C arguments. The C arguments are provided as PyObject* values, terminated
336328
by NULL.

Objects/abstract.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2700,8 +2700,8 @@ _PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name,
27002700
return retval;
27012701
}
27022702

2703-
PyObject *
2704-
_PyObject_VaCallFunctionObjArgs(PyObject *callable, va_list vargs)
2703+
static PyObject *
2704+
object_vacall(PyObject *callable, va_list vargs)
27052705
{
27062706
PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];
27072707
PyObject **stack;
@@ -2767,7 +2767,7 @@ PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...)
27672767
}
27682768

27692769
va_start(vargs, name);
2770-
result = _PyObject_VaCallFunctionObjArgs(callable, vargs);
2770+
result = object_vacall(callable, vargs);
27712771
va_end(vargs);
27722772

27732773
Py_DECREF(callable);
@@ -2791,7 +2791,7 @@ _PyObject_CallMethodIdObjArgs(PyObject *obj,
27912791
}
27922792

27932793
va_start(vargs, name);
2794-
result = _PyObject_VaCallFunctionObjArgs(callable, vargs);
2794+
result = object_vacall(callable, vargs);
27952795
va_end(vargs);
27962796

27972797
Py_DECREF(callable);
@@ -2805,7 +2805,7 @@ PyObject_CallFunctionObjArgs(PyObject *callable, ...)
28052805
PyObject *result;
28062806

28072807
va_start(vargs, callable);
2808-
result = _PyObject_VaCallFunctionObjArgs(callable, vargs);
2808+
result = object_vacall(callable, vargs);
28092809
va_end(vargs);
28102810

28112811
return result;

Objects/typeobject.c

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,9 +1425,9 @@ _PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid)
14251425
as lookup_method to cache the interned name string object. */
14261426

14271427
static PyObject *
1428-
call_method(PyObject *obj, _Py_Identifier *name, ...)
1428+
call_method(PyObject *obj, _Py_Identifier *name,
1429+
PyObject **args, Py_ssize_t nargs)
14291430
{
1430-
va_list va;
14311431
PyObject *func, *retval;
14321432

14331433
func = lookup_maybe(obj, name);
@@ -1437,10 +1437,7 @@ call_method(PyObject *obj, _Py_Identifier *name, ...)
14371437
return NULL;
14381438
}
14391439

1440-
va_start(va, name);
1441-
retval = _PyObject_VaCallFunctionObjArgs(func, va);
1442-
va_end(va);
1443-
1440+
retval = _PyObject_FastCall(func, args, nargs);
14441441
Py_DECREF(func);
14451442

14461443
return retval;
@@ -1449,9 +1446,9 @@ call_method(PyObject *obj, _Py_Identifier *name, ...)
14491446
/* Clone of call_method() that returns NotImplemented when the lookup fails. */
14501447

14511448
static PyObject *
1452-
call_maybe(PyObject *obj, _Py_Identifier *name, ...)
1449+
call_maybe(PyObject *obj, _Py_Identifier *name,
1450+
PyObject **args, Py_ssize_t nargs)
14531451
{
1454-
va_list va;
14551452
PyObject *func, *retval;
14561453

14571454
func = lookup_maybe(obj, name);
@@ -1461,10 +1458,7 @@ call_maybe(PyObject *obj, _Py_Identifier *name, ...)
14611458
return NULL;
14621459
}
14631460

1464-
va_start(va, name);
1465-
retval = _PyObject_VaCallFunctionObjArgs(func, va);
1466-
va_end(va);
1467-
1461+
retval = _PyObject_FastCall(func, args, nargs);
14681462
Py_DECREF(func);
14691463

14701464
return retval;
@@ -5701,15 +5695,16 @@ static PyObject * \
57015695
FUNCNAME(PyObject *self) \
57025696
{ \
57035697
_Py_static_string(id, OPSTR); \
5704-
return call_method(self, &id, NULL); \
5698+
return call_method(self, &id, NULL, 0); \
57055699
}
57065700

57075701
#define SLOT1(FUNCNAME, OPSTR, ARG1TYPE) \
57085702
static PyObject * \
57095703
FUNCNAME(PyObject *self, ARG1TYPE arg1) \
57105704
{ \
5705+
PyObject* stack[1] = {arg1}; \
57115706
_Py_static_string(id, OPSTR); \
5712-
return call_method(self, &id, arg1, NULL); \
5707+
return call_method(self, &id, stack, 1); \
57135708
}
57145709

57155710
/* Boolean helper for SLOT1BINFULL().
@@ -5751,6 +5746,7 @@ method_is_overloaded(PyObject *left, PyObject *right, struct _Py_Identifier *nam
57515746
static PyObject * \
57525747
FUNCNAME(PyObject *self, PyObject *other) \
57535748
{ \
5749+
PyObject* stack[1]; \
57545750
_Py_static_string(op_id, OPSTR); \
57555751
_Py_static_string(rop_id, ROPSTR); \
57565752
int do_other = Py_TYPE(self) != Py_TYPE(other) && \
@@ -5762,20 +5758,23 @@ FUNCNAME(PyObject *self, PyObject *other) \
57625758
if (do_other && \
57635759
PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self)) && \
57645760
method_is_overloaded(self, other, &rop_id)) { \
5765-
r = call_maybe(other, &rop_id, self, NULL); \
5761+
stack[0] = self; \
5762+
r = call_maybe(other, &rop_id, stack, 1); \
57665763
if (r != Py_NotImplemented) \
57675764
return r; \
57685765
Py_DECREF(r); \
57695766
do_other = 0; \
57705767
} \
5771-
r = call_maybe(self, &op_id, other, NULL); \
5768+
stack[0] = other; \
5769+
r = call_maybe(self, &op_id, stack, 1); \
57725770
if (r != Py_NotImplemented || \
57735771
Py_TYPE(other) == Py_TYPE(self)) \
57745772
return r; \
57755773
Py_DECREF(r); \
57765774
} \
57775775
if (do_other) { \
5778-
return call_maybe(other, &rop_id, self, NULL); \
5776+
stack[0] = self; \
5777+
return call_maybe(other, &rop_id, stack, 1); \
57795778
} \
57805779
Py_RETURN_NOTIMPLEMENTED; \
57815780
}
@@ -5786,7 +5785,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
57865785
static Py_ssize_t
57875786
slot_sq_length(PyObject *self)
57885787
{
5789-
PyObject *res = call_method(self, &PyId___len__, NULL);
5788+
PyObject *res = call_method(self, &PyId___len__, NULL, 0);
57905789
Py_ssize_t len;
57915790

57925791
if (res == NULL)
@@ -5846,6 +5845,7 @@ slot_sq_item(PyObject *self, Py_ssize_t i)
58465845
static int
58475846
slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
58485847
{
5848+
PyObject *stack[2];
58495849
PyObject *res;
58505850
PyObject *index_obj;
58515851

@@ -5854,10 +5854,14 @@ slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
58545854
return -1;
58555855
}
58565856

5857-
if (value == NULL)
5858-
res = call_method(self, &PyId___delitem__, index_obj, NULL);
5859-
else
5860-
res = call_method(self, &PyId___setitem__, index_obj, value, NULL);
5857+
stack[0] = index_obj;
5858+
if (value == NULL) {
5859+
res = call_method(self, &PyId___delitem__, stack, 1);
5860+
}
5861+
else {
5862+
stack[1] = value;
5863+
res = call_method(self, &PyId___setitem__, stack, 2);
5864+
}
58615865
Py_DECREF(index_obj);
58625866

58635867
if (res == NULL) {
@@ -5905,12 +5909,17 @@ SLOT1(slot_mp_subscript, "__getitem__", PyObject *)
59055909
static int
59065910
slot_mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value)
59075911
{
5912+
PyObject *stack[2];
59085913
PyObject *res;
59095914

5910-
if (value == NULL)
5911-
res = call_method(self, &PyId___delitem__, key, NULL);
5912-
else
5913-
res = call_method(self, &PyId___setitem__, key, value, NULL);
5915+
stack[0] = key;
5916+
if (value == NULL) {
5917+
res = call_method(self, &PyId___delitem__, stack, 1);
5918+
}
5919+
else {
5920+
stack[1] = value;
5921+
res = call_method(self, &PyId___setitem__, stack, 2);
5922+
}
59145923

59155924
if (res == NULL)
59165925
return -1;
@@ -5942,7 +5951,8 @@ slot_nb_power(PyObject *self, PyObject *other, PyObject *modulus)
59425951
slot_nb_power, so check before calling self.__pow__. */
59435952
if (Py_TYPE(self)->tp_as_number != NULL &&
59445953
Py_TYPE(self)->tp_as_number->nb_power == slot_nb_power) {
5945-
return call_method(self, &PyId___pow__, other, modulus, NULL);
5954+
PyObject* stack[2] = {other, modulus};
5955+
return call_method(self, &PyId___pow__, stack, 2);
59465956
}
59475957
Py_RETURN_NOTIMPLEMENTED;
59485958
}
@@ -6009,7 +6019,7 @@ static PyObject *
60096019
slot_nb_index(PyObject *self)
60106020
{
60116021
_Py_IDENTIFIER(__index__);
6012-
return call_method(self, &PyId___index__, NULL);
6022+
return call_method(self, &PyId___index__, NULL, 0);
60136023
}
60146024

60156025

@@ -6031,8 +6041,9 @@ SLOT1(slot_nb_inplace_remainder, "__imod__", PyObject *)
60316041
static PyObject *
60326042
slot_nb_inplace_power(PyObject *self, PyObject * arg1, PyObject *arg2)
60336043
{
6044+
PyObject *stack[1] = {arg1};
60346045
_Py_IDENTIFIER(__ipow__);
6035-
return call_method(self, &PyId___ipow__, arg1, NULL);
6046+
return call_method(self, &PyId___ipow__, stack, 1);
60366047
}
60376048
SLOT1(slot_nb_inplace_lshift, "__ilshift__", PyObject *)
60386049
SLOT1(slot_nb_inplace_rshift, "__irshift__", PyObject *)
@@ -6153,7 +6164,8 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
61536164
static PyObject *
61546165
slot_tp_getattro(PyObject *self, PyObject *name)
61556166
{
6156-
return call_method(self, &PyId___getattribute__, name, NULL);
6167+
PyObject *stack[1] = {name};
6168+
return call_method(self, &PyId___getattribute__, stack, 1);
61576169
}
61586170

61596171
static PyObject *
@@ -6220,14 +6232,19 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
62206232
static int
62216233
slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value)
62226234
{
6235+
PyObject *stack[2];
62236236
PyObject *res;
62246237
_Py_IDENTIFIER(__delattr__);
62256238
_Py_IDENTIFIER(__setattr__);
62266239

6227-
if (value == NULL)
6228-
res = call_method(self, &PyId___delattr__, name, NULL);
6229-
else
6230-
res = call_method(self, &PyId___setattr__, name, value, NULL);
6240+
stack[0] = name;
6241+
if (value == NULL) {
6242+
res = call_method(self, &PyId___delattr__, stack, 1);
6243+
}
6244+
else {
6245+
stack[1] = value;
6246+
res = call_method(self, &PyId___setattr__, stack, 2);
6247+
}
62316248
if (res == NULL)
62326249
return -1;
62336250
Py_DECREF(res);
@@ -6295,7 +6312,7 @@ static PyObject *
62956312
slot_tp_iternext(PyObject *self)
62966313
{
62976314
_Py_IDENTIFIER(__next__);
6298-
return call_method(self, &PyId___next__, NULL);
6315+
return call_method(self, &PyId___next__, NULL, 0);
62996316
}
63006317

63016318
static PyObject *
@@ -6323,14 +6340,19 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
63236340
static int
63246341
slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
63256342
{
6343+
PyObject* stack[2];
63266344
PyObject *res;
63276345
_Py_IDENTIFIER(__delete__);
63286346
_Py_IDENTIFIER(__set__);
63296347

6330-
if (value == NULL)
6331-
res = call_method(self, &PyId___delete__, target, NULL);
6332-
else
6333-
res = call_method(self, &PyId___set__, target, value, NULL);
6348+
stack[0] = target;
6349+
if (value == NULL) {
6350+
res = call_method(self, &PyId___delete__, stack, 1);
6351+
}
6352+
else {
6353+
stack[1] = value;
6354+
res = call_method(self, &PyId___set__, stack, 2);
6355+
}
63346356
if (res == NULL)
63356357
return -1;
63366358
Py_DECREF(res);

0 commit comments

Comments
 (0)