Skip to content

A sum, difference, product, quotient, conjunction, disjunction, exclusive disjunction, union or intersection caches the flat list of its operands, and SimplifyHard allocates 5.2% less - #1426

Open
Rafael-SOWNet wants to merge 1 commit into
masterfrom
linear-children-cache
Open

Rafael-SOWNet wants to merge 1 commit into
masterfrom
linear-children-cache

Conversation

@Rafael-SOWNet

@Rafael-SOWNet Rafael-SOWNet commented Sep 18, 2026

Copy link
Copy Markdown
Member

#224, per the comment there: "just simple optimization for the agent".

Sumf.LinearChildren and Mulf.LinearChildren — and, as asked below, the five flatteners of the same shape for and, or, xor, unite and intersect in Functions/Boolean/Simplifier.cs, read by the sort-and-group and push-not-inside rules — flattened a nested sum or product into a lazy chain of Concat/Select enumerables, rebuilt on every enumeration — a difference's subtracted terms each multiplied by -1 anew, a quotient's divisor factors each raised to -1 anew — and every rule that reads a sum as a list reads it again for every candidate. A node is immutable, so the flat list is a property of the node: it is now built once, as an array, from its parts' own cached lists (LazyPropertyA, the same cache the other per-node properties use), and handed out as an IReadOnlyList<Entity>. Memory is linear in the tree, since a nested node's array comes from the arrays cached on its children rather than from walking down again, which is the "redistribute with linear memory" of the issue's sketch.

Measured by the gate, which failed as it should on an improvement:

recorded (6e5986a7) before this PR (89d3b0a7) this PR
SimplifyHard allocation 181,019,056 B 186,284,672 B 171,609,808 B (−5.2% / −7.9%)
SimplifyHard mean 128.8 ms 106.5 ms 103.8 ms

Of that, the sum/product caches alone are 171,275,272 B; the five connective caches put 334,536 B (+0.2%) back — the arrays of chains that are flattened once outweigh the enumerations they save on this benchmark — and are in for uniformity: one rule for what a flattener is, and the generic IBranchGetter machinery (which its own comment called awful) gone.

The other eighteen gated rows are inside tolerance (ParseHard carries the +1.6% the quantifier grammar cost in #1421; Derivate +1.9%, the one-time array on a node that is flattened once and read once). performance-baseline.json is updated in the same change, as version_performance_control.md says an improvement must be, with the commit and date it was measured at.

Also corrects the Modf record's summary, which claimed the remainder follows the dividend's sign as C#'s % does; it is the floored remainder ((-7) mod 3 is 2), as the evaluation and Syntax.md have said since #708 — a stale comment, not a behaviour change.

Full suite 11890 passed, 0 failed.

Closes #224.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

@Happypig375

Copy link
Copy Markdown
Member

Are these the only LinearChildren properties? How about And/Or/Xor?

@Rafael-SOWNet

Copy link
Copy Markdown
Member Author

No — five more, and I had missed them: Andf, Orf, Xorf, Unionf and Intersectionf each have a LinearChildren of the same lazy-Concat shape in Functions/Boolean/Simplifier.cs (behind a generic IBranchGetter that the file itself calls "awful"), read by the sort-and-group rules of Simplify and by the push-not-inside rules. Same treatment, same file, Sumf.Flatten shared: a cached array per node built from its children's arrays. Building and measuring now; the figures follow in this thread, and the PR description will say which nodes carry the cache.

…sive disjunction, union or intersection caches the flat list of its operands, and SimplifyHard allocates 5.2% less

#224. LinearChildren flattened a nested sum or product into a lazy chain of Concat and Select
enumerables, built afresh on every enumeration -- and a difference's subtracted terms were
each multiplied by -1 anew, a quotient's divisor factors each raised to -1 anew -- while every
rule that reads a sum as a list reads it again for every candidate. A node is immutable, so the
flat list is a property of the node: it is built once, as an array, from its parts' own cached
lists, and handed out as an IReadOnlyList<Entity> from then on. Memory is linear in the tree,
since a nested node's array is built from the arrays cached on its children rather than by
walking down again, and no enumeration is repeated. The same for the five other associative
chains -- and, or, xor, unite, intersect -- whose flatteners in Functions/Boolean/Simplifier.cs
had the same lazy shape behind a generic branch getter, and read by the sort-and-group rules.

The benchmark gate measured it: with the sum and product caches alone SimplifyHard allocates
171,275,272 B per operation against the 181,019,056 B recorded at 6e5986a, 5.4% less, and
8.1% less than the 186,284,672 B of the commit before this one; the five connective caches
put 334,536 B back (+0.2%), the arrays of chains flattened once outweighing the enumerations
they save, so the whole is 171,609,808 B, 5.2% less. The other eighteen gated rows are within
tolerance, ParseHard carrying the +1.6% the quantifier grammar cost in #1421. Recorded in
performance-baseline.json in the same change, as the gate's own instructions say an
improvement must be.

Also corrects the Modf record's summary, which said the remainder takes the sign of the
dividend as C#'s % does: it is the floored remainder, (-7) mod 3 is 2, as the evaluation and
Syntax.md have said since #708.

Closes #224.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet Rafael-SOWNet changed the title A sum, difference, product or quotient caches the flat list of its terms, and SimplifyHard allocates 5.4% less A sum, difference, product, quotient, conjunction, disjunction, exclusive disjunction, union or intersection caches the flat list of its operands, and SimplifyHard allocates 5.2% less Sep 18, 2026
@Rafael-SOWNet

Copy link
Copy Markdown
Member Author

Done in 5c4a7d91 (force-pushed, one commit): Andf, Orf, Xorf, Unionf, Intersectionf cache their flat operand list exactly as Sumf/Mulf do, and Simplifier.cs's generic branch-getter is gone. Measured by the gate: SimplifyHard 171,609,808 B (−5.2% against the recorded 181,019,056 B); the five connective caches by themselves put 334,536 B (+0.2%) back against the sum/product-only figure — chains of and/or are flattened once and rarely re-read on this benchmark, so their arrays cost slightly more than the enumerations they save. Kept for uniformity; if you would rather have the 0.2% than one rule for all nine, say so and I drop the five. Baseline re-recorded at the new figure; 2255 boolean/set/transformation/simplification tests green.

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.

Linear Children should cache its result

2 participants