| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
There was a problem hiding this comment.
A few comments:
Sorry, something went wrong.
| (PyTuple_GET_SIZE(mc->xargs)) | PY_VECTORCALL_ARGUMENTS_OFFSET, | ||
| mc->vectorcall_kwnames); | ||
|
|
||
| PyMem_Free(tmp_args); |
There was a problem hiding this comment.
PyMem_Free is after the return statement
Sorry, something went wrong.
| PyObject **vectorcall_args; /* Borrowed references */ | ||
| PyObject *vectorcall_kwnames; |
There was a problem hiding this comment.
The ownership here seems a bit complicated and I think it can be simplified. As I understand it, vectorcall_kwnames only exists to ensure that some entries in vectorcall_args stay alive.
Instead, I'd suggest:
Sorry, something went wrong.
There was a problem hiding this comment.
The vectorcall_kwnames is needed as an argument for PyObject_VectorcallMethod in methodcaller_vectorcall (https://github.com/python/cpython/blob/main/Modules/_operator.c#L1666), so we cannot get rid of it.
The ownership is not too hard I think: the objects in vectorcall_args have references borrowed from either mc->args or (the keys from) mc->kwds. I added a comment to clarify this.
Making the vectorcall_args a tuple is still an option though. It requires a bit more memory and a tiny bit of computation in the initialization. It would be the C equivalent of vectorcall_args = args + tuple(kwds). I'll work it out in a branch to see how it compares
Sorry, something went wrong.
There was a problem hiding this comment.
Okay, in that case don't worry about it unless you prefer it as a tuple.
Sorry, something went wrong.
There was a problem hiding this comment.
The diff between the two approaches is this:
eendebakpt/cpython@methodcaller_ft...eendebakpt:cpython:methodcaller_ft_v2
What is nice about making vectorcall_args a tuple is that if there are no keyword arguments, we can reuse mc->args. It does require more operations in the construction though.
Sorry, something went wrong.
There was a problem hiding this comment.
I think either approach is fine! My guess is that the vast majority of uses of methodcaller() do not use keyword arguments.
Sorry, something went wrong.
There was a problem hiding this comment.
Running benchmarks shows what is to be expected from the implementations: using a tuple for vectorcall_args is a bit slower in initializing, except when there are no keyword arguments (since then we reuse the arg tuple). Differences are small though.
Since using a tuple leads to cleaner code and the majority of uses is without keywords I slightly prefer the tuple approach. I will open a new PR for it.
Sorry, something went wrong.
There was a problem hiding this comment.
Overall this looks good to me. A few minor formatting suggestions below.
Sorry, something went wrong.
| methodcaller_vectorcall( | ||
| methodcallerobject *mc, PyObject *const *args, size_t nargsf, PyObject* kwnames) |
There was a problem hiding this comment.
I think the more common formatting looks like:
methodcaller_vectorcall(methodcallerobject *mc, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
Sorry, something went wrong.
| assert(1 + number_of_arguments <= _METHODCALLER_MAX_ARGS); | ||
| memcpy(tmp_args + 1, mc->vectorcall_args, sizeof(PyObject *) * number_of_arguments); | ||
|
|
||
| PyObject *result = PyObject_VectorcallMethod( |
There was a problem hiding this comment.
Up to you, but I'd write this without the temporary result variable like:
return PyObject_VectorcallMethod(...);
Sorry, something went wrong.
| return result; | ||
| } | ||
|
|
||
| static int _methodcaller_initialize_vectorcall(methodcallerobject* mc) |
There was a problem hiding this comment.
| static int _methodcaller_initialize_vectorcall(methodcallerobject* mc) | |
| static int | |
| _methodcaller_initialize_vectorcall(methodcallerobject *mc) |
Sorry, something went wrong.
Co-authored-by: Sam Gross <colesbury@gmail.com>
Co-authored-by: Sam Gross <colesbury@gmail.com>
| Back | FazBrowse Home | New Git URL |
The function operator.methodcaller is not thread-safe since the additional of the vectorcall method in ##107201. In the free-threading build the issue is easy to trigger, for the normal build harder.
We could either remove the vectorcall implementation, or adapt the vectorcall implementation to be thread safe (for both the normal and free-threading build). In this PR we make the methodcaller safe by
Benchmark results:
(old machine, no PGO, comparing this PR with the same PR but the vectorcall disabled)
Benchmark scriptimport pyperf setup = """ from operator import methodcaller as mc arr = [] call = mc('sort') call_kwarg = mc('sort', reverse=True) """ runner = pyperf.Runner() runner.timeit(name="call", stmt="call(arr)", setup=setup) runner.timeit(name="creation", stmt="call = mc('sort')", setup=setup) runner.timeit(name="creation+call", stmt="call = mc('sort'); call(arr)", setup=setup) runner.timeit(name="call kwarg", stmt="call_kwarg(arr)", setup=setup) runner.timeit(name="creation kwarg", stmt="call = mc('sort', reverse=True)", setup=setup) runner.timeit(name="creation+call kwarg", stmt="call = mc('sort', reverse=True); call(arr)", setup=setup)