[Python-checkins] cpython: call_method() now uses _PyObject_FastCall()

victor.stinner python-checkins at python.org
Tue Jan 10 18:22:24 EST 2017


https://hg.python.org/cpython/rev/b9404639a18c
changeset:   106081:b9404639a18c
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Wed Jan 11 00:07:40 2017 +0100
summary:
  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.

files:
  Include/abstract.h   |    8 --
  Objects/abstract.c   |   10 +-
  Objects/typeobject.c |  104 ++++++++++++++++++------------
  3 files changed, 68 insertions(+), 54 deletions(-)


diff --git a/Include/abstract.h b/Include/abstract.h
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -323,14 +323,6 @@
 PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable,
                                                     ...);
 
-#ifndef Py_LIMITED_API
-/* Similar PyObject_CallFunctionObjArgs(), but pass positional arguments
-   as a va_list: list of PyObject* object. */
-PyAPI_FUNC(PyObject *) _PyObject_VaCallFunctionObjArgs(
-    PyObject *callable,
-    va_list vargs);
-#endif
-
 /* Call the method named 'name' of object 'obj' with a variable number of
    C arguments.  The C arguments are provided as PyObject* values, terminated
    by NULL.
diff --git a/Objects/abstract.c b/Objects/abstract.c
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2700,8 +2700,8 @@
     return retval;
 }
 
-PyObject *
-_PyObject_VaCallFunctionObjArgs(PyObject *callable, va_list vargs)
+static PyObject *
+object_vacall(PyObject *callable, va_list vargs)
 {
     PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];
     PyObject **stack;
@@ -2767,7 +2767,7 @@
     }
 
     va_start(vargs, name);
-    result = _PyObject_VaCallFunctionObjArgs(callable, vargs);
+    result = object_vacall(callable, vargs);
     va_end(vargs);
 
     Py_DECREF(callable);
@@ -2791,7 +2791,7 @@
     }
 
     va_start(vargs, name);
-    result = _PyObject_VaCallFunctionObjArgs(callable, vargs);
+    result = object_vacall(callable, vargs);
     va_end(vargs);
 
     Py_DECREF(callable);
@@ -2805,7 +2805,7 @@
     PyObject *result;
 
     va_start(vargs, callable);
-    result = _PyObject_VaCallFunctionObjArgs(callable, vargs);
+    result = object_vacall(callable, vargs);
     va_end(vargs);
 
     return result;
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -1425,9 +1425,9 @@
    as lookup_method to cache the interned name string object. */
 
 static PyObject *
-call_method(PyObject *obj, _Py_Identifier *name, ...)
-{
-    va_list va;
+call_method(PyObject *obj, _Py_Identifier *name,
+            PyObject **args, Py_ssize_t nargs)
+{
     PyObject *func, *retval;
 
     func = lookup_maybe(obj, name);
@@ -1437,10 +1437,7 @@
         return NULL;
     }
 
-    va_start(va, name);
-    retval = _PyObject_VaCallFunctionObjArgs(func, va);
-    va_end(va);
-
+    retval = _PyObject_FastCall(func, args, nargs);
     Py_DECREF(func);
 
     return retval;
@@ -1449,9 +1446,9 @@
 /* Clone of call_method() that returns NotImplemented when the lookup fails. */
 
 static PyObject *
-call_maybe(PyObject *obj, _Py_Identifier *name, ...)
-{
-    va_list va;
+call_maybe(PyObject *obj, _Py_Identifier *name,
+           PyObject **args, Py_ssize_t nargs)
+{
     PyObject *func, *retval;
 
     func = lookup_maybe(obj, name);
@@ -1461,10 +1458,7 @@
         return NULL;
     }
 
-    va_start(va, name);
-    retval = _PyObject_VaCallFunctionObjArgs(func, va);
-    va_end(va);
-
+    retval = _PyObject_FastCall(func, args, nargs);
     Py_DECREF(func);
 
     return retval;
@@ -5701,15 +5695,16 @@
 FUNCNAME(PyObject *self) \
 { \
     _Py_static_string(id, OPSTR); \
-    return call_method(self, &id, NULL); \
+    return call_method(self, &id, NULL, 0); \
 }
 
 #define SLOT1(FUNCNAME, OPSTR, ARG1TYPE) \
 static PyObject * \
 FUNCNAME(PyObject *self, ARG1TYPE arg1) \
 { \
+    PyObject* stack[1] = {arg1}; \
     _Py_static_string(id, OPSTR); \
-    return call_method(self, &id, arg1, NULL); \
+    return call_method(self, &id, stack, 1); \
 }
 
 /* Boolean helper for SLOT1BINFULL().
@@ -5751,6 +5746,7 @@
 static PyObject * \
 FUNCNAME(PyObject *self, PyObject *other) \
 { \
+    PyObject* stack[1]; \
     _Py_static_string(op_id, OPSTR); \
     _Py_static_string(rop_id, ROPSTR); \
     int do_other = Py_TYPE(self) != Py_TYPE(other) && \
@@ -5762,20 +5758,23 @@
         if (do_other && \
             PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self)) && \
             method_is_overloaded(self, other, &rop_id)) { \
-            r = call_maybe(other, &rop_id, self, NULL); \
+            stack[0] = self; \
+            r = call_maybe(other, &rop_id, stack, 1); \
             if (r != Py_NotImplemented) \
                 return r; \
             Py_DECREF(r); \
             do_other = 0; \
         } \
-        r = call_maybe(self, &op_id, other, NULL); \
+        stack[0] = other; \
+        r = call_maybe(self, &op_id, stack, 1); \
         if (r != Py_NotImplemented || \
             Py_TYPE(other) == Py_TYPE(self)) \
             return r; \
         Py_DECREF(r); \
     } \
     if (do_other) { \
-        return call_maybe(other, &rop_id, self, NULL); \
+        stack[0] = self; \
+        return call_maybe(other, &rop_id, stack, 1); \
     } \
     Py_RETURN_NOTIMPLEMENTED; \
 }
@@ -5786,7 +5785,7 @@
 static Py_ssize_t
 slot_sq_length(PyObject *self)
 {
-    PyObject *res = call_method(self, &PyId___len__, NULL);
+    PyObject *res = call_method(self, &PyId___len__, NULL, 0);
     Py_ssize_t len;
 
     if (res == NULL)
@@ -5846,6 +5845,7 @@
 static int
 slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
 {
+    PyObject *stack[2];
     PyObject *res;
     PyObject *index_obj;
 
@@ -5854,10 +5854,14 @@
         return -1;
     }
 
-    if (value == NULL)
-        res = call_method(self, &PyId___delitem__, index_obj, NULL);
-    else
-        res = call_method(self, &PyId___setitem__, index_obj, value, NULL);
+    stack[0] = index_obj;
+    if (value == NULL) {
+        res = call_method(self, &PyId___delitem__, stack, 1);
+    }
+    else {
+        stack[1] = value;
+        res = call_method(self, &PyId___setitem__, stack, 2);
+    }
     Py_DECREF(index_obj);
 
     if (res == NULL) {
@@ -5905,12 +5909,17 @@
 static int
 slot_mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value)
 {
+    PyObject *stack[2];
     PyObject *res;
 
-    if (value == NULL)
-        res = call_method(self, &PyId___delitem__, key, NULL);
-    else
-        res = call_method(self, &PyId___setitem__, key, value, NULL);
+    stack[0] = key;
+    if (value == NULL) {
+        res = call_method(self, &PyId___delitem__, stack, 1);
+    }
+    else {
+        stack[1] = value;
+        res = call_method(self, &PyId___setitem__, stack, 2);
+    }
 
     if (res == NULL)
         return -1;
@@ -5942,7 +5951,8 @@
        slot_nb_power, so check before calling self.__pow__. */
     if (Py_TYPE(self)->tp_as_number != NULL &&
         Py_TYPE(self)->tp_as_number->nb_power == slot_nb_power) {
-        return call_method(self, &PyId___pow__, other, modulus, NULL);
+        PyObject* stack[2] = {other, modulus};
+        return call_method(self, &PyId___pow__, stack, 2);
     }
     Py_RETURN_NOTIMPLEMENTED;
 }
@@ -6009,7 +6019,7 @@
 slot_nb_index(PyObject *self)
 {
     _Py_IDENTIFIER(__index__);
-    return call_method(self, &PyId___index__, NULL);
+    return call_method(self, &PyId___index__, NULL, 0);
 }
 
 
@@ -6031,8 +6041,9 @@
 static PyObject *
 slot_nb_inplace_power(PyObject *self, PyObject * arg1, PyObject *arg2)
 {
+    PyObject *stack[1] = {arg1};
     _Py_IDENTIFIER(__ipow__);
-    return call_method(self, &PyId___ipow__, arg1, NULL);
+    return call_method(self, &PyId___ipow__, stack, 1);
 }
 SLOT1(slot_nb_inplace_lshift, "__ilshift__", PyObject *)
 SLOT1(slot_nb_inplace_rshift, "__irshift__", PyObject *)
@@ -6153,7 +6164,8 @@
 static PyObject *
 slot_tp_getattro(PyObject *self, PyObject *name)
 {
-    return call_method(self, &PyId___getattribute__, name, NULL);
+    PyObject *stack[1] = {name};
+    return call_method(self, &PyId___getattribute__, stack, 1);
 }
 
 static PyObject *
@@ -6220,14 +6232,19 @@
 static int
 slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value)
 {
+    PyObject *stack[2];
     PyObject *res;
     _Py_IDENTIFIER(__delattr__);
     _Py_IDENTIFIER(__setattr__);
 
-    if (value == NULL)
-        res = call_method(self, &PyId___delattr__, name, NULL);
-    else
-        res = call_method(self, &PyId___setattr__, name, value, NULL);
+    stack[0] = name;
+    if (value == NULL) {
+        res = call_method(self, &PyId___delattr__, stack, 1);
+    }
+    else {
+        stack[1] = value;
+        res = call_method(self, &PyId___setattr__, stack, 2);
+    }
     if (res == NULL)
         return -1;
     Py_DECREF(res);
@@ -6295,7 +6312,7 @@
 slot_tp_iternext(PyObject *self)
 {
     _Py_IDENTIFIER(__next__);
-    return call_method(self, &PyId___next__, NULL);
+    return call_method(self, &PyId___next__, NULL, 0);
 }
 
 static PyObject *
@@ -6323,14 +6340,19 @@
 static int
 slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
 {
+    PyObject* stack[2];
     PyObject *res;
     _Py_IDENTIFIER(__delete__);
     _Py_IDENTIFIER(__set__);
 
-    if (value == NULL)
-        res = call_method(self, &PyId___delete__, target, NULL);
-    else
-        res = call_method(self, &PyId___set__, target, value, NULL);
+    stack[0] = target;
+    if (value == NULL) {
+        res = call_method(self, &PyId___delete__, stack, 1);
+    }
+    else {
+        stack[1] = value;
+        res = call_method(self, &PyId___set__, stack, 2);
+    }
     if (res == NULL)
         return -1;
     Py_DECREF(res);

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list