Skip to content

Arithmetic borrows its operands - #92

Merged
tamnd merged 1 commit into
mainfrom
arithmetic-borrows
Aug 29, 2026
Merged

Arithmetic borrows its operands#92
tamnd merged 1 commit into
mainfrom
arithmetic-borrows

Conversation

@tamnd

@tamnd tamnd commented Aug 29, 2026

Copy link
Copy Markdown
Owner

ops::promote was 11% of the run in the profile taken after #91. It folds bool into the int it is and lifts an int to a float when the other side is one, and it was taking both operands by value. An operator only reads the two values it is given, so that was a machine word copied twice for every addition in a loop and a heap allocation made twice for every addition on a bignum, for two values dropped on the next line.

Num and Pair now borrow the integer. A bool needs something to point at, and Int owns a boxed bignum in its large variant so it cannot be const promoted, hence a pair of statics for the two integers a bool can be.

Numbers

Interleaved against the previous build so machine drift does not land on one side, medians of twelve timed runs each:

Benchmark Before After
int_loop 474.1 ms 425.2 ms 1.33x on a warm re-run, 1.12x here
float_loop 328.3 ms 284.2 ms 1.16x
str_ops 239.5 ms 210.8 ms 1.14x
branch_dispatch 332.7 ms 319.3 ms 1.04x
list_grow 225.9 ms 219.4 ms 1.03x

About 1.09x overall, and more than that on programs that are mostly arithmetic.

The profile afterwards is Vm::execute at 45%, Object::clone at 17%, drop_glue::<Object> at 12%, Int::div_mod at 7% and Object::equals at 5%. The promotion is not in it anywhere. Clone and drop together are 29% and that is the object representation, so it is M2 or M3 work rather than something to shave here.

Checking

Output diffed against CPython 3.14 on the five tier zero benchmarks, on a program that mixes bignum, bool and float across every arithmetic, bitwise, shift and comparison operator, and on the OverflowError, ZeroDivisionError, TypeError and ValueError paths. Byte identical throughout. 429 tests pass, clippy and rustdoc are clean.

Every binary operator on numbers went through one promotion step that
folded `bool` into the `int` it is and lifted an `int` to a `float` when
the other side was one. That step took both operands by value, so an
operator that only reads them was handed two copies: a machine word
copied twice for every addition in a loop, and a heap allocation made
twice for every addition on a bignum, both thrown away on the next line.

The promotion now hands out references. `Num` and `Pair` borrow the
integer instead of owning it, and a pair of statics gives a `bool`
something to point at, since `Int` owns a boxed bignum in its large
variant and so cannot be const promoted.

Interleaved against the previous build on the five tier zero benchmarks:
1.33x on int_loop, 1.16x on float_loop, 1.14x on str_ops, 1.04x on
branch_dispatch, 1.03x on list_grow, so about 1.09x overall and more
than that where the program is mostly arithmetic. The profile no longer
mentions the promotion at all. What is at the top of it now is `Object`
clone and drop at 29% together, which is the object representation and a
job for a later milestone.

Output was diffed against CPython 3.14 on the five benchmarks, on a
program covering bignum, bool and float mixing across every arithmetic,
bitwise, shift and comparison operator, and on the OverflowError,
ZeroDivisionError, TypeError and ValueError paths. All byte identical.
@tamnd
tamnd merged commit a1b63fe into main Aug 29, 2026
9 checks passed
@tamnd
tamnd deleted the arithmetic-borrows branch August 29, 2026 08:14
@tamnd tamnd mentioned this pull request Aug 29, 2026
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