Skip to content

Cut the fixed cost of a macro call (0147) - #448

Merged
rmorozov merged 6 commits into
masterfrom
claude/project-thread-7gy5jy
Oct 11, 2026
Merged

rmorozov merged 6 commits into
masterfrom
claude/project-thread-7gy5jy

Conversation

@rmorozov

Copy link
Copy Markdown
Member

Requested by Ruslan · project thread

Before: besides rendering its body (about 530 instructions on Render/macros), each macro call spent about 1,340 instructions binding and checking its arguments and taking its slot frame.

After: about 540 fewer per call. Instruction counts (bench/count.py --baseline, Release, against master 2486c22):

Case Base New Change
Render/macros 1,019,941 911,229 -10.66%
Render/config_file 1,916,147 1,889,223 -1.41%
Render/html_autoescape 1,610,791 1,594,279 -1.03%
Render/plain_text 986 987 +0.10%
every other Load and Render case within ±0.12%

Allocations are unchanged. Render/macros allocates 32 more bytes per render, because a stored default now carries a flag.

How. All changes are in src/statements.* and src/render_workspace.*.

  • One pass over the keyword arguments finds the argument each one binds and counts the extras. The check is then two comparisons. The throwing path (ThrowExtraMacroArgs) keeps the old messages and their order.
  • Two per-call scans now run once. Whether any default names another argument is computed at Load. Whether each stored default is mutable is computed when the callable is made.
  • Binding forwards each value into its slot. Before, it was copied into a by-value parameter and then moved.
  • Binding caller/kwargs/varargs is skipped for a macro that catches none of them.
  • RenderWorkspace::Take/Release are inline while the current chunk has room.

Not done: passing the evaluated arguments by rvalue.

  • A prototype gained only another -1.25% on Render/macros.
  • It needs Callable::StatementCallable in internal_value.h, which 0140 is reworking, and the call sites in expression_evaluator.cpp, which wave 2 owns.
  • After 0140 a string copy costs only a reference count, so the gain would shrink further.
  • The public user_callable.h is untouched.

The details are in docs/tasks/0147, which is marked done.

Behaviour change, toward Jinja2. A default that names another argument (b=a) is now bound as evaluated. It is no longer copied when it is a list or dict, so b is the same list as a, as in Python. The new corpus case statements.macro_default_refers_arg_mutable pins this. Defaults stored at definition are still copied on every call.

Four more corpus cases cover the argument checks: macro_kwargs_too_many_args, macro_varargs_unknown_kwarg, macro_kwarg_and_kwargs and macro_kwarg_repeats_positional_kwargs.

Process.

  • Roles: main session plus the verifier. The verifier took 1 round. It ran ASan+UBSan ctest (3/3), compared 56 binding templates across old C++, new C++ and Python 3.1.6, and checked clang-tidy 22 and clang-format on the changed lines.
  • It found the undocumented b=a change. That is now documented and tested.
  • Red pushes so far: 0.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QmXwK3VURSCbAQdyqzfjsy


Generated by Claude Code

Render/macros -10.7% instructions: per-macro flags computed at Load or when
the callable is made, one pass over the keyword arguments, values forwarded
into their slots instead of copied then moved, and RenderWorkspace Take/Release
inline while the current chunk has room. Corpus cases for the argument checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QmXwK3VURSCbAQdyqzfjsy
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QmXwK3VURSCbAQdyqzfjsy
@rmorozov rmorozov self-assigned this Oct 10, 2026
@rmorozov
rmorozov marked this pull request as ready for review October 11, 2026 06:36
@rmorozov
rmorozov merged commit 523d59e into master Oct 11, 2026
41 checks passed
rmorozov added a commit that referenced this pull request Oct 11, 2026
0147, 0152 and 0154 are done; 0158, 0159 and 0160 are new.


Claude-Session: https://claude.ai/code/session_01KMcWcpGwhiaEs33YjAgEV4

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants