Skip to content

Use idempotent loads of the bytecode. - #24

Merged
Pavel-Durov merged 1 commit into
ykjit:masterfrom
ltratt:idempotent_load
Jul 28, 2026
Merged

Use idempotent loads of the bytecode.#24
Pavel-Durov merged 1 commit into
ykjit:masterfrom
ltratt:idempotent_load

Conversation

@ltratt

@ltratt ltratt commented Jul 28, 2026

Copy link
Copy Markdown

I've done this fairly differently from the previous approach, which (in the terminology of this PR) only exposed load_ip_uint8_. This commit tries to reduce the number of calls by using load_ip_uint32: that allows common cases (e.g. small integers of 8 bytes) to be done without further idempotent calls. The hope -- and benchmarking vs. the previous approach seems to confirm this -- is that it reduces the overhead when we're interpreting.

I experimented with several designs: loading 64 bits and decoding integers that way but some mp instructions can be >8 bytes, and it seems that the sweet spot is either "is 2 or 3 bytes" or "is 9 or 10 bytes". So that was slower than the 32 bit approach (and, really, we could get away with 24 bits in this commit, as we never use more than 3 bytes).

There are a couple of gotchas with this commit: it bakes in a little endian assumption; it requires architectures that allow unaligned 32-bit reads; and we have to allow at least 3 bytes padding after the last instruction.

In terms of performance, this hugely speeds up some benchmarks, but it does slow some down. Despite the latter, this commit (relative to the preceeding commit) seems to clearly be heading in the right direction:

BigLoop/ykmp/1000000000  2759692 ±  146   227746 ±  976   0.08  91.75% faster
Sieve/ykmp/3000           178133 ±  228    19497 ±   59   0.11  89.05% faster
Mandelbrot/ykmp/500       128149 ±  843    44276 ± 1156   0.35  65.45% faster
List/ykmp/1500            134336 ±  149    62093 ±  626   0.46  53.78% faster
NBody/ykmp/250000         191769 ± 5127   112451 ±   92   0.59  41.36% faster
Queens/ykmp/1000           84899 ±  598    70252 ±  557   0.83  17.25% faster
CD/ykmp/250               186601 ±  627   209803 ± 1146   1.12  12.43% slower
Permute/ykmp/1000         115033 ±  710   133177 ±  171   1.16  15.77% slower
Richards/ykmp/100         305566 ± 1433   355874 ±  361   1.16  16.46% slower
Bounce/ykmp/1500           89773 ±  264   122221 ±  811   1.36  36.14% slower
Towers/ykmp/600           103125 ±  555   153793 ±  312   1.49  49.13% slower
Storage/ykmp/1000          90828 ±  318   112431 ±31069   1.24  indistinguishable

I've done this fairly differently from the previous approach, which
(in the terminology of this PR) only exposed `load_ip_uint8_`. This
commit tries to reduce the number of calls by using `load_ip_uint32`:
that allows common cases (e.g. small integers of 8 bytes) to be done
without further idempotent calls. The hope -- and benchmarking vs. the
previous approach seems to confirm this -- is that it reduces the
overhead when we're interpreting.

I experimented with several designs: loading 64 bits and decoding
integers that way but some mp instructions can be >8 bytes, and it
seems that the sweet spot is either "is 2 or 3 bytes" or "is 9 or 10
bytes". So that was slower than the 32 bit approach (and, really, we
could get away with 24 bits in this commit, as we never use more than 3
bytes).

There are a couple of gotchas with this commit: it bakes in a little
endian assumption; it requires architectures that allow unaligned 32-bit
reads; and we have to allow at least 3 bytes padding after the last
instruction.

In terms of performance, this hugely speeds up some benchmarks, but it
does slow some down. Despite the latter, this commit (relative to the
preceeding commit) seems to clearly be heading in the right direction:

```
BigLoop/ykmp/1000000000  2759692 ±  146   227746 ±  976   0.08  91.75% faster
Sieve/ykmp/3000           178133 ±  228    19497 ±   59   0.11  89.05% faster
Mandelbrot/ykmp/500       128149 ±  843    44276 ± 1156   0.35  65.45% faster
List/ykmp/1500            134336 ±  149    62093 ±  626   0.46  53.78% faster
NBody/ykmp/250000         191769 ± 5127   112451 ±   92   0.59  41.36% faster
Queens/ykmp/1000           84899 ±  598    70252 ±  557   0.83  17.25% faster
CD/ykmp/250               186601 ±  627   209803 ± 1146   1.12  12.43% slower
Permute/ykmp/1000         115033 ±  710   133177 ±  171   1.16  15.77% slower
Richards/ykmp/100         305566 ± 1433   355874 ±  361   1.16  16.46% slower
Bounce/ykmp/1500           89773 ±  264   122221 ±  811   1.36  36.14% slower
Towers/ykmp/600           103125 ±  555   153793 ±  312   1.49  49.13% slower
Storage/ykmp/1000          90828 ±  318   112431 ±31069   1.24  indistinguishable
```
@Pavel-Durov
Pavel-Durov added this pull request to the merge queue Jul 28, 2026
Merged via the queue into ykjit:master with commit 5313211 Jul 28, 2026
2 checks passed
@ltratt
ltratt deleted the idempotent_load branch July 29, 2026 16:11
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.

2 participants