Skip to content

Recover 1.42's BOSL2 slowdown: short-circuit assert, faster number formatting - #266

Merged
revarbat merged 3 commits into
mainfrom
perf/assert-and-format
Oct 8, 2026
Merged

revarbat merged 3 commits into
mainfrom
perf/assert-and-format

Conversation

@revarbat

@revarbat revarbat commented Oct 8, 2026

Copy link
Copy Markdown
Member

Evaluator 1.42.0 made BOSL2 evaluation ~5% slower (BOSL2 docs build 8.34T → 8.67T instructions, 1.37 vs 1.44). Traced to 1.42's eager assert() messages and exact six-digit number formatting. This takes it back below 1.41.0.

instructions 1.41.0 main this PR
BOSL2 doc examples (530-example replay) 803.7G 836.1G 800.1G
assert with str() message 1.033G 1.847G 1.054G
str() of numbers 1.455G 1.861G 0.908G
builtin calls 1.845G 1.976G 1.913G
  • assert() short-circuits (maintainer decision): a passing assert evaluates only its condition, like ||. A deliberate divergence from OpenSCAD, which evaluates the message eagerly, so a message with echo()/an undefined name/a warning is silent when the assert passes. Failure output is unchanged; unexpected-argument warnings still print every call (decided once per call site). Interpreter and VM.
  • formatNumber takes std::to_chars' shortest rendering and falls back to the exact path only on a 7-digit tie or a subnormal. Byte-identical: 139M-double fuzz against the old function, 0 mismatches.
  • Builtin calls skip the argument reorder copy when no argument is named.

C++ 1463 pass; bindings 63 pass. New tests cover the short-circuit both ways (passing: silent; failing: side effects + same ERROR), warnings on passing asserts, ties/subnormals.

🤖 Generated with Claude Code

revarbat and others added 3 commits October 7, 2026 17:56
A passing assert() now evaluates its condition and nothing else, the way
`||` skips its right operand -- a deliberate divergence from OpenSCAD,
which evaluates every argument first. BOSL2 validates nearly every call
with assert(cond, str(...)), and building those never-shown messages was
~4% of its evaluation. A failing or condition-less assert evaluates every
other argument after the condition, in source order, so its output is
unchanged; unexpected-argument warnings still print on every call.

The argument layout (condition/message indices, whether anything warns)
is worked out once per compiled call site (AssertLayout), and the VM
compiles the condition, a JumpIfFalse to the failure path, and an
AssertPass op only when there are warnings or chained children to run.

BOSL2 replay sample: 836.1G -> 803.6G instructions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For a normal double the shortest round-trip rendering rounds to 6 digits
exactly as the value does, except when it is itself a 7-digit tie; only
then (and for subnormals) does the exact printf path run. Byte-identical
to the previous output over a fuzz of 139M doubles (random bits, short
decimals and their neighbours, ties at every exponent, integers, powers
of two and ten, subnormals, specials).

fmt.scad micro-benchmark: 1.87G -> 0.91G instructions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…positional

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@revarbat
revarbat merged commit 10d0a23 into main Oct 8, 2026
3 checks passed
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