Repository navigation
gh-161242: Use AC vectorcall for AttributeError, ImportError, NameError - #161462
Conversation
…ameError
These exceptions and their subclasses had no vectorcall, and `__init__`
parsed keywords through PyArg_ParseTupleAndKeywords with a temporary
empty tuple on every call. Error messages are unchanged except when more
keywords than parameters are given, which now names the unexpected
keyword.
The clinic inputs have no docstring, so docstrings are unchanged and
these exceptions don't gain a `__text_signature__`.
pyperf microbenchmark (PGO+LTO, run serially):
| Benchmark | base | vectorcall |
|----------------------------------|:-------:|:---------------------:|
| AttributeError(s, name=s, obj=s) | 127 ns | 41.9 ns: 3.02x faster |
| ImportError(s, name=s, path=s) | 129 ns | 43.4 ns: 2.96x faster |
| NameError(s, name=s) | 88.3 ns | 39.3 ns: 2.24x faster |
| ModuleNotFoundError(s, name=s) | 89.9 ns | 40.6 ns: 2.21x faster |
| AttributeError(s) | 39.4 ns | 32.1 ns: 1.23x faster |
| UnboundLocalError(s) | 38.6 ns | 31.8 ns: 1.22x faster |
| NameError(s) | 38.5 ns | 32.2 ns: 1.19x faster |
| ImportError(s) | 40.7 ns | 34.4 ns: 1.18x faster |
| attribute_error_to_none() | 176 ns | 173 ns: 1.02x faster |
Benchmark code:
```python
import pyperf
runner = pyperf.Runner()
setup = """s = 'x'
class O: pass
o = O()
def attribute_error_to_none():
try:
o.x
except AttributeError:
pass
"""
for stmt in ["AttributeError(s)", "AttributeError(s, name=s, obj=s)",
"NameError(s)", "NameError(s, name=s)",
"ImportError(s)", "ImportError(s, name=s, path=s)",
"ModuleNotFoundError(s, name=s)", "UnboundLocalError(s)",
"attribute_error_to_none()"]:
runner.timeit(stmt, stmt, setup)
```
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| if (!PyArg_ParseTupleAndKeywords(empty_tuple, kwds, "|$OOO:ImportError", kwlist, | ||
| &name, &path, &name_from)) { | ||
| Py_DECREF(empty_tuple); | ||
| if (BaseException_init((PyObject *)self, args, NULL) == -1) { |
There was a problem hiding this comment.
There is a behavior change for e = ImportError('a'); e.__init__('b', foo=1). The call to e.__init__ fails both on main and the PR, but with the PR e.args is not modified. I think the change is good and no real issue. Maybe not even add a test for or a note in the news entry.
There was a problem hiding this comment.
Yea, I was trying for a while to find a good way to "forward" an args tuple like the original did but it doesn't make a big performance different (< 5 ns) and arguably the new behavior is better.
| } | ||
| obj = fastargs[1]; | ||
| skip_optional_kwonly: | ||
| __clinic_args = PyTuple_FromArray(args, nargs); |
There was a problem hiding this comment.
I think you plan to tackle this in a followup PR right?
There was a problem hiding this comment.
Yes, I'm trying to keep PRs to just one of
- Add a Argument Clinic new code gen + convert minimal example test types to use it
- Change existing code gen (which can effect a lot very quickly)
- Move things to Argument Clinic and/or Vectorcall. For this I plan to make a
type-refactorissue then do a large migration once Generate vectorcall code to parse arguments using Argument Clinic #87613 is done replacing the much slowerPyArg_ParseTupleAndKeywordsbroadly + handwritten ones that are flat for performance.
For this particular piece it's a Clinic codegen optimization that we can avoid a new tuple some of the time. In general I think the goto + local variable structure of a lot of the clinic codegen is a lot more complex than needed, have lots of thoughts but separate feature chain.
These exceptions and their subclasses had no vectorcall, and
__init__parsed keywords through PyArg_ParseTupleAndKeywords with a temporary empty tuple on every call. Error messages are unchanged except when more keywords than parameters are given, which now names the unexpected keyword.The clinic inputs have no docstring, so docstrings are unchanged and these exceptions don't gain a
__text_signature__.pyperf microbenchmark (PGO+LTO, run serially):
Benchmark code: