| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept this contribution by verifying everyone involved has signed the PSF contributor agreement (CLA). CLA MissingOur records indicate the following people have not signed the CLA: For legal reasons we need all the people listed to sign the CLA before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. If you have recently signed the CLA, please wait at least one business day You can check yourself to see if the CLA has been received. Thanks again for the contribution, we look forward to reviewing it! |
Sorry, something went wrong.
…keep backward compatibility
There was a problem hiding this comment.
Just a couple of comments here.
I wonder if it would be simpler to restrict the scope of such a change to only signatures like def f(*args, kw1=<default1>, kw2=<default2>, ...), like print()/min()/max() have.
Sorry, something went wrong.
| @@ -3341,16 +3342,16 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg | |||
| for (Py_ssize_t i = 0; i < nargs - 1; ++i) { | |||
| PyTuple_SET_ITEM(__clinic_args, i, args[1 + i]); | |||
There was a problem hiding this comment.
This seems suspicious -- isn't __clinic_args a pointer-to-PyObject-pointer, not a pointer-to-tuple?
Sorry, something went wrong.
There was a problem hiding this comment.
My fault. The tuple doesn't need to be created.
Sorry, something went wrong.
| a = args[0]; | ||
| __clinic_args = args[1]; | ||
| return_value = test_vararg_impl(module, a, __clinic_args); | ||
| __clinic_args = (PyObject *const *)args[1]; |
There was a problem hiding this comment.
It would be nice to avoid such unsafe casts. Maybe passing a PyObject *** into _PyArg_UnpackKeywordsWithVarargFast instead?
Sorry, something went wrong.
There was a problem hiding this comment.
This copy and cast are unnecessary.
Maybe __clinic_args = args + 1; is enough, note the args refers to the 2nd argument of function test_vararg, assuming it's not assigned in line 3389.
Sorry, something went wrong.
| buf[vararg] = (PyObject *)&args[vararg]; | ||
|
|
||
| /* copy required positional args */ | ||
| for (i = 0; i < vararg; i++) { |
There was a problem hiding this comment.
Isn't it possible to avoid copying at all by directly passing some sub-buffer of the *args buffer into the ..._impl function?
Sorry, something went wrong.
There was a problem hiding this comment.
You are right. None of the positional args or variable-length args need to be copied. I will let _PyArg_UnpackKeywordsWithVarargFast parse default and keyword args only.
Sorry, something went wrong.
|
Thanks for the PR and benchmarks @colorfulappl, I am current blocked a bit but plan to review this soon! |
Sorry, something went wrong.
Thank you @isidentical ! |
Sorry, something went wrong.
|
Sure, then just ping me whenever the new patch ready and I'll try to take a look at it then! |
Sorry, something went wrong.
# Conflicts: # Python/clinic/bltinmodule.c.h
|
Sorry for such a long delay. When generate parsing code for function with varargs, this PR:
I'd really appreciate it if you could take a look and give me some advice @isidentical . |
Sorry, something went wrong.
|
Benchmark Result Benchmark: Environment: +-----------------------------------------------------+---------+-----------------------+ | Benchmark | base | opt | +=====================================================+=========+=======================+ | print(a) | 133 ns | 126 ns: 1.06x faster | +-----------------------------------------------------+---------+-----------------------+ | print(a, b, c) | 337 ns | 326 ns: 1.04x faster | +-----------------------------------------------------+---------+-----------------------+ | print(a, b, c, *v) | 679 ns | 661 ns: 1.03x faster | +-----------------------------------------------------+---------+-----------------------+ | print(a, sep='', file=stdout) | 134 ns | 125 ns: 1.07x faster | +-----------------------------------------------------+---------+-----------------------+ | print(a, sep='', flush=True, file=stdout) | 571 ns | 563 ns: 1.01x faster | +-----------------------------------------------------+---------+-----------------------+ | print(*v, sep='', flush=True, file=stdout) | 833 ns | 819 ns: 1.02x faster | +-----------------------------------------------------+---------+-----------------------+ | print(a, b, c, *v, sep='', flush=True, file=stdout) | 1.18 us | 1.16 us: 1.02x faster | +-----------------------------------------------------+---------+-----------------------+ | Geometric mean | (ref) | 1.03x faster | +-----------------------------------------------------+---------+-----------------------+ |
Sorry, something went wrong.
|
I found there are some bugs when argument clinic processes varargs and made a fix in #32092. |
Sorry, something went wrong.
Thanks for the advice; I will pay attention to it. I added some test cases for the class method __new__ (without actually creating the objects, only packing and returning the arguments). And I found that when the __new__ or __init__ function accepts a vararg only, the argument clinic generated code does not check keyword arguments. e.g. # Signature of this function:
#
# @classmethod
# _testclinic.TestClassVarargOnly.__new__(cls, *args)
test_class_new_vararg_only(kw=1)
# Here we expect an error like "TypeError: TestClassVarargOnly.__new__() got an unexpected keyword argument 'kw'",
# but nothing raised.We may need to fix it in another issue. |
Sorry, something went wrong.
| max_pos | ||
| ), indent=4)) | ||
| p.converter.parser_name, | ||
| p.name, |
There was a problem hiding this comment.
I would expect here p.converter.name instead.
I'm trying your patch for converting some vararg functions in the math module (see the old conversion attempt), and it seems that without this change autogenerated code is broken, because it references a Python parameter name instead of C (i.e., coordinates, instead of args):
static PyObject *
math_hypot_impl(PyObject *module, Py_ssize_t varargssize,
PyObject *const *args);
static PyObject *
math_hypot(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
Py_ssize_t varargssize = nargs;
PyObject *const *__clinic_args;
if (!_PyArg_CheckPositional("hypot", nargs, 0, PY_SSIZE_T_MAX)) {
goto exit;
}
__clinic_args = coordinates + 0;
return_value = math_hypot_impl(module, varargssize, __clinic_args);
exit:
return return_value;
}See docs. BTW, I think there should be a test for that.
Sorry, something went wrong.
There was a problem hiding this comment.
Hardcoding "args" will work too, but I'm not sure it's a good idea.
Sorry, something went wrong.
There was a problem hiding this comment.
Thanks for the advice.
When the function signature is METH_FASTCALL, the pass-in parameter is always named args, so replacing p.name with args works.
I made a fix and added two tests. It looks correct now.
Sorry, something went wrong.
| def test_vararg_with_default_with_other_name(self): | ||
| with self.assertRaises(TypeError): | ||
| ac_tester.vararg_with_default_with_other_name() | ||
| self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, b=False), (1, (), False)) |
There was a problem hiding this comment.
You could add an exception test with _b.
Sorry, something went wrong.
|
Not sure, if this is a good idea, but could you consider using nargs instead of varargsize for *_impl() functions? This will reduce needs for changing the body of converted function (take math_gcd as an example to convert from scratch). |
Sorry, something went wrong.
This patch intends to reduce the overhead on passing vararg from Argument Clinic generated code to user-implemented *.impl() function. Before this patch, vararg arguments are packed to a tuple, *_impl() function fetches these arguments by access the tuple items. In such a case, the number of vararg is implied in the tuple object.
@skirpichev Do you mean pass both nargs and max_pos to *_impl() function and let the user to calculate the number of vararg arguments? This may be confusing for users, and violate the purpose of Argument Clinic (Argument Clinic How-To - Abstract):
|
Sorry, something went wrong.
|
Indeed, my suggestion only valid for only-vararg case (as gcd, lcm and so on). Not sure if this case worth a special treatment... |
Sorry, something went wrong.
|
@colorfulappl, could you fix merge conflicts? |
Sorry, something went wrong.
|
Thanks for your work, but this has merge conflicts. Also, issue seems to be fixed. I'm closing this. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
When "Augument Clinic generated code" are parsing arguments, the args are packed to a tuple before passing to callee. This may be unnecessary.
Pass a raw pointer which points to on-stack varargs, and a varargssize integer to indicate how many varargs are passed, can save the time of tuple creation/destruction and value copy.