gh-157407: Shrink _PyArg_Parser and don't write builtin parsers at run time by eendebakpt · Pull Request #157531 · python/cpython · GitHub
Skip to content

gh-157407: Shrink _PyArg_Parser and don't write builtin parsers at run time - #157531

Open
eendebakpt wants to merge 3 commits into
python:mainfrom
eendebakpt:feature/clinic-parser-shrink
Open

eendebakpt wants to merge 3 commits into
python:mainfrom
eendebakpt:feature/clinic-parser-shrink

Conversation

@eendebakpt

@eendebakpt eendebakpt commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Make the argument clinic generated code more efficient:

  1. Store the number of positional-only parameters statically. Argument Clinic now emits .pos = N in the static _PyArg_Parser with allows _PyArg_UnpackKeywords() to skip parser_init().
  2. Reduce the size of _PyArg_Parser. pos/min/max become uint16_t and is_kwtuple_owned becomes uint8_t. custom_msg is removed: it is only needed when reporting a conversion error, so it is now looked up in the format string (";message") on that path. No in-tree parser uses ";message". _parser_init() raises SystemError for more than 65535 keywords. The struct goes from 73 bytes to 48 bytes.

Binary size change: .data / file size, main vs. this PR (Release builds):

.data .text file
Windows python316.dll (MSVC) −11,528 +48 −11,264
Windows, all *.pyd + dll −22,008 +48 −24,064
Linux python (gcc 15, static libpython) −9,120 +192
Linux, python + all *.so −17,408 +192

Possible follow-ups

With some more refactoring this could go further:

  • Non-format parsers (the common case, ~750 of ~760 in CPython) never use format, min or max. Builtin-module parsers also never use once, is_kwtuple_owned or next. A separate, smaller struct for them would save more memory, at the cost of changing the private API.
  • More static initialization: Argument Clinic could also emit fname, min and max for format-string parsers, so that builtin ones skip parser_init() too. parser_clear() then only needs to reset the kwtuple and once, since the other fields only depend on the static keywords and format.

Generated with help from Claude Code

eendebakpt and others added 2 commits September 14, 2026 22:26
…cally

Argument Clinic now sets _PyArg_Parser.pos statically.  Parsers with a
statically allocated keyword tuple (builtin modules) therefore need no
initialization: _PyArg_UnpackKeywords() skips parser_init() for them,
so they are never written to at run time.

A lazily built keyword tuple is now published last, with
_Py_atomic_store_ptr_release().  parser_clear() keeps pos, since it
only depends on the keywords.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Narrow pos, min, max and is_kwtuple_owned, which are small counts, and
remove custom_msg: it is only needed to report a conversion error, and
can then be looked up in the format string.  Keep format, keywords and
fname first, so that existing positional initializers still work.
_PyArg_Parser shrinks from 72 to 48 bytes on 64-bit platforms.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants