Skip to content

perf: dispatch body children and plain names by kind byte (0141) - #468

Closed
rmorozov wants to merge 7 commits into
masterfrom
claude/project-thread-7au93o
Closed

rmorozov wants to merge 7 commits into
masterfrom
claude/project-thread-7au93o

Conversation

@rmorozov

Copy link
Copy Markdown
Member

Requested by Ruslan · project thread

Before: every child of a template body rendered through one shared virtual call in ComposedRenderer::Render. A body alternates raw text and {{ ... }}, so that one indirect call site changed target on almost every child: in callgrind's branch simulation it was 95% of all indirect mispredicts on mitsuhiko_table (105,000 of 110,265).

After: the body loop switches on the child's kind byte and calls raw text and {{ ... }} directly (inlined); every other kind still goes through the vptr. A {{ name }} renders through a direct ValueRefExpression::Render. Simulated mispredicts drop 3-71% (mitsuhiko_table -71%, macros -24%, for_loop_vars -27%), instructions move -0.19..+1.35%.

How: src/renderer.h (ComposedRenderer::Render defined after the classes it dispatches to), RenderOf in src/expression_evaluator.h, ValueRefExpression::Render override (and SelfRefExpression keeps the base's). The full numbers, and the rejected wider variant (the same switch at every Evaluate/EvaluateRef call site: Ir up to +5.5%, mispredicts up on expressions), are in docs/tasks/0141-switch-dispatch.md.

For the perf verdict: instruction counts rise on most Render cases (for_loop_vars +1.35%, expressions +1.08%, many_tags +0.76%, others +0.1..+0.7%) in exchange for the mispredict drop. The cloud VM has no hardware counters and its wall time is too noisy (medians -2..-11%, minimums -3..+4%) to settle it; a quiet machine should.

Includes #464 (CI ccache branch) as a merge commit, at Ruslan's request so this PR's CI runs use it; its files drop out of this diff once #464 merges.

Roles: main session, verifier (1 round, ready: tests, ASan+UBSan, differential vs base and Python Jinja2 across 5 modes). Red pushes: none so far.

🤖 Generated with Claude Code

https://claude.ai/code/session_018j47FUYwQ9s69rR6Jesrwc


Generated by Claude Code

Builds compiled everything from scratch in every job (macOS build steps grew
from 47 to 166 job-minutes between Oct 3 and Oct 11) and MSVC built serially
with NMake. Jobs now restore a per-job ccache saved by master pushes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SiUW6zog3Gpn13eX59fE1c
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SiUW6zog3Gpn13eX59fE1c
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SiUW6zog3Gpn13eX59fE1c
ComposedRenderer::Render calls raw text and {{ ... }} children directly,
switching on the node's kind byte, instead of one shared virtual call that
mispredicts as a body alternates the two. A plain name renders through a
direct ValueRefExpression::Render. Measured in docs/tasks/0141.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018j47FUYwQ9s69rR6Jesrwc
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018j47FUYwQ9s69rR6Jesrwc
@rmorozov rmorozov self-assigned this Oct 11, 2026
Inline in the header, its larger body changed GCC's inlining in the parser
files that include it: Load +0.06..+1.22% in instructions on CI. In
statements.cpp Load is back to -0.04..+0.08%.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018j47FUYwQ9s69rR6Jesrwc
@rmorozov rmorozov closed this Oct 11, 2026
rmorozov pushed a commit that referenced this pull request Oct 11, 2026
Switch dispatch for body children and plain names (closed PR #468)
traded +0.1..+1.35% Render instructions for fewer mispredicts in
callgrind's last-target branch model only; the perf track rejected it.
The write-up, the rejected wide variant, the header-inlining trap and the
condition for reopening stay in the task file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018j47FUYwQ9s69rR6Jesrwc
rmorozov added a commit that referenced this pull request Oct 11, 2026
Switch dispatch for body children and plain names (closed PR #468)
traded +0.1..+1.35% Render instructions for fewer mispredicts in
callgrind's last-target branch model only; the perf track rejected it.
The write-up, the rejected wide variant, the header-inlining trap and the
condition for reopening stay in the task file.


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

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