Skip to content

Benchmark trigonometry against precision, and stop discarding the allocations - #850

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
bench/trig-precision
Aug 9, 2026
Merged

Benchmark trigonometry against precision, and stop discarding the allocations#850
Rafael-SOWNet merged 1 commit into
masterfrom
bench/trig-precision

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Both halves of #167, after @Happypig375 said "add as many benchmarks as needed, maybe profile memory use as well".

Trigonometry against precision

CommonFunctionsInterVersion had no trigonometric case at all, at any precision — which is what the issue asked for. EvalTrig and EvalTrigPrecise evaluate sin(1) + cos(1) + tan(1) at the default hundred digits and at five hundred.

Measured on one machine while writing them, so read the shape rather than the numbers:

digits per evaluation
100 (default) 3.8 ms
200 10.2 ms
500 25.4 ms
1000 146 ms

The issue's premise — "arbitrary precision of trig functions cost a lot in performance" — holds, and now has a row that will show it moving.

Both build their nodes on every call, and that is not cosmetic

An Entity caches its own Evaled, so the obvious form of this benchmark — one shared instance, evaluated repeatedly — measures a dictionary lookup instead of any arithmetic:

same instance, 100000 evaluations : 1 ms total
fresh nodes,     1000 evaluations : 996 ms

Building the nodes costs microseconds against milliseconds of arithmetic, so including it does not move the figure; evaluating a cached instance would move it to zero.

Which is what EvalEasy has been measuring

That is the explanation for a number already in the table. EvalEasy evaluates one static instance, and its row falls to 2 ns in the latest column:

12,059 → … → 34 → 72 → 68 → 84 → 28 → 11 → 11 → 13 → 9 → 2 ns

That is not the arithmetic getting faster — it is the row silently changing meaning when the evaluation cache landed. Left exactly as it is, because rewriting it would break the comparison the file exists to provide, but the trap is now written down in the benchmark and in the document.

Allocations were being collected and thrown away

The class has carried [MemoryDiagnoser] all along. Program.cs asked for "Mean", "Error", "StdDev" and nothing else, so the figure was measured on every run and then dropped on the floor. It now asks for "Allocated" as well — which is what TransformationLayer already does, with a comment saying the regressions it exists to catch are invisible in the timings.

Verified rather than assumed, because TableToString filters columns by header and silently skips one it cannot find — so a wrong string here would have been a no-op that looked like a fix. Ran BenchmarkDotNet with [MemoryDiagnoser] and printed the headers:

... | Mean | Error | Allocated

What this does not do

It does not add a column to version_performance_control.md. Columns there are whole-suite runs produced by the Kernel Benchmark workflow, and this PR's rows will appear in the next one — the document says so rather than leaving a reader to wonder why two rows are blank.

🤖 Generated with Claude Code

…ocations

Both halves of what #167 asked for, after Happypig375 said to add the
benchmarks and to profile memory as well.

**Trigonometry against precision.** CommonFunctionsInterVersion had no
trigonometric case at any precision. EvalTrig and EvalTrigPrecise evaluate
sin(1) + cos(1) + tan(1) at the default hundred digits and at five hundred.
Measured while writing them: 3.8 ms, 10.2 ms at two hundred, 25.4 ms at five
hundred and 146 ms at a thousand, so the issue's premise holds and now has a
row that will show it moving.

Both build their nodes on every call rather than evaluating a shared instance.
That is not a stylistic choice: an Entity caches its own Evaled, so the
obvious form of this benchmark measures a dictionary lookup. A hundred
thousand evaluations of one instance take a millisecond in total; a thousand
evaluations of fresh nodes take a second.

Which is also what EvalEasy has been measuring since that cache landed, and
why it reads two nanoseconds. Left as it is so the column history stays
comparable, with the trap written down in both the benchmark and the document.

**Allocations.** The class has carried [MemoryDiagnoser] all along, so the
figure was collected on every run and then dropped, because Program.cs asked
for Mean, Error and StdDev and nothing else. Now it asks for Allocated too.
The column header is exactly "Allocated" -- verified by running
BenchmarkDotNet and printing the headers, since TableToString skips a header
it cannot find without complaining.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit 253b5a8 into master Aug 9, 2026
25 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the bench/trig-precision branch August 9, 2026 17:58
Rafael-SOWNet added a commit that referenced this pull request Aug 9, 2026
The half of #167 the benchmark itself could not supply. #850 added EvalTrig and
EvalTrigPrecise and stopped the allocation figure being discarded, and the
Kernel Benchmark has now run on master with both, so the numbers come from a
GitHub runner rather than a contributor's machine.

Trigonometry at five hundred digits is 43.3 ms against 1.3 ms at the default
hundred.

Allocation gets a table of its own rather than doubling the width of the main
one. EvalEasy allocating nothing is the proof of the caching note above it
rather than a further argument for it.
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.

1 participant