Skip to content

Commit

Permalink
gh-90370: Argument Clinic: avoid temporary tuple creation for varargs (
Browse files Browse the repository at this point in the history
…#126064)

Avoid temporary tuple creation when all arguments either positional-only
or vararg.

Objects/setobject.c and Modules/gcmodule.c adapted. This fixes slight
performance regression for set methods, introduced by gh-115112.
  • Loading branch information
skirpichev authored Oct 31, 2024
1 parent d07dcce commit 8c22eba
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 222 deletions.
48 changes: 21 additions & 27 deletions Lib/test/clinic.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -4148,36 +4148,32 @@ PyDoc_STRVAR(test_vararg_and_posonly__doc__,
{"test_vararg_and_posonly", _PyCFunction_CAST(test_vararg_and_posonly), METH_FASTCALL, test_vararg_and_posonly__doc__},

static PyObject *
test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args);
test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs,
PyObject *const *args);

static PyObject *
test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
Py_ssize_t nvararg = nargs - 1;
PyObject *a;
PyObject *__clinic_args = NULL;
PyObject *const *__clinic_args = NULL;

if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) {
goto exit;
}
a = args[0];
__clinic_args = PyTuple_New(nargs - 1);
if (!__clinic_args) {
goto exit;
}
for (Py_ssize_t i = 0; i < nargs - 1; ++i) {
PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[1 + i]));
}
return_value = test_vararg_and_posonly_impl(module, a, __clinic_args);
__clinic_args = args + 1;
return_value = test_vararg_and_posonly_impl(module, a, nvararg, __clinic_args);

exit:
Py_XDECREF(__clinic_args);
return return_value;
}

static PyObject *
test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args)
/*[clinic end generated code: output=79b75dc07decc8d6 input=9cfa748bbff09877]*/
test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs,
PyObject *const *args)
/*[clinic end generated code: output=dc2dd9483cc0459e input=9cfa748bbff09877]*/

/*[clinic input]
test_vararg
Expand Down Expand Up @@ -4931,14 +4927,14 @@ PyDoc_STRVAR(Test___init____doc__,
"Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE.");

static int
Test___init___impl(TestObj *self, PyObject *args);
Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args);

static int
Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
{
int return_value = -1;
PyTypeObject *base_tp = TestType;
PyObject *__clinic_args = NULL;
PyObject *const *__clinic_args = NULL;

if ((Py_IS_TYPE(self, base_tp) ||
Py_TYPE(self)->tp_new == base_tp->tp_new) &&
Expand All @@ -4948,17 +4944,16 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) {
goto exit;
}
__clinic_args = PyTuple_GetSlice(0, -1);
return_value = Test___init___impl((TestObj *)self, __clinic_args);
__clinic_args = _PyTuple_CAST(args)->ob_item;
return_value = Test___init___impl((TestObj *)self, nvararg, __clinic_args);

exit:
Py_XDECREF(__clinic_args);
return return_value;
}

static int
Test___init___impl(TestObj *self, PyObject *args)
/*[clinic end generated code: output=0ed1009fe0dcf98d input=2a8bd0033c9ac772]*/
Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args)
/*[clinic end generated code: output=6a64b417c9080a73 input=2a8bd0033c9ac772]*/


/*[clinic input]
Expand All @@ -4976,14 +4971,14 @@ PyDoc_STRVAR(Test__doc__,
"Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE.");

static PyObject *
Test_impl(PyTypeObject *type, PyObject *args);
Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args);

static PyObject *
Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
PyObject *return_value = NULL;
PyTypeObject *base_tp = TestType;
PyObject *__clinic_args = NULL;
PyObject *const *__clinic_args = NULL;

if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
!_PyArg_NoKeywords("Test", kwargs)) {
Expand All @@ -4992,17 +4987,16 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) {
goto exit;
}
__clinic_args = PyTuple_GetSlice(0, -1);
return_value = Test_impl(type, __clinic_args);
__clinic_args = _PyTuple_CAST(args)->ob_item;
return_value = Test_impl(type, nvararg, __clinic_args);

exit:
Py_XDECREF(__clinic_args);
return return_value;
}

static PyObject *
Test_impl(PyTypeObject *type, PyObject *args)
/*[clinic end generated code: output=8b219f6633e2a2e9 input=70ad829df3dd9b84]*/
Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args)
/*[clinic end generated code: output=bf22f942407383a5 input=70ad829df3dd9b84]*/


/*[clinic input]
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3381,8 +3381,8 @@ def test_keyword_only_parameter(self):
def test_varpos(self):
# fn(*args)
fn = ac_tester.varpos
self.assertEqual(fn(), ())
self.assertEqual(fn(1, 2), (1, 2))
self.assertEqual(fn(), ((),))
self.assertEqual(fn(1, 2), ((1, 2),))

def test_posonly_varpos(self):
# fn(a, b, /, *args)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Avoid temporary tuple creation for vararg in argument passing with Argument
Clinic generated code (if arguments either vararg or positional-only).
43 changes: 35 additions & 8 deletions Modules/_testclinic.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ pack_arguments_newref(int argc, ...)
return tuple;
}

static PyObject *
pack_varargs_to_tuple(Py_ssize_t varargssize, PyObject *const *args)
{
assert(!PyErr_Occurred());
PyObject *tuple = PyTuple_New(varargssize);
if (!tuple) {
return NULL;
}
for (Py_ssize_t i = 0; i < varargssize; i++) {
PyTuple_SET_ITEM(tuple, i, Py_NewRef(args[i]));
}
return tuple;
}

/* Pack arguments to a tuple.
* `wrapper` is function which converts primitive type to PyObject.
* `arg_type` is type that arguments should be converted to before wrapped. */
Expand Down Expand Up @@ -970,10 +984,16 @@ varpos
[clinic start generated code]*/

static PyObject *
varpos_impl(PyObject *module, PyObject *args)
/*[clinic end generated code: output=7b0b9545872bdca4 input=f87cd674145d394c]*/
varpos_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args)
/*[clinic end generated code: output=b65096f423fb5dcc input=f87cd674145d394c]*/
{
return Py_NewRef(args);
PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args);
if (!vararg_tuple) {
return NULL;
}
PyObject *result = pack_arguments_newref(1, vararg_tuple);
Py_DECREF(vararg_tuple);
return result;
}


Expand All @@ -989,10 +1009,16 @@ posonly_varpos

static PyObject *
posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b,
PyObject *args)
/*[clinic end generated code: output=5dae5eb2a0d623cd input=c9fd7895cfbaabba]*/
Py_ssize_t nargs, PyObject *const *args)
/*[clinic end generated code: output=d10d43d86d117ab3 input=c9fd7895cfbaabba]*/
{
return pack_arguments_newref(3, a, b, args);
PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args);
if (!vararg_tuple) {
return NULL;
}
PyObject *result = pack_arguments_newref(3, a, b, vararg_tuple);
Py_DECREF(vararg_tuple);
return result;
}


Expand Down Expand Up @@ -1157,8 +1183,9 @@ Proof-of-concept of GH-99233 refcount error bug.
[clinic start generated code]*/

static PyObject *
gh_99233_refcount_impl(PyObject *module, PyObject *args)
/*[clinic end generated code: output=585855abfbca9a7f input=eecfdc2092d90dc3]*/
gh_99233_refcount_impl(PyObject *module, Py_ssize_t nargs,
PyObject *const *args)
/*[clinic end generated code: output=b570007e61e5c670 input=eecfdc2092d90dc3]*/
{
Py_RETURN_NONE;
}
Expand Down
51 changes: 17 additions & 34 deletions Modules/clinic/_testclinic.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 13 additions & 23 deletions Modules/clinic/gcmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 8c22eba

Please sign in to comment.