Skip to content

Commit

Permalink
pythongh-90370: avoid temporary tuple creation for vararg in AC
Browse files Browse the repository at this point in the history
Current patch partially address issue, working in case when all
arguments either positional-only or vararg.

This also converts gcd(), lcm() and hypot() functions of the math module
to the Argument Clinic.  Fix issue python#101123.

Objects/setobject.c and Modules/gcmodule.c adapted.  This fixes slight
performance regression for set methods, introduced by
python#115112:

| Benchmark            | ref    | patch                |
|----------------------|:------:|:--------------------:|
| set().update(s1, s2) | 354 ns | 264 ns: 1.34x faster |

Benchmark code:
```py
import pyperf
s1, s2 = {1}, {2}
runner = pyperf.Runner()
runner.bench_func('set().update(s1, s2)', set().update, s1, s2)
```
  • Loading branch information
skirpichev committed Oct 28, 2024
1 parent 19e93e2 commit ad5e812
Show file tree
Hide file tree
Showing 13 changed files with 360 additions and 253 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 = Py_MAX(nargs - 1, 0);
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=f0f68154d891dd6d 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 ad5e812

Please sign in to comment.