Skip to content

Ykify bytecode dispatch loop. - #2

Merged
ltratt merged 1 commit into
ykjit:masterfrom
Pavel-Durov:yk-bytecode-dispatch-loop
Oct 19, 2026
Merged

ltratt merged 1 commit into
ykjit:masterfrom
Pavel-Durov:yk-bytecode-dispatch-loop

Conversation

@Pavel-Durov

@Pavel-Durov Pavel-Durov commented Oct 11, 2026 •

Copy link
Copy Markdown

This is a follow-up from #1.

This change adds a controlpoint to mrb_vm_exec.
The pc is kept in a local and promoted while tracing, the opcode and operands are read through yk_idempotent, so traces see bytecode as constants. The loads are keyed on (pc, iseq generation), bumped when an iseq is freed, so a reused address cannot be used for bytecode load. Locations are created for backward jumps only.

Two workarounds:

  1. The checked-arithmetic builtins are disabled - need to add support in yk.
  2. Sends with more than 6 arguments bypass cipush - need to add support in yk.

AWFY Benchmarks comparing with base mruby

Five benchmarks get faster by 27-53% (Sieve, Queens, Permute, Mandelbrot, List). Seven are slow 6-25% (Bounce, Json, NBody, Richards), 2x (Storage, CD) and 4.3x (Towers). Havlak and DeltaBlue are flat.
The wins are on tight arithmetic loops. The slowdowns come mainly from sends and recursion, which will be addressed in follow-up PRs.

 Benchmark              mruby (ms)  this (ms)  Ratio  Summary
 Sieve/mruby/3000        32304 ±  74    15199 ±  62   0.47  52.95% faster
 Queens/mruby/1000       22621 ±  62    13502 ± 405   0.60  40.31% faster
 Permute/mruby/1000      28034 ±  76    17404 ± 524   0.62  37.92% faster
 Mandelbrot/mruby/500    10015 ±  21     7178 ±   2   0.72  28.32% faster
 List/mruby/1500         23097 ±  49    16843 ± 108   0.73  27.08% faster
 Havlak/mruby/15        164155 ±1095   161715 ± 722   0.99  1.49% faster
 Bounce/mruby/1500       26078 ± 268    27717 ± 105   1.06  6.28% slower
 Json/mruby/100          37389 ± 477    40472 ± 269   1.08  8.25% slower
 NBody/mruby/250000      46373 ±  42    53106 ±  76   1.15  14.52% slower
 Richards/mruby/100      84507 ± 163   105342 ±1808   1.25  24.65% slower
 Storage/mruby/1000      60022 ±  31   118278 ± 182   1.97  97.06% slower
 CD/mruby/250            57098 ± 291   114998 ±2052   2.01  101.40% slower
 Towers/mruby/600        22676 ±  66    97760 ±  90   4.31  331.11% slower
 DeltaBlue/mruby/12000   20626 ± 116    20505 ± 151   0.99  indistinguishable

haste.zip

@ltratt

ltratt commented Oct 11, 2026

Copy link
Copy Markdown

How has this changed performance relative to before this PR?

@Pavel-Durov

Pavel-Durov commented Oct 11, 2026 •

Copy link
Copy Markdown
Author

How has this changed performance relative to before this PR?

relative to master?

@ltratt

ltratt commented Oct 11, 2026

Copy link
Copy Markdown

relative to master?

Yes: in the context of this PR, it's only really useful to know if performance gets better/worse relative to master (relative to mruby is a slightly different metric, more relevant to the long term).

@Pavel-Durov

Copy link
Copy Markdown
Author

The master here is the base mruby. Compared with the compilation config - yk_wordboxing.rb

@ltratt

ltratt commented Oct 11, 2026

Copy link
Copy Markdown

As I said, that's not a useful base: we need to know relative to ykmruby's master branch.

@Pavel-Durov

Copy link
Copy Markdown
Author

Not sure I understand, Its comparing this PR with mruby@2a4167e in mruby
Which is the point of last sync.

Comment thread include/mruby/numeric.h Resolved
Comment thread include/mruby/yk.h Outdated

/* mrb_yk_iseq_gen increased whenever an iseq is freed.
Its used as idempotent key for bytecode fetching in the dispatch loop.*/
extern uint32_t mrb_yk_iseq_gen;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How rapidly does this number tend to increase? Is 32 bits enough?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the program, It hasn't been an issue with AWFY benchmarks, but I agree it should be u64 because a long running process could overflow 32 bits

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 66718c4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's important not to change this blindly: for all I know, this only increments relative to the number of classes in a program at which point 13 (or probably even 16!) bits would be the right number. A quick investigation will sort this out.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The counter increments once per irep free call. This happens when code compiled at runtime (e.g. eval or load) becomes garbage.
AWFY benchmarks barely touch it, but a long-running app that evals heavily could free a lot.
uint64_t costs essentially nothing, so I'd keep it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's an irep free call?

@Pavel-Durov Pavel-Durov Oct 14, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Proc is a basic mruby object (struct RProc), every call frame executes a Proc. The irep is the compiled bytecode that a Proc points to.

When the GC collects a Proc, it drops the Proc reference to its irep. If that was the last reference, the irep is freed through mrb_irep_free. This is what I mean by "irep free call".

In AWFY this doesnt happen while the benchmark runs, even on objectheavy benchmark (Storage, List, Json). The GC collect many Procs, but every irep is still reachable from the program's top-level irep so no count reaches 0. And nothing is dynamically compiled at runtime either.

So we don't really need this counter for AWFY becnchmarks, but we do need it for a general use of long running programs that execute dynamic evals.

mrb_irep_free is what I mean by "irep free call"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically this is the counter that is incremented every time we create (or free?) a "function-esque thing"? If so, I agree it rarely increments, but it's better to use 64 bits.

FWIW, in yklua I recently realised that it's better to increment this on creation rather than deletion (ykjit/yklua#164).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to creation d80f71b

Comment thread include/mruby/yk.h Resolved
@ltratt

ltratt commented Oct 11, 2026

Copy link
Copy Markdown

Can we (at least) split this PR in two? One commit just doing the static noise, then another commit with the bits I actually need to look at?

@Pavel-Durov

Copy link
Copy Markdown
Author

Can we (at least) split this PR in two? One commit just doing the static noise, then another commit with the bits I actually need to look at?

ok will do

@Pavel-Durov

Copy link
Copy Markdown
Author

Can we (at least) split this PR in two? One commit just doing the static noise, then another commit with the bits I actually need to look at?

Done: #3

Comment thread src/vm.c Resolved
Comment thread src/vm.c Resolved
Comment thread src/vm.c Resolved
Comment thread src/yk.c Resolved
Comment thread build_config/yk_wordboxing.rb Resolved
@ltratt

ltratt commented Oct 19, 2026

Copy link
Copy Markdown

Please squash.

Add a control point to mrb_vm_exec. The pc is kept in a local and
promoted while tracing, the opcode and operands are read through
yk_idempotent loads. Locations are created for backward jump only,
@Pavel-Durov
Pavel-Durov force-pushed the yk-bytecode-dispatch-loop branch from f4c54de to f17eedd Compare October 19, 2026 10:52
@Pavel-Durov

Copy link
Copy Markdown
Author

Squashed f17eedd

@ltratt
ltratt merged commit b07f9a2 into ykjit:master Oct 19, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants