render-pipeline: measure cross-reference invalidation (step 0 of #575; no behaviour change) - #613
Merged
Merged
Conversation
…es (measurement only) What changed: - RenderCache gains plain Cell counters for `adapted` and `assembled` hits/misses and a label-pass counter bumped once per render_cached layout pass; `counters()` returns them with the existing `blocks` stats as `CacheCounters` (`since` for deltas). `stats()` is unchanged. Nothing on the render path reads the counters: no behaviour change. - examples/xref_measure.rs: #535's 450-section article (byte-identical generator) plus a +toc+pageref variant, through protocol::handle_line with a warm cache; three edits (no-label, renumber, move-label). - tests/cache_counters.rs: the counters count cold misses, warm hits and passes. What was run: - cargo test --test cache_counters: test result: ok. 1 passed; 0 failed - generator check against #535's large_doc_bench.rs: identical; 637761 bytes Next step: full measurement (5 runs, 450 sections) and the results doc. Implementation-Agent: claude-opus-5 subagent of daniel-parent Commit-Executor: daniel-parent subagent Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
…575 step 0) What changed: - docs/design/incremental-cross-references-measurements.md: adapted / blocks / assembled misses, layout passes and median latency for the no-label, renumber and move-label edits on #535's 450-section article and a +toc+pageref variant, with the exact commands, plus a stage split showing warm == cold latency because adapt_cached's split_at_page_breaks walks the source prefix per block (quadratic). What was run: - cargo run --release --example xref_measure -- 5 450 (exit=0; table in the doc) - cargo run --release --example stages -- doc150.tex 2 / doc535.tex 2 - cargo test --test incremental --test cache_counters: test result: ok. 1 passed; 0 failed; 1 ignored (incremental, 375.30s) test result: ok. 1 passed; 0 failed (cache_counters) Next step: Commander ruling on #575; the adapter prefix scans are a separate render-pipeline performance finding for its owner. Implementation-Agent: claude-opus-5 subagent of daniel-parent Commit-Executor: daniel-parent subagent Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
This was referenced Sep 15, 2026
Contributor
Author
|
daniel-parent independent review (subagent)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Step 0 of the incremental cross-references proposal (#575): measure before choosing. No behaviour change and no design decision.
RenderCache::counters()(crates/render-pipeline/src/incremental.rs). PlainCellhit and miss counters for theadaptedandassembledmaps, and alabel_passescounter thatrender_cachedbumps once per layout pass. They are returned together with the existingblocksstats asCacheCounters, andsince()gives deltas.stats()is unchanged, and nothing on the render path reads the counters.examples/xref_measure.rsruns compiler: 3x faster large-document compile — shaping memo, glyph-table index, lent cleveref config (#65) #535's 450-section article throughprotocol::handle_linewith a warm cache. The generator is byte-identical to compiler: 3x faster large-document compile — shaping memo, glyph-table index, lent cleveref config (#65) #535's (637 761 B). The bench also runs a+toc+pagerefvariant. It measures three edits:no-label,renumber(a\sectioninserted before section 1) andmove-label(a paragraph with no label moved across a heading, so that one\labelchanges page). It reports the misses for each map, the layout passes, and the median-of-5 latency.tests/cache_counters.rschecks that a cold render counts misses and a warm one counts hits, and that the pass counter equalsRendered::passes.docs/design/incremental-cross-references-measurements.mdholds the results and the exact commands. It is a separate file, so it does not conflict with docs: design proposal — incremental cross-references (for Commander decision) #575.The cold render takes 18 278 ms for
535and 34 738 ms for535+toc+pageref. The coldadaptedcount is 4 115 misses out of 5 013 lookups per pass, so every distinct block misses.Why
#575 §5 step 0 asks whether the IDE worker is already fast on these three edits before any option is chosen. Findings:
renumbermissesadaptedfor every distinct block in every pass.move-labelmisses it for every distinct block in pass 2.renumber.adapter::adapt_cached, and it grows quadratically.stages, run warm: adapt takes 2 013 ms at 150 sections and 16 368 ms at 450. Parse plus typeset plus assemble is under 100 ms.split_at_page_breaks. Three per-block walks of the source prefix account for it:in_theorem_environment(717 ms at 150 sections; added by2945f61a), theListItembranch (831 ms;list_stack_atand others) and the after-list branch (290 ms).adapter.rs, not as a proposal. Until it is fixed, render-pipelinelabels_fpnarrowing or page seeding could not show a latency win on this document.Overlap check
RenderCache: FT-070 memory: measure the breakdown, derive the per-glyph carets (Cluster 160 -> 88 B) #232, render-pipeline: layout cache keys tag each kind by its own discriminant (3 colliding tags, latent) #182, FT-070: a negotiated page window — the fix for a 500 KB document that has no reply #294, FT-070 memory: pages reference the assembled items instead of cloning them (-244 MiB at 2 MB) #283 and FT-070: engine performance harness, committed baseline and a >5% regression gate #206. None adds counters. FT-070 memory: pages reference the assembled items instead of cloning them (-244 MiB at 2 MB) #283 and FT-070: profile the warm keystroke, and stop copying a restored block's item list #273 touchincremental.rs, but this PR only adds fields and methods, so there is no textual overlap in the hunks they change.cross-reference(docs: design proposal — incremental cross-references (for Commander decision) #575, render-pipeline: floats numbered in \include reading order, LoF/LoT and \ref agree (stacked on #537 and #538) #578, compiler: 3x faster large-document compile — shaping memo, glyph-table index, lent cleveref config (#65) #535, compiler: handle_line 1.4x faster on large documents — no placed-item copies, borrowed session output, pages serialised once (stacked on #535) #579),split_at_page_breaks(render-pipeline: thread VSpace stretch/shrink into vspace_flex (GH-VSKIP-GLUE-STRETCH slice 2, needs re-pin past #606) #607, which threads vspace flex, not the scans),in_theorem_environment(none) andlist_stack_at(render-pipeline: a verbatim body inside a list is a trivlist inside that list (14/17) #268 and render-pipeline: secnumdepth, description, \baselinestretch and run-in headings #292, both behavioural).GH-XREF-MEASURE-STEP0(daniel-parent) matched render-pipeline/xref.Test results
Not done
adapter.rs. There is no change tolabels_fpor to page seeding (that waits on the Commander's ruling on docs: design proposal — incremental cross-references (for Commander decision) #575).handle_linelatency includes an over-limit failure reply: 17.6 MB is over the 16 MiB line limit, and FT-070: a negotiated page window — the fix for a 500 KB document that has no reply #294 is the fix.render msis the meaningful figure.move-labelmoves one section heading's page; it cannot move one label in isolation, because a paragraph has to be moved to do it. Exactly 1 of the 450 headings changed page in both documents.🤖 Generated with Claude Code
https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz