summaryrefslogtreecommitdiffstats
path: root/Objects/call.c
diff options
context:
space:
mode:
authorMichael J. Sullivan <sully@msully.net>2019-05-26 07:23:34 (GMT)
committerInada Naoki <songofacandy@gmail.com>2019-05-26 07:23:33 (GMT)
commit47dd2f9fd86c32a79e77fef1fbb1ce25dc929de6 (patch)
treef8a1341fc0b1bfe669349714ca90e90686183f8b /Objects/call.c
parent7114c6504a60365b8b0cd718da0ec8a737599fb9 (diff)
downloadcpython-47dd2f9fd86c32a79e77fef1fbb1ce25dc929de6.zip
cpython-47dd2f9fd86c32a79e77fef1fbb1ce25dc929de6.tar.gz
cpython-47dd2f9fd86c32a79e77fef1fbb1ce25dc929de6.tar.bz2
bpo-37017: PyObject_CallMethodObjArgs uses LOAD_METHOD optimization (GH-13516)
Update PyObject_CallMethodObjArgs and _PyObject_CallMethodIdObjArgs to use _PyObject_GetMethod to avoid creating a bound method object in many cases. On a microbenchmark of PyObject_CallMethodObjArgs calling a method on an interpreted Python class, this optimization resulted in a 1.7x speedup.
Diffstat (limited to 'Objects/call.c')
-rw-r--r--Objects/call.c45
1 files changed, 29 insertions, 16 deletions
diff --git a/Objects/call.c b/Objects/call.c
index cb9ccd9..b608492 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -1159,7 +1159,7 @@ _PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name,
/* --- Call with "..." arguments ---------------------------------- */
static PyObject *
-object_vacall(PyObject *callable, va_list vargs)
+object_vacall(PyObject *base, PyObject *callable, va_list vargs)
{
PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];
PyObject **stack;
@@ -1174,7 +1174,7 @@ object_vacall(PyObject *callable, va_list vargs)
/* Count the number of arguments */
va_copy(countva, vargs);
- nargs = 0;
+ nargs = base ? 1 : 0;
while (1) {
PyObject *arg = va_arg(countva, PyObject *);
if (arg == NULL) {
@@ -1196,7 +1196,12 @@ object_vacall(PyObject *callable, va_list vargs)
}
}
- for (i = 0; i < nargs; ++i) {
+ i = 0;
+ if (base) {
+ stack[i++] = base;
+ }
+
+ for (; i < nargs; ++i) {
stack[i] = va_arg(vargs, PyObject *);
}
@@ -1210,23 +1215,26 @@ object_vacall(PyObject *callable, va_list vargs)
}
+/* Private API for the LOAD_METHOD opcode. */
+extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **);
+
PyObject *
-PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...)
+PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
{
- va_list vargs;
- PyObject *result;
-
- if (callable == NULL || name == NULL) {
+ if (obj == NULL || name == NULL) {
return null_error();
}
- callable = PyObject_GetAttr(callable, name);
+ PyObject *callable = NULL;
+ int is_method = _PyObject_GetMethod(obj, name, &callable);
if (callable == NULL) {
return NULL;
}
+ obj = is_method ? obj : NULL;
+ va_list vargs;
va_start(vargs, name);
- result = object_vacall(callable, vargs);
+ PyObject *result = object_vacall(obj, callable, vargs);
va_end(vargs);
Py_DECREF(callable);
@@ -1238,20 +1246,25 @@ PyObject *
_PyObject_CallMethodIdObjArgs(PyObject *obj,
struct _Py_Identifier *name, ...)
{
- va_list vargs;
- PyObject *callable, *result;
-
if (obj == NULL || name == NULL) {
return null_error();
}
- callable = _PyObject_GetAttrId(obj, name);
+ PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
+ if (!oname) {
+ return NULL;
+ }
+
+ PyObject *callable = NULL;
+ int is_method = _PyObject_GetMethod(obj, oname, &callable);
if (callable == NULL) {
return NULL;
}
+ obj = is_method ? obj : NULL;
+ va_list vargs;
va_start(vargs, name);
- result = object_vacall(callable, vargs);
+ PyObject *result = object_vacall(obj, callable, vargs);
va_end(vargs);
Py_DECREF(callable);
@@ -1266,7 +1279,7 @@ PyObject_CallFunctionObjArgs(PyObject *callable, ...)
PyObject *result;
va_start(vargs, callable);
- result = object_vacall(callable, vargs);
+ result = object_vacall(NULL, callable, vargs);
va_end(vargs);
return result;