-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add PyObject_CallOneArg() #81664
Comments
As discussed in https://mail.python.org/archives/list/capi-sig@python.org/thread/X6HJOEX6RRFLNKAQSQR6HLD7K4QZ4OTZ/ it would be convenient to have a function PyObject_CallOneArg() for making calls with exactly 1 positional argument and no keyword arguments. Such calls are very common. This would be analogous to PyObject_CallNoArgs(). |
What do you think about macro like this? #define _PyObject_CALL_WITH_ARGS(func, ...) \
_PyObject_Vectorcall(func, (PyObject*[]){NULL, __VA_ARGS__} + 1, \
sizeof((PyObject*[]){__VA_ARGS__})/sizeof(PyObject*) | PY_VECTORCALL_ARGUMENTS_OFFSET, \
NULL) Pros: it can be used for 2 or 3 arguments too. |
Variadic macros are not part of C89, so that would require changing PEP-7. |
It's a single relatively short macro. I'm not worried about that. I'm more about compiler support for such macros. I'll make a PR with this idea and see what CI says. |
PEP-7 uses C99 since Python 3.6:
Macros cannot be used in all C extensions. For the PEP-587, I was asked to replace macros with functions. I would prefer a PyObject_CallOneArg() function than yet another ugly and dangerous macro. Sometimes, a regular function is better for stack consumption: it doesn't increase the stack consumption in the call site, stack memory is "released" once PyObject_CallOneArg() exit. In the past, I had to disable inlining in some functions to reduce the stack consumption. (I'm not sure if this optimization is still around.) |
AFAIK, gcc, clang, and MSVC support it. Another cons is, general pitfall of macro: _PyObject_CALL_WITH_ARGS(func, PyDict_GetItem(d, key)); // PyDict_GetItem(d, key) is called twice. If two or more arguments are not common, I prefer _PyObject_CallOneArg to macro. |
That's pretty bad and in my opinion a good reason to reject this idea.
I posted some counts at https://mail.python.org/archives/list/capi-sig@python.org/message/3I76R27YMFSKB5JQIM3E4NA3BECVTZHP/ |
Actually, it's not a problem: sizeof() is special, it only looks at the type of its argument, it doesn't execute the code. |
That's not what the PEP says: "Python versions greater than or equal to 3.6 use C89 with several select C99 features" "several select C99 features" is not the same of using C99. Plus, I'm also worried about C++. We also support compiling C++ extensions with the same header files, so it must be C++ compatible also. |
This is just an helper inline function / macro to ease calling _PyObject_Vectorcall. I don't want to make this non-inline function. |
The motivation for this PR is "it would be convenient to have this function". This is probably true, but generally I would not change 47 files at once. Most of the locations are probably not performance sensitive. |
This change doesn't make caller code complicated. It makes caller little simpler. Choosing performance sensitive code is much hard than replace all occurrences. |
Exactly. I see no reason to prefer PyObject_CallFunctionObjArgs(func, arg, NULL) over _PyObject_CallOneArg(func, arg) |
It adds yet another special case underscore function that one cannot use in external projects. So I would not say that is simpler. Has there been any performance measurement at all? |
If you're worried about the underscore, I will make a separate PR to add a non-underscored version, similar to PyObject_CallNoArgs() |
In this case, maybe the whole idea of _PyObject_CallOneArg() is not worth it? Is there any benchmark showing if it's faster or use less stack? |
I don't get what you mean. Do you care about *adding* API with underscore? If so, it doesn't make caller code complex. It can not be a reason to reject changing many files. Or do you care about *using* API with underscore? If so, I'm OK to stop changing some callers which are not tightly coupled with Python. (e.g. sqlite) |
I care about this one. Indeed I think underscore functions should be used in strategic places inside the core interpreter and not sprinkled across the whole code base by default. But in general I also care about not having too many new functions to avoid churn. |
Here is one example: class D(dict):
def __missing__(self, key):
return None
d = D() and now benchmark d[0] **before**: Mean +- std dev: 173 ns +- 1 ns To be precise, I ran: ./python -m perf timeit --duplicate 200 -s 'class D(dict):' -s ' def __missing__(self, key):' -s ' return None' -s 'd = D()' 'd[0]' |
Stefan: I used an underscore by analogy with PyObject_CallNoArgs()/_PyObject_CallNoArg(), where the first is in the limited API and the second is an inline function in the cpython API. But maybe we could revisit that decision. |
I don't want to add many functions to limited/public APIs. When portability is more important than performance, there are many APIs already. Candidate of revert: _csv, _decimal, _sqlite, pyexpat, _testbuffer, _xxtestfuzz |
Victor, what's your opinion on adding PyObject_CallOneArg() to the limited API? |
Test of stack usage: from _testcapi import stack_pointer
class D(dict):
def __missing__(self, key):
sp = stack_pointer()
print(f"stack usage = {TOP - sp}")
return None
d = D()
TOP = stack_pointer()
d[0] **before**: stack usage = 1296 |
The following commit introduced a reference leak: commit 196a530
Example: https://buildbot.python.org/all/#/builders/80/builds/642 test_xml_etree leaked [1, 1, 1] references, sum=3 |
INADA-san, Jeroen: hum, you both proposed a similar fix :-) One fix is enough. See my review on PR 14600. |
It seems that I lost the race ;-) But seriously: if we both independently came up with the same solution, that's a good sign that the solution is correct. |
Thanks for the quick fix ;-) |
Oh, PyObject_CallOneArg() is missing in the What's New in Python 3.9: Can someone please propose a PR to add it? I reopen the issue. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: