Skip to content

YJIT: GC and recompile all code pages #6406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 25, 2022
Merged

Conversation

k0kubun
Copy link
Member

@k0kubun k0kubun commented Sep 20, 2022

This PR implements code GC for YJIT. The design is as follows:

  • Free as many ISEQs as possible.
    • One idea to approach code GC is to free only JIT code for GCed ISEQs. Another idea is to free less frequently used JIT code, like MJIT. But freeing and recompiling all ISEQs should typically give a better peak performance because code after GC will not interleave any code for initialization. So we chose that design.
    • This could trigger too many GCs when --yjit-exec-mem-size is too small, but the benchmark numbers were not bad for such cases.
  • Not incremental.
    • When it runs out of entire --yjit-exec-mem-size, it starts marking on-stack ISEQs, and then frees all pages that have no on-stack ISEQs.
    • When it never reaches --yjit-exec-mem-size, it could leave unneeded code forever. To address that, we could incrementally mark an unused ISEQ when the ISEQ is GCed, and then free pages when all of their ISEQs are GCed. However, we've found that very few ISEQs are GCed on railsbench YJIT: Avoid creating payloads for non-JITed ISEQs #6549, so there's little benefit in doing so. Moreover, by not doing it incrementally, you can completely avoid the overhead of code GC when it doesn't reach the max size. Alternatively, we could also add API to manually trigger code GC, and call it after an application boot.

Benchmark

I measured code GC's performance on railsbench with various --yjit-exec-mem-sizes. --yjit-exec-mem-size=6 doesn't trigger GC while 1~5 do, so only 1~6 are tested.

On railsbench, without code GC, YJIT uses 335 code pages (8~16Kib * 335 = 2.61~5.23MiB). With enough --yjit-exec-mem-size and code GC, YJIT leaves only 146~180 code pages (8~16KiB * 146~180 = 1.14~2.81MiB).

Measured on Linux. A memory page is 4KiB on Linux, so a YJIT code page (default: 16KiB) has 2 memory pages for inline code, and 2 memory pages for outlined code. Because YJIT goes to the next code page when it runs out of space for inline code or outlined code, code pages except for the current page should be using 2~4 memory pages (8~16KiB).
Mem (MiB) master code GC (this PR)
Time (ms) YJIT ratio (%) Time (ms) YJIT ratio (%) before/after GC count Used pages Freed pages
1 1596.0 0.1 1623.7 0.3 0.98 21 64 0
2 4459.5 5.4 1112.3 2.6 4.01 13 128 0
3 2081.4 25.9 1105.2 88.9 1.88 1 157 35
4 1130.0 74.1 1117.1 89.3 1.01 1 147 109
5 1132.6 88.5 1121.8 89.3 1.01 1 168 152
6 1118.7 88.6 1125.6 89.0 0.99 0 331 0

Logs: --enable-yjit for benchmarking the speed

--yjit-exec-mem-size=1
./run_benchmarks.rb railsbench -e before1::/home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size=1
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size\=1 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1610ms
itr #2: 1575ms
itr #3: 1604ms
itr #4: 1578ms
itr #5: 1591ms
itr #6: 1577ms
itr #7: 1571ms
itr #8: 1599ms
itr #9: 1547ms
itr #10: 1599ms
itr #11: 1599ms
itr #12: 1573ms
itr #13: 1599ms
itr #14: 1574ms
itr #15: 1576ms
itr #16: 1604ms
itr #17: 1572ms
itr #18: 1573ms
itr #19: 1599ms
itr #20: 1569ms
itr #21: 1571ms
itr #22: 1569ms
itr #23: 1601ms
itr #24: 1572ms
itr #25: 1576ms
Average of last 10, non-warmup iters: 1581ms
Total time spent benchmarking: 41s

end_time: 2022-10-24 13:57:30 PDT (-0700)
before1: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before1 (ms)  stddev (%)
railsbench  1581.0        0.9
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after1::/home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size=1
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size\=1 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 2021ms
itr #2: 1621ms
itr #3: 1654ms
itr #4: 1593ms
itr #5: 1657ms
itr #6: 1623ms
itr #7: 1649ms
itr #8: 1622ms
itr #9: 1621ms
itr #10: 1651ms
itr #11: 1653ms
itr #12: 1655ms
itr #13: 1623ms
itr #14: 1586ms
itr #15: 1650ms
itr #16: 1648ms
itr #17: 1590ms
itr #18: 1652ms
itr #19: 1624ms
itr #20: 1649ms
itr #21: 1651ms
itr #22: 1653ms
itr #23: 1651ms
itr #24: 1621ms
itr #25: 1618ms
Average of last 10, non-warmup iters: 1636ms
Total time spent benchmarking: 43s

end_time: 2022-10-24 13:58:13 PDT (-0700)
after1: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after1 (ms)  stddev (%)
railsbench  1636.3       1.2
----------  -----------  ----------
--yjit-exec-mem-size=2
./run_benchmarks.rb railsbench -e before2::/home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size=2
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size\=2 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 4072ms
itr #2: 4192ms
itr #3: 4339ms
itr #4: 4337ms
itr #5: 4374ms
itr #6: 4362ms
itr #7: 4368ms
itr #8: 4382ms
itr #9: 4416ms
itr #10: 4387ms
itr #11: 4422ms
itr #12: 4361ms
itr #13: 4432ms
itr #14: 4370ms
itr #15: 4432ms
itr #16: 4406ms
itr #17: 4401ms
itr #18: 4406ms
itr #19: 4408ms
itr #20: 4409ms
itr #21: 4405ms
itr #22: 4437ms
itr #23: 4380ms
itr #24: 4437ms
itr #25: 4414ms
Average of last 10, non-warmup iters: 4410ms
Total time spent benchmarking: 111s

end_time: 2022-10-24 14:00:05 PDT (-0700)
before2: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before2 (ms)  stddev (%)
railsbench  4410.7        0.4
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after2::/home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size=2
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size\=2 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1353ms
itr #2: 1099ms
itr #3: 1099ms
itr #4: 1100ms
itr #5: 1132ms
itr #6: 1099ms
itr #7: 1098ms
itr #8: 1099ms
itr #9: 1132ms
itr #10: 1099ms
itr #11: 1099ms
itr #12: 1099ms
itr #13: 1132ms
itr #14: 1100ms
itr #15: 1132ms
itr #16: 1097ms
itr #17: 1131ms
itr #18: 1096ms
itr #19: 1098ms
itr #20: 1097ms
itr #21: 1131ms
itr #22: 1099ms
itr #23: 1098ms
itr #24: 1100ms
itr #25: 1132ms
Average of last 10, non-warmup iters: 1108ms
Total time spent benchmarking: 30s

end_time: 2022-10-24 14:00:35 PDT (-0700)
after2: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after2 (ms)  stddev (%)
railsbench  1108.6       1.4
----------  -----------  ----------
--yjit-exec-mem-size=3
./run_benchmarks.rb railsbench -e before3::/home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size=3
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size\=3 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1879ms
itr #2: 1831ms
itr #3: 1884ms
itr #4: 1897ms
itr #5: 1873ms
itr #6: 1912ms
itr #7: 1867ms
itr #8: 1953ms
itr #9: 1886ms
itr #10: 1955ms
itr #11: 1929ms
itr #12: 1966ms
itr #13: 1931ms
itr #14: 1967ms
itr #15: 1937ms
itr #16: 1966ms
itr #17: 1934ms
itr #18: 1903ms
itr #19: 1971ms
itr #20: 1971ms
itr #21: 1897ms
itr #22: 1969ms
itr #23: 1934ms
itr #24: 1938ms
itr #25: 1946ms
Average of last 10, non-warmup iters: 1943ms
Total time spent benchmarking: 50s

end_time: 2022-10-24 14:01:25 PDT (-0700)
before3: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before3 (ms)  stddev (%)
railsbench  1943.5        1.3
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after3::/home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size=3
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size\=3 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1289ms
itr #2: 1096ms
itr #3: 1092ms
itr #4: 1126ms
itr #5: 1124ms
itr #6: 1094ms
itr #7: 1058ms
itr #8: 1128ms
itr #9: 1094ms
itr #10: 1094ms
itr #11: 1156ms
itr #12: 1094ms
itr #13: 1063ms
itr #14: 1125ms
itr #15: 1095ms
itr #16: 1124ms
itr #17: 1094ms
itr #18: 1090ms
itr #19: 1091ms
itr #20: 1125ms
itr #21: 1092ms
itr #22: 1093ms
itr #23: 1058ms
itr #24: 1124ms
itr #25: 1091ms
Average of last 10, non-warmup iters: 1098ms
Total time spent benchmarking: 29s

end_time: 2022-10-24 14:01:55 PDT (-0700)
after3: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after3 (ms)  stddev (%)
railsbench  1098.7       1.8
----------  -----------  ----------
--yjit-exec-mem-size=4
./run_benchmarks.rb railsbench -e before4::/home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size=4
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size\=4 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1248ms
itr #2: 1107ms
itr #3: 1142ms
itr #4: 1073ms
itr #5: 1140ms
itr #6: 1108ms
itr #7: 1103ms
itr #8: 1138ms
itr #9: 1108ms
itr #10: 1107ms
itr #11: 1141ms
itr #12: 1101ms
itr #13: 1108ms
itr #14: 1104ms
itr #15: 1071ms
itr #16: 1137ms
itr #17: 1108ms
itr #18: 1143ms
itr #19: 1072ms
itr #20: 1143ms
itr #21: 1138ms
itr #22: 1107ms
itr #23: 1106ms
itr #24: 1108ms
itr #25: 1140ms
Average of last 10, non-warmup iters: 1120ms
Total time spent benchmarking: 30s

end_time: 2022-10-24 14:02:25 PDT (-0700)
before4: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before4 (ms)  stddev (%)
railsbench  1120.8        2.0
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after4::/home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size=4
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size\=4 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1229ms
itr #2: 1153ms
itr #3: 1114ms
itr #4: 1153ms
itr #5: 1117ms
itr #6: 1118ms
itr #7: 1153ms
itr #8: 1082ms
itr #9: 1185ms
itr #10: 1116ms
itr #11: 1118ms
itr #12: 1151ms
itr #13: 1152ms
itr #14: 1116ms
itr #15: 1117ms
itr #16: 1118ms
itr #17: 1115ms
itr #18: 1117ms
itr #19: 1149ms
itr #20: 1154ms
itr #21: 1117ms
itr #22: 1153ms
itr #23: 1118ms
itr #24: 1121ms
itr #25: 1155ms
Average of last 10, non-warmup iters: 1132ms
Total time spent benchmarking: 30s

end_time: 2022-10-24 14:02:56 PDT (-0700)
after4: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after4 (ms)  stddev (%)
railsbench  1132.3       1.5
----------  -----------  ----------
--yjit-exec-mem-size=5
./run_benchmarks.rb railsbench -e before5::/home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size=5
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size\=5 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1251ms
itr #2: 1076ms
itr #3: 1138ms
itr #4: 1110ms
itr #5: 1137ms
itr #6: 1104ms
itr #7: 1103ms
itr #8: 1103ms
itr #9: 1109ms
itr #10: 1141ms
itr #11: 1113ms
itr #12: 1105ms
itr #13: 1109ms
itr #14: 1106ms
itr #15: 1105ms
itr #16: 1107ms
itr #17: 1138ms
itr #18: 1077ms
itr #19: 1139ms
itr #20: 1140ms
itr #21: 1105ms
itr #22: 1076ms
itr #23: 1109ms
itr #24: 1140ms
itr #25: 1107ms
Average of last 10, non-warmup iters: 1114ms
Total time spent benchmarking: 29s

end_time: 2022-10-24 14:03:26 PDT (-0700)
before5: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before5 (ms)  stddev (%)
railsbench  1114.3        2.1
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after5::/home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size=5
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size\=5 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1254ms
itr #2: 1110ms
itr #3: 1113ms
itr #4: 1112ms
itr #5: 1146ms
itr #6: 1110ms
itr #7: 1112ms
itr #8: 1142ms
itr #9: 1113ms
itr #10: 1111ms
itr #11: 1147ms
itr #12: 1114ms
itr #13: 1112ms
itr #14: 1114ms
itr #15: 1113ms
itr #16: 1115ms
itr #17: 1145ms
itr #18: 1113ms
itr #19: 1112ms
itr #20: 1114ms
itr #21: 1114ms
itr #22: 1146ms
itr #23: 1114ms
itr #24: 1111ms
itr #25: 1115ms
Average of last 10, non-warmup iters: 1120ms
Total time spent benchmarking: 30s

end_time: 2022-10-24 14:03:56 PDT (-0700)
after5: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after5 (ms)  stddev (%)
railsbench  1120.5       1.1
----------  -----------  ----------
--yjit-exec-mem-size=6
./run_benchmarks.rb railsbench -e before6::/home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size=6
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-before/bin/ruby --yjit-exec-mem-size\=6 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1246ms
itr #2: 1071ms
itr #3: 1139ms
itr #4: 1104ms
itr #5: 1107ms
itr #6: 1139ms
itr #7: 1104ms
itr #8: 1104ms
itr #9: 1104ms
itr #10: 1103ms
itr #11: 1107ms
itr #12: 1104ms
itr #13: 1139ms
itr #14: 1069ms
itr #15: 1101ms
itr #16: 1137ms
itr #17: 1137ms
itr #18: 1103ms
itr #19: 1105ms
itr #20: 1142ms
itr #21: 1070ms
itr #22: 1139ms
itr #23: 1071ms
itr #24: 1138ms
itr #25: 1139ms
Average of last 10, non-warmup iters: 1118ms
Total time spent benchmarking: 29s

end_time: 2022-10-24 15:23:54 PDT (-0700)
before6: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before6 (ms)  stddev (%)
railsbench  1118.7        2.5
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after6::/home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size=6
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-release-after/bin/ruby --yjit-exec-mem-size\=6 -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 1261ms
itr #2: 1113ms
itr #3: 1118ms
itr #4: 1117ms
itr #5: 1118ms
itr #6: 1120ms
itr #7: 1146ms
itr #8: 1117ms
itr #9: 1116ms
itr #10: 1119ms
itr #11: 1151ms
itr #12: 1118ms
itr #13: 1119ms
itr #14: 1116ms
itr #15: 1154ms
itr #16: 1118ms
itr #17: 1119ms
itr #18: 1118ms
itr #19: 1153ms
itr #20: 1120ms
itr #21: 1116ms
itr #22: 1120ms
itr #23: 1116ms
itr #24: 1119ms
itr #25: 1154ms
Average of last 10, non-warmup iters: 1125ms
Total time spent benchmarking: 30s

end_time: 2022-10-24 15:21:27 PDT (-0700)
after6: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after6 (ms)  stddev (%)
railsbench  1125.6       1.3
----------  -----------  ----------

Logs: --enable-yjit=dev for stats

--yjit-exec-mem-size=1
./run_benchmarks.rb railsbench -e before1::/home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size=1 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size\=1 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 3431ms
itr #2: 3335ms
itr #3: 3341ms
itr #4: 3342ms
itr #5: 3354ms
itr #6: 3327ms
itr #7: 3363ms
itr #8: 3317ms
itr #9: 3335ms
itr #10: 3282ms
itr #11: 3324ms
itr #12: 3305ms
itr #13: 3309ms
itr #14: 3351ms
itr #15: 3314ms
itr #16: 3375ms
itr #17: 3324ms
itr #18: 3338ms
itr #19: 3345ms
itr #20: 3352ms
itr #21: 3332ms
itr #22: 3345ms
itr #23: 3348ms
itr #24: 3385ms
itr #25: 3320ms
Average of last 10, non-warmup iters: 3347ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg       1102 (60.2%)
      iseq_complex_callee        453 (24.7%)
                 kw_splat         99 ( 5.4%)
      args_splat_non_iseq         83 ( 4.5%)
    cfunc_ruby_array_varg         75 ( 4.1%)
         iseq_arity_error          7 ( 0.4%)
              send_getter          5 ( 0.3%)
            zsuper_method          5 ( 0.3%)
    optimized_method_call          3 ( 0.2%)
invokesuper exit reasons:
         block         68 (90.7%)
    me_changed          7 ( 9.3%)
leave exit reasons:
        interp_return      30622 (99.5%)
    start_pc_non_zero        163 ( 0.5%)
         se_interrupt          4 ( 0.0%)
getblockparamproxy exit reasons:
    (all relevant counters are zero)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
    rhs_too_small         14 (100.0%)
opt_getinlinecache exit reasons:
    miss          1 (100.0%)
invalidation reasons:
    constant_state_bump        139 (55.4%)
          method_lookup         80 (31.9%)
       constant_ic_fill         32 (12.7%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:         3951
compiled_iseq_count:          479
compiled_block_count:        4838
freed_iseq_count:              48
invalidation_count:           251
constant_state_bumps:           0
inline_code_size:         1478290
outlined_code_size:       1361791
num_gc_obj_refs:             6565
total_exit_count:           41291
total_insns_count:     1669437111
vm_insns_count:        1668560512
yjit_insns_count:          887268
ratio_in_yjit:               0.1%
avg_len_in_yjit:             21.2
Top-20 most frequent exit ops (100.0% of exits):
               invokeblock:       4078 (38.2%)
          from_branch_stub:       1796 (16.8%)
               invokesuper:       1656 (15.5%)
                      send:       1241 (11.6%)
    opt_send_without_block:       1228 (11.5%)
                checkmatch:        177 (1.7%)
               objtostring:        138 (1.3%)
             setlocal_WC_0:        135 (1.3%)
                  opt_aref:         74 (0.7%)
      opt_getconstant_path:         54 (0.5%)
                     throw:         39 (0.4%)
               getconstant:         20 (0.2%)
               expandarray:         14 (0.1%)
                      once:         14 (0.1%)
                     leave:          4 (0.0%)
                opt_length:          1 (0.0%)
                       nop:          0 (0.0%)
                  getlocal:          0 (0.0%)
                  setlocal:          0 (0.0%)
             getblockparam:          0 (0.0%)
Total time spent benchmarking: 87s

end_time: 2022-10-24 14:11:11 PDT (-0700)
before1: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before1 (ms)  stddev (%)
railsbench  3347.0        0.6
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after1::/home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size=1 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size\=1 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 8061ms
itr #2: 3264ms
itr #3: 3309ms
itr #4: 3282ms
itr #5: 3286ms
itr #6: 3395ms
itr #7: 3324ms
itr #8: 3276ms
itr #9: 3281ms
itr #10: 3320ms
itr #11: 3283ms
itr #12: 3257ms
itr #13: 3341ms
itr #14: 3273ms
itr #15: 3240ms
itr #16: 3310ms
itr #17: 3284ms
itr #18: 3250ms
itr #19: 3313ms
itr #20: 3314ms
itr #21: 3346ms
itr #22: 3275ms
itr #23: 3234ms
itr #24: 3338ms
itr #25: 3265ms
Average of last 10, non-warmup iters: 3293ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
    optimized_method_call      10324 (38.9%)
                block_arg       7930 (29.9%)
      iseq_complex_callee       4463 (16.8%)
              iseq_zsuper       1729 ( 6.5%)
           refined_method        853 ( 3.2%)
    cfunc_ruby_array_varg        527 ( 2.0%)
                 kw_splat        436 ( 1.6%)
         args_splat_cfunc        132 ( 0.5%)
      args_splat_non_iseq        123 ( 0.5%)
      iseq_ruby2_keywords         12 ( 0.0%)
            zsuper_method          8 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
invokesuper exit reasons:
         block        358 (72.3%)
    me_changed        137 (27.7%)
leave exit reasons:
        interp_return     140604 (99.2%)
    start_pc_non_zero       1100 ( 0.8%)
         se_interrupt          8 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified        185 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat        132 (90.4%)
    rhs_too_small         14 ( 9.6%)
opt_getinlinecache exit reasons:
    miss          3 (100.0%)
invalidation reasons:
          method_lookup        336 (61.2%)
    constant_state_bump        151 (27.5%)
       constant_ic_fill         62 (11.3%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:         1379
compiled_block_count:       44719
compiled_iseq_count:         2007
compiled_page_count:           64
freed_iseq_count:              61
freed_page_count:               0
invalidation_count:           549
constant_state_bumps:           0
inline_code_size:          517122
outlined_code_size:        515871
freed_code_size:                0
code_gc_count:                 21
num_gc_obj_refs:            35534
total_exit_count:          186184
total_insns_count:     1670228337
vm_insns_count:        1666017334
yjit_insns_count:         4256583
ratio_in_yjit:               0.3%
avg_len_in_yjit:             22.6
Top-20 most frequent exit ops (100.0% of exits):
    opt_send_without_block:      22046 (48.4%)
                      send:       8393 (18.4%)
               invokeblock:       8094 (17.8%)
               invokesuper:       4551 (10.0%)
             setlocal_WC_0:        359 (0.8%)
               objtostring:        345 (0.8%)
        getblockparamproxy:        333 (0.7%)
               getconstant:        333 (0.7%)
                  opt_aref:        287 (0.6%)
                     throw:        241 (0.5%)
                checkmatch:        193 (0.4%)
               expandarray:        146 (0.3%)
                      once:         75 (0.2%)
      opt_getconstant_path:         69 (0.2%)
                 opt_nil_p:         37 (0.1%)
          opt_newarray_max:         29 (0.1%)
          from_branch_stub:         19 (0.0%)
             setblockparam:         15 (0.0%)
                     leave:          8 (0.0%)
                    opt_eq:          5 (0.0%)
Total time spent benchmarking: 92s

end_time: 2022-10-24 14:12:44 PDT (-0700)
after1: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after1 (ms)  stddev (%)
railsbench  3293.4       1.1
----------  -----------  ----------
--yjit-exec-mem-size=2
./run_benchmarks.rb railsbench -e before2::/home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size=2 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size\=2 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 37537ms
itr #2: 37623ms
itr #3: 37788ms
itr #4: 37886ms
itr #5: 37926ms
itr #6: 37985ms
itr #7: 38034ms
itr #8: 38033ms
itr #9: 38127ms
itr #10: 38144ms
itr #11: 38227ms
itr #12: 38268ms
itr #13: 38264ms
itr #14: 38270ms
itr #15: 38336ms
itr #16: 38375ms
itr #17: 38386ms
itr #18: 38420ms
itr #19: 38429ms
itr #20: 38422ms
itr #21: 38353ms
itr #22: 38447ms
itr #23: 38458ms
itr #24: 38488ms
itr #25: 38596ms
Average of last 10, non-warmup iters: 38437ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
    optimized_method_call     250095 (82.2%)
                block_arg      51661 (17.0%)
      iseq_complex_callee       1198 ( 0.4%)
           refined_method        611 ( 0.2%)
    cfunc_ruby_array_varg        435 ( 0.1%)
                 kw_splat        131 ( 0.0%)
      args_splat_non_iseq        122 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
         args_splat_cfunc          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
              iseq_zsuper          1 ( 0.0%)
invokesuper exit reasons:
         block        172 (85.1%)
    me_changed         30 (14.9%)
leave exit reasons:
        interp_return    7140023 (100.0%)
    start_pc_non_zero        422 ( 0.0%)
         se_interrupt         15 ( 0.0%)
getblockparamproxy exit reasons:
    (all relevant counters are zero)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
    rhs_too_small         14 (100.0%)
opt_getinlinecache exit reasons:
    miss          1 (100.0%)
invalidation reasons:
          method_lookup        182 (42.3%)
    constant_state_bump        179 (41.6%)
       constant_ic_fill         69 (16.0%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:      5421071
compiled_iseq_count:          917
compiled_block_count:        9494
freed_iseq_count:              61
invalidation_count:           430
constant_state_bumps:           0
inline_code_size:       349238964
outlined_code_size:     526032849
num_gc_obj_refs:          3158591
total_exit_count:        12874711
total_insns_count:     1684587660
vm_insns_count:        1593555683
yjit_insns_count:        96766665
ratio_in_yjit:               5.4%
avg_len_in_yjit:              7.1
Top-20 most frequent exit ops (100.0% of exits):
          from_branch_stub:    5419317 (94.5%)
    opt_send_without_block:     255651 (4.5%)
                      send:      51814 (0.9%)
               invokeblock:       4755 (0.1%)
               invokesuper:       1925 (0.0%)
             setlocal_WC_0:        305 (0.0%)
                checkmatch:        193 (0.0%)
        getblockparamproxy:        148 (0.0%)
      opt_getconstant_path:        139 (0.0%)
               objtostring:        139 (0.0%)
                  opt_aref:         81 (0.0%)
                     throw:         66 (0.0%)
                 opt_nil_p:         30 (0.0%)
          opt_newarray_max:         29 (0.0%)
                      once:         23 (0.0%)
               getconstant:         20 (0.0%)
             setblockparam:         15 (0.0%)
                     leave:         15 (0.0%)
               expandarray:         14 (0.0%)
                    opt_eq:          5 (0.0%)
Total time spent benchmarking: 959s

end_time: 2022-10-24 14:28:43 PDT (-0700)
before2: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before2 (ms)  stddev (%)
railsbench  38437.8       0.2
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after2::/home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size=2 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size\=2 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 10751ms
itr #2: 3284ms
itr #3: 3293ms
itr #4: 3303ms
itr #5: 3314ms
itr #6: 3314ms
itr #7: 3303ms
itr #8: 3295ms
itr #9: 3299ms
itr #10: 3298ms
itr #11: 3279ms
itr #12: 3301ms
itr #13: 3301ms
itr #14: 3298ms
itr #15: 3334ms
itr #16: 3266ms
itr #17: 3338ms
itr #18: 3294ms
itr #19: 3290ms
itr #20: 3321ms
itr #21: 3293ms
itr #22: 3301ms
itr #23: 3299ms
itr #24: 3325ms
itr #25: 3290ms
Average of last 10, non-warmup iters: 3302ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg     171804 (42.2%)
    optimized_method_call     112394 (27.6%)
      iseq_complex_callee      80749 (19.8%)
              iseq_zsuper      23293 ( 5.7%)
         args_splat_cfunc       8095 ( 2.0%)
                 kw_splat       6950 ( 1.7%)
      args_splat_non_iseq       1256 ( 0.3%)
      iseq_ruby2_keywords       1157 ( 0.3%)
           refined_method        854 ( 0.2%)
    cfunc_ruby_array_varg        548 ( 0.1%)
                 keywords         91 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block       2291 (65.3%)
    me_changed       1219 (34.7%)
leave exit reasons:
        interp_return     979907 (98.4%)
    start_pc_non_zero      16105 ( 1.6%)
         se_interrupt         19 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified        279 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat       5855 (99.8%)
    rhs_too_small         14 ( 0.2%)
opt_getinlinecache exit reasons:
    miss          4 (100.0%)
invalidation reasons:
          method_lookup        436 (60.1%)
    constant_state_bump        206 (28.4%)
       constant_ic_fill         83 (11.4%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:         1323
compiled_block_count:       62233
compiled_iseq_count:         2589
compiled_page_count:          128
freed_iseq_count:              61
freed_page_count:               0
invalidation_count:           725
constant_state_bumps:           0
inline_code_size:         1052563
outlined_code_size:       1039622
freed_code_size:                0
code_gc_count:                 13
num_gc_obj_refs:            49926
total_exit_count:         1489275
total_insns_count:     1678526091
vm_insns_count:        1634133903
yjit_insns_count:        44901556
ratio_in_yjit:               2.6%
avg_len_in_yjit:             29.8
Top-20 most frequent exit ops (100.0% of exits):
    opt_send_without_block:     186852 (36.7%)
                      send:     176218 (34.6%)
               invokesuper:      61090 (12.0%)
               invokeblock:      48196 (9.5%)
               getconstant:       9923 (1.9%)
                  opt_aref:       8321 (1.6%)
                     throw:       6279 (1.2%)
               expandarray:       5869 (1.2%)
             setlocal_WC_0:       4903 (1.0%)
        getblockparamproxy:        427 (0.1%)
      opt_getconstant_path:        387 (0.1%)
                 opt_nil_p:        320 (0.1%)
               objtostring:        217 (0.0%)
                checkmatch:        193 (0.0%)
                      once:         80 (0.0%)
          opt_newarray_max:         29 (0.0%)
                     leave:         19 (0.0%)
             setblockparam:         14 (0.0%)
          from_branch_stub:         13 (0.0%)
               defineclass:          5 (0.0%)
Total time spent benchmarking: 95s

end_time: 2022-10-24 14:30:19 PDT (-0700)
after2: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after2 (ms)  stddev (%)
railsbench  3302.4       0.6
----------  -----------  ----------
--yjit-exec-mem-size=3
./run_benchmarks.rb railsbench -e before3::/home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size=3 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size\=3 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 39644ms
itr #2: 39707ms
itr #3: 39851ms
itr #4: 39886ms
itr #5: 39946ms
itr #6: 40037ms
itr #7: 40076ms
itr #8: 40071ms
itr #9: 40150ms
itr #10: 40177ms
itr #11: 40190ms
itr #12: 40301ms
itr #13: 40210ms
itr #14: 40282ms
itr #15: 40241ms
itr #16: 40365ms
itr #17: 40312ms
itr #18: 40303ms
itr #19: 40320ms
itr #20: 40418ms
itr #21: 40466ms
itr #22: 40339ms
itr #23: 40370ms
itr #24: 40456ms
itr #25: 40518ms
Average of last 10, non-warmup iters: 40387ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
    optimized_method_call    3106940 (77.4%)
                block_arg     849965 (21.2%)
      iseq_complex_callee      53319 ( 1.3%)
           refined_method        854 ( 0.0%)
    cfunc_ruby_array_varg        437 ( 0.0%)
                 kw_splat        138 ( 0.0%)
      args_splat_non_iseq        128 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
         args_splat_cfunc          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
              iseq_zsuper          1 ( 0.0%)
invokesuper exit reasons:
         block        180 (71.7%)
    me_changed         71 (28.3%)
leave exit reasons:
        interp_return   27870228 (96.7%)
    start_pc_non_zero     958876 ( 3.3%)
         se_interrupt         31 ( 0.0%)
getblockparamproxy exit reasons:
    (all relevant counters are zero)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
    rhs_too_small         14 (100.0%)
opt_getinlinecache exit reasons:
    miss          6 (100.0%)
invalidation reasons:
          method_lookup        231 (45.3%)
    constant_state_bump        190 (37.3%)
       constant_ic_fill         89 (17.5%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:      4664185
compiled_iseq_count:         1448
compiled_block_count:       14164
freed_iseq_count:              61
invalidation_count:           510
constant_state_bumps:           0
inline_code_size:       577359646
outlined_code_size:     431783005
num_gc_obj_refs:          5479668
total_exit_count:        37097283
total_insns_count:     1772381620
vm_insns_count:        1314203361
yjit_insns_count:       467405314
ratio_in_yjit:              25.9%
avg_len_in_yjit:             12.4
Top-20 most frequent exit ops (100.0% of exits):
          from_branch_stub:    4662928 (50.5%)
    opt_send_without_block:    3166008 (34.3%)
                      send:     850132 (9.2%)
               invokeblock:     544515 (5.9%)
               invokesuper:       2099 (0.0%)
             setlocal_WC_0:        305 (0.0%)
                     throw:        194 (0.0%)
                checkmatch:        193 (0.0%)
        getblockparamproxy:        148 (0.0%)
               objtostring:        139 (0.0%)
      opt_getconstant_path:        104 (0.0%)
                  opt_aref:         78 (0.0%)
               getconstant:         40 (0.0%)
                     leave:         31 (0.0%)
                 opt_nil_p:         30 (0.0%)
          opt_newarray_max:         29 (0.0%)
                      once:         27 (0.0%)
             setblockparam:         15 (0.0%)
               expandarray:         14 (0.0%)
               defineclass:          5 (0.0%)
Total time spent benchmarking: 1010s

end_time: 2022-10-24 14:47:09 PDT (-0700)
before3: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before3 (ms)  stddev (%)
railsbench  40387.2       0.2
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after3::/home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size=3 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size\=3 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 3679ms
itr #2: 1961ms
itr #3: 2013ms
itr #4: 1910ms
itr #5: 2010ms
itr #6: 1914ms
itr #7: 2011ms
itr #8: 2011ms
itr #9: 1915ms
itr #10: 2013ms
itr #11: 1963ms
itr #12: 2010ms
itr #13: 1963ms
itr #14: 1911ms
itr #15: 1968ms
itr #16: 2016ms
itr #17: 1972ms
itr #18: 1935ms
itr #19: 1968ms
itr #20: 1968ms
itr #21: 2014ms
itr #22: 2019ms
itr #23: 1968ms
itr #24: 1966ms
itr #25: 1963ms
Average of last 10, non-warmup iters: 1979ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg    7435306 (43.1%)
    optimized_method_call    4488557 (26.0%)
      iseq_complex_callee    3416144 (19.8%)
              iseq_zsuper    1151797 ( 6.7%)
         args_splat_cfunc     348148 ( 2.0%)
                 kw_splat     314348 ( 1.8%)
      args_splat_non_iseq      50434 ( 0.3%)
      iseq_ruby2_keywords      49996 ( 0.3%)
           refined_method        853 ( 0.0%)
    cfunc_ruby_array_varg        569 ( 0.0%)
                 keywords         92 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block     115186 (69.7%)
    me_changed      50156 (30.3%)
leave exit reasons:
        interp_return   37307911 (96.1%)
    start_pc_non_zero    1528966 ( 3.9%)
         se_interrupt        262 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified         97 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat     249235 (100.0%)
    rhs_too_small         14 ( 0.0%)
opt_getinlinecache exit reasons:
    miss          3 (100.0%)
invalidation reasons:
          method_lookup        452 (60.5%)
    constant_state_bump        210 (28.1%)
       constant_ic_fill         85 (11.4%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:            1
compiled_block_count:       25467
compiled_iseq_count:         2652
compiled_page_count:          157
freed_iseq_count:              61
freed_page_count:              35
invalidation_count:           747
constant_state_bumps:           0
inline_code_size:         1280822
outlined_code_size:       1279562
freed_code_size:           573440
code_gc_count:                  1
num_gc_obj_refs:            21170
total_exit_count:        58383653
total_insns_count:     2057159604
vm_insns_count:         227558645
yjit_insns_count:      1850676701
ratio_in_yjit:              88.9%
avg_len_in_yjit:             31.3
Top-20 most frequent exit ops (100.0% of exits):
                      send:    7618444 (36.1%)
    opt_send_without_block:    7263990 (34.5%)
               invokesuper:    2763928 (13.1%)
               invokeblock:    1903664 (9.0%)
               getconstant:     432847 (2.1%)
                  opt_aref:     350082 (1.7%)
                     throw:     259104 (1.2%)
               expandarray:     249249 (1.2%)
             setlocal_WC_0:     199538 (0.9%)
      opt_getconstant_path:      32597 (0.2%)
               opt_empty_p:        747 (0.0%)
                 opt_nil_p:        349 (0.0%)
               objtostring:        346 (0.0%)
                     leave:        262 (0.0%)
        getblockparamproxy:        246 (0.0%)
                checkmatch:        193 (0.0%)
                      once:         80 (0.0%)
          opt_newarray_max:         29 (0.0%)
             setblockparam:         15 (0.0%)
               defineclass:          5 (0.0%)
Total time spent benchmarking: 56s

end_time: 2022-10-24 14:48:06 PDT (-0700)
after3: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after3 (ms)  stddev (%)
railsbench  1979.3       1.3
----------  -----------  ----------
--yjit-exec-mem-size=4
./run_benchmarks.rb railsbench -e before4::/home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size=4 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size\=4 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 11175ms
itr #2: 10521ms
itr #3: 10531ms
itr #4: 10554ms
itr #5: 10567ms
itr #6: 10658ms
itr #7: 10595ms
itr #8: 10615ms
itr #9: 10552ms
itr #10: 10567ms
itr #11: 10627ms
itr #12: 10632ms
itr #13: 10627ms
itr #14: 10611ms
itr #15: 10570ms
itr #16: 10593ms
itr #17: 10576ms
itr #18: 10649ms
itr #19: 10588ms
itr #20: 10635ms
itr #21: 10652ms
itr #22: 10645ms
itr #23: 10600ms
itr #24: 10618ms
itr #25: 10652ms
Average of last 10, non-warmup iters: 10621ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg    6264866 (44.8%)
    optimized_method_call    4211372 (30.1%)
      iseq_complex_callee    1845575 (13.2%)
              iseq_zsuper    1051399 ( 7.5%)
         args_splat_cfunc     298162 ( 2.1%)
                 kw_splat     213820 ( 1.5%)
      args_splat_non_iseq      50116 ( 0.4%)
      iseq_ruby2_keywords      49996 ( 0.4%)
           refined_method        854 ( 0.0%)
    cfunc_ruby_array_varg        453 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block      65172 (56.6%)
    me_changed      50066 (43.4%)
leave exit reasons:
        interp_return   40913799 (95.8%)
    start_pc_non_zero    1805771 ( 4.2%)
         se_interrupt        188 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified         97 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat     249239 (100.0%)
    rhs_too_small         14 ( 0.0%)
opt_getinlinecache exit reasons:
    miss          6 (100.0%)
invalidation reasons:
          method_lookup        231 (45.0%)
    constant_state_bump        190 (37.0%)
       constant_ic_fill         92 (17.9%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:      1404731
compiled_iseq_count:         2122
compiled_block_count:       19332
freed_iseq_count:              61
invalidation_count:           513
constant_state_bumps:           0
inline_code_size:       123756806
outlined_code_size:     104889806
num_gc_obj_refs:           379568
total_exit_count:        59087100
total_insns_count:     1987371656
vm_insns_count:         514553436
yjit_insns_count:      1490991521
ratio_in_yjit:              74.1%
avg_len_in_yjit:             24.9
Top-20 most frequent exit ops (100.0% of exits):
                      send:    6397630 (35.2%)
    opt_send_without_block:    5662566 (31.2%)
               invokesuper:    1913155 (10.5%)
          from_branch_stub:    1404098 (7.7%)
               invokeblock:    1320754 (7.3%)
               getconstant:     432849 (2.4%)
                  opt_aref:     350084 (1.9%)
                     throw:     259104 (1.4%)
               expandarray:     249253 (1.4%)
             setlocal_WC_0:     150296 (0.8%)
      opt_getconstant_path:      32607 (0.2%)
        getblockparamproxy:        245 (0.0%)
                checkmatch:        193 (0.0%)
                     leave:        188 (0.0%)
               objtostring:        143 (0.0%)
                 opt_nil_p:         30 (0.0%)
          opt_newarray_max:         29 (0.0%)
                      once:         27 (0.0%)
             setblockparam:         15 (0.0%)
                opt_length:          6 (0.0%)
Total time spent benchmarking: 270s

end_time: 2022-10-24 14:52:37 PDT (-0700)
before4: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before4 (ms)  stddev (%)
railsbench  10621.1       0.3
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after4::/home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size=4 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size\=4 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 4450ms
itr #2: 1986ms
itr #3: 1974ms
itr #4: 1995ms
itr #5: 2034ms
itr #6: 1961ms
itr #7: 1949ms
itr #8: 1991ms
itr #9: 1947ms
itr #10: 1937ms
itr #11: 1940ms
itr #12: 1939ms
itr #13: 1942ms
itr #14: 1986ms
itr #15: 1889ms
itr #16: 1981ms
itr #17: 1934ms
itr #18: 1935ms
itr #19: 1947ms
itr #20: 1962ms
itr #21: 1938ms
itr #22: 1996ms
itr #23: 1935ms
itr #24: 1943ms
itr #25: 1952ms
Average of last 10, non-warmup iters: 1952ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg    7435000 (43.1%)
    optimized_method_call    4488480 (26.0%)
      iseq_complex_callee    3415817 (19.8%)
              iseq_zsuper    1151760 ( 6.7%)
         args_splat_cfunc     348122 ( 2.0%)
                 kw_splat     314317 ( 1.8%)
      args_splat_non_iseq      50425 ( 0.3%)
      iseq_ruby2_keywords      49992 ( 0.3%)
           refined_method        854 ( 0.0%)
    cfunc_ruby_array_varg        567 ( 0.0%)
                 keywords         85 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block     115143 (69.7%)
    me_changed      50069 (30.3%)
leave exit reasons:
        interp_return   37937171 (97.8%)
    start_pc_non_zero     867825 ( 2.2%)
         se_interrupt        247 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified         97 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat     249235 (100.0%)
    rhs_too_small         14 ( 0.0%)
opt_getinlinecache exit reasons:
    miss          3 (100.0%)
invalidation reasons:
          method_lookup        456 (60.2%)
    constant_state_bump        211 (27.9%)
       constant_ic_fill         90 (11.9%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:            1
compiled_block_count:       29473
compiled_iseq_count:         2653
compiled_page_count:          147
freed_iseq_count:              61
freed_page_count:             109
invalidation_count:           757
constant_state_bumps:           0
inline_code_size:         1196455
outlined_code_size:       1195976
freed_code_size:          1785856
code_gc_count:                  1
num_gc_obj_refs:            24311
total_exit_count:        58979425
total_insns_count:     2052053782
vm_insns_count:         219331996
yjit_insns_count:      1853764040
ratio_in_yjit:              89.3%
avg_len_in_yjit:             31.1
Top-20 most frequent exit ops (100.0% of exits):
                      send:    7618104 (36.2%)
    opt_send_without_block:    7263804 (34.5%)
               invokesuper:    2763624 (13.1%)
               invokeblock:    1903534 (9.0%)
               getconstant:     432830 (2.1%)
                  opt_aref:     350080 (1.7%)
                     throw:     259098 (1.2%)
               expandarray:     249249 (1.2%)
             setlocal_WC_0:     199522 (0.9%)
               opt_empty_p:        752 (0.0%)
                 opt_nil_p:        355 (0.0%)
               objtostring:        350 (0.0%)
        getblockparamproxy:        248 (0.0%)
                     leave:        247 (0.0%)
                checkmatch:        193 (0.0%)
      opt_getconstant_path:        104 (0.0%)
                      once:         80 (0.0%)
          opt_newarray_max:         29 (0.0%)
             setblockparam:         15 (0.0%)
                  branchif:          6 (0.0%)
Total time spent benchmarking: 56s

end_time: 2022-10-24 14:53:34 PDT (-0700)
after4: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after4 (ms)  stddev (%)
railsbench  1952.9       1.0
----------  -----------  ----------
--yjit-exec-mem-size=5
./run_benchmarks.rb railsbench -e before5::/home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size=5 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size\=5 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 3702ms
itr #2: 2059ms
itr #3: 2069ms
itr #4: 2054ms
itr #5: 2048ms
itr #6: 2006ms
itr #7: 2058ms
itr #8: 2012ms
itr #9: 2015ms
itr #10: 2011ms
itr #11: 2013ms
itr #12: 2010ms
itr #13: 2065ms
itr #14: 2015ms
itr #15: 2072ms
itr #16: 2065ms
itr #17: 2015ms
itr #18: 2071ms
itr #19: 2069ms
itr #20: 2063ms
itr #21: 2019ms
itr #22: 2015ms
itr #23: 2064ms
itr #24: 2021ms
itr #25: 2063ms
Average of last 10, non-warmup iters: 2046ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg    7435339 (43.1%)
    optimized_method_call    4488519 (26.0%)
      iseq_complex_callee    3416085 (19.8%)
              iseq_zsuper    1151798 ( 6.7%)
         args_splat_cfunc     348150 ( 2.0%)
                 kw_splat     314218 ( 1.8%)
      args_splat_non_iseq      50433 ( 0.3%)
      iseq_ruby2_keywords      49996 ( 0.3%)
           refined_method        854 ( 0.0%)
    cfunc_ruby_array_varg        453 ( 0.0%)
                 keywords         92 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block     115161 (69.7%)
    me_changed      50066 (30.3%)
leave exit reasons:
        interp_return   36187324 (95.8%)
    start_pc_non_zero    1576977 ( 4.2%)
         se_interrupt        217 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified         97 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat     249240 (100.0%)
    rhs_too_small         14 ( 0.0%)
opt_getinlinecache exit reasons:
    miss          6 (100.0%)
invalidation reasons:
          method_lookup        231 (44.7%)
    constant_state_bump        190 (36.8%)
       constant_ic_fill         96 (18.6%)
bindings_allocations:         154
bindings_set:                   0
compiled_iseq_count:         2666
compiled_block_count:       23626
freed_iseq_count:              61
invalidation_count:           517
constant_state_bumps:           0
inline_code_size:         5096031
outlined_code_size:       5104071
num_gc_obj_refs:            19240
total_exit_count:        57007128
total_insns_count:     2060960027
vm_insns_count:         237819823
yjit_insns_count:      1843960008
ratio_in_yjit:              88.5%
avg_len_in_yjit:             32.0
Top-20 most frequent exit ops (100.0% of exits):
                      send:    7618133 (36.6%)
    opt_send_without_block:    7260083 (34.9%)
               invokesuper:    2763830 (13.3%)
               invokeblock:    1652634 (7.9%)
               getconstant:     432849 (2.1%)
                  opt_aref:     350098 (1.7%)
                     throw:     259104 (1.2%)
               expandarray:     249254 (1.2%)
             setlocal_WC_0:     199536 (1.0%)
      opt_getconstant_path:      32611 (0.2%)
               opt_empty_p:        753 (0.0%)
        getblockparamproxy:        245 (0.0%)
                     leave:        217 (0.0%)
                checkmatch:        193 (0.0%)
               objtostring:        141 (0.0%)
                 opt_nil_p:         30 (0.0%)
          opt_newarray_max:         29 (0.0%)
                      once:         27 (0.0%)
             setblockparam:         15 (0.0%)
               defineclass:          5 (0.0%)
Total time spent benchmarking: 57s

end_time: 2022-10-24 14:54:32 PDT (-0700)
before5: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before5 (ms)  stddev (%)
railsbench  2046.9        1.2
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after5::/home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size=5 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size\=5 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 5306ms
itr #2: 1927ms
itr #3: 1888ms
itr #4: 1931ms
itr #5: 1977ms
itr #6: 1928ms
itr #7: 1981ms
itr #8: 2034ms
itr #9: 1983ms
itr #10: 1950ms
itr #11: 1937ms
itr #12: 1978ms
itr #13: 1971ms
itr #14: 1929ms
itr #15: 1979ms
itr #16: 1987ms
itr #17: 1929ms
itr #18: 1980ms
itr #19: 1931ms
itr #20: 1988ms
itr #21: 1978ms
itr #22: 1942ms
itr #23: 1991ms
itr #24: 1931ms
itr #25: 1982ms
Average of last 10, non-warmup iters: 1964ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg    7434087 (43.1%)
    optimized_method_call    4488461 (26.0%)
      iseq_complex_callee    3415692 (19.8%)
              iseq_zsuper    1151742 ( 6.7%)
         args_splat_cfunc     348120 ( 2.0%)
                 kw_splat     314300 ( 1.8%)
      args_splat_non_iseq      50421 ( 0.3%)
      iseq_ruby2_keywords      49992 ( 0.3%)
           refined_method        854 ( 0.0%)
    cfunc_ruby_array_varg        569 ( 0.0%)
                 keywords         92 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block     115140 (69.7%)
    me_changed      50070 (30.3%)
leave exit reasons:
        interp_return   37924452 (97.7%)
    start_pc_non_zero     876553 ( 2.3%)
         se_interrupt        268 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified        188 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat     249231 (100.0%)
    rhs_too_small         14 ( 0.0%)
opt_getinlinecache exit reasons:
    miss          3 (100.0%)
invalidation reasons:
          method_lookup        456 (60.1%)
    constant_state_bump        211 (27.8%)
       constant_ic_fill         92 (12.1%)
bindings_allocations:         154
bindings_set:                   0
compilation_failure:            1
compiled_block_count:       33956
compiled_iseq_count:         2655
compiled_page_count:          168
freed_iseq_count:              61
freed_page_count:             152
invalidation_count:           759
constant_state_bumps:           0
inline_code_size:         1368395
outlined_code_size:       1367865
freed_code_size:          2490368
code_gc_count:                  1
num_gc_obj_refs:            27836
total_exit_count:        58966698
total_insns_count:     2050028883
vm_insns_count:         219442277
yjit_insns_count:      1851628852
ratio_in_yjit:              89.3%
avg_len_in_yjit:             31.0
Top-20 most frequent exit ops (100.0% of exits):
                      send:    7617201 (36.2%)
    opt_send_without_block:    7263718 (34.5%)
               invokesuper:    2763571 (13.1%)
               invokeblock:    1903526 (9.0%)
               getconstant:     432830 (2.1%)
                  opt_aref:     350096 (1.7%)
                     throw:     259095 (1.2%)
               expandarray:     249245 (1.2%)
             setlocal_WC_0:     199522 (0.9%)
        getblockparamproxy:       1079 (0.0%)
               opt_empty_p:        736 (0.0%)
                 opt_nil_p:        355 (0.0%)
               objtostring:        345 (0.0%)
      opt_getconstant_path:        304 (0.0%)
                     leave:        268 (0.0%)
                checkmatch:        193 (0.0%)
                      once:         80 (0.0%)
          opt_newarray_max:         29 (0.0%)
             setblockparam:         15 (0.0%)
                  branchif:          8 (0.0%)
Total time spent benchmarking: 57s

end_time: 2022-10-24 14:55:29 PDT (-0700)
after5: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after5 (ms)  stddev (%)
railsbench  1964.6       1.3
----------  -----------  ----------
--yjit-exec-mem-size=6
./run_benchmarks.rb railsbench -e before6::/home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size=6 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-before/bin/ruby --yjit-exec-mem-size\=6 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 3752ms
itr #2: 2004ms
itr #3: 2009ms
itr #4: 2057ms
itr #5: 2064ms
itr #6: 2064ms
itr #7: 1970ms
itr #8: 2004ms
itr #9: 2011ms
itr #10: 2057ms
itr #11: 2010ms
itr #12: 2006ms
itr #13: 2060ms
itr #14: 2007ms
itr #15: 2052ms
itr #16: 2005ms
itr #17: 2115ms
itr #18: 2067ms
itr #19: 2060ms
itr #20: 2007ms
itr #21: 2008ms
itr #22: 2003ms
itr #23: 2003ms
itr #24: 2068ms
itr #25: 2010ms
Average of last 10, non-warmup iters: 2035ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg    7435355 (43.1%)
    optimized_method_call    4488519 (26.0%)
      iseq_complex_callee    3416098 (19.8%)
              iseq_zsuper    1151797 ( 6.7%)
         args_splat_cfunc     348148 ( 2.0%)
                 kw_splat     314213 ( 1.8%)
      args_splat_non_iseq      50434 ( 0.3%)
      iseq_ruby2_keywords      49996 ( 0.3%)
           refined_method        854 ( 0.0%)
    cfunc_ruby_array_varg        453 ( 0.0%)
                 keywords         92 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block     115160 (69.7%)
    me_changed      50066 (30.3%)
leave exit reasons:
        interp_return   36187235 (95.8%)
    start_pc_non_zero    1576974 ( 4.2%)
         se_interrupt        249 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified         97 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat     249237 (100.0%)
    rhs_too_small         14 ( 0.0%)
opt_getinlinecache exit reasons:
    miss          6 (100.0%)
invalidation reasons:
          method_lookup        231 (44.7%)
    constant_state_bump        190 (36.8%)
       constant_ic_fill         96 (18.6%)
bindings_allocations:         154
bindings_set:                   0
compiled_iseq_count:         2666
compiled_block_count:       23639
freed_iseq_count:              61
invalidation_count:           517
constant_state_bumps:           0
inline_code_size:         5098551
outlined_code_size:       5105656
num_gc_obj_refs:            19262
total_exit_count:        57007101
total_insns_count:     2064476599
vm_insns_count:         234964097
yjit_insns_count:      1850332368
ratio_in_yjit:              88.6%
avg_len_in_yjit:             32.1
Top-20 most frequent exit ops (100.0% of exits):
                      send:    7618147 (36.6%)
    opt_send_without_block:    7260099 (34.9%)
               invokesuper:    2763833 (13.3%)
               invokeblock:    1652633 (7.9%)
               getconstant:     432849 (2.1%)
                  opt_aref:     350090 (1.7%)
                     throw:     259104 (1.2%)
               expandarray:     249251 (1.2%)
             setlocal_WC_0:     199537 (1.0%)
      opt_getconstant_path:      32611 (0.2%)
               opt_empty_p:        752 (0.0%)
                     leave:        249 (0.0%)
        getblockparamproxy:        245 (0.0%)
                checkmatch:        193 (0.0%)
               objtostring:        142 (0.0%)
                 opt_nil_p:         30 (0.0%)
          opt_newarray_max:         29 (0.0%)
                      once:         27 (0.0%)
             setblockparam:         15 (0.0%)
               defineclass:          5 (0.0%)
Total time spent benchmarking: 57s

end_time: 2022-10-24 14:56:27 PDT (-0700)
before6: ruby 3.2.0dev (2022-10-24T17:54:01Z master b652dbf63b) +YJIT [x86_64-linux]

----------  ------------  ----------
bench       before6 (ms)  stddev (%)
railsbench  2035.2        1.9
----------  ------------  ----------
./run_benchmarks.rb railsbench -e after6::/home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size=6 --yjit-stats
Running benchmark "railsbench" (1/1)
setarch x86_64 -R taskset -c 7 /home/k0kubun/.rbenv/versions/yjit-after/bin/ruby --yjit-exec-mem-size\=6 --yjit-stats -I ./harness benchmarks/railsbench/benchmark.rb
ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]
Command: bundle check 2> /dev/null || bundle install
The Gemfile's dependencies are satisfied
Command: bin/rails db:migrate db:seed
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Deleted all 100 posts
Creating 100 posts....................................................................................................
itr #1: 3686ms
itr #2: 1943ms
itr #3: 1945ms
itr #4: 1945ms
itr #5: 1947ms
itr #6: 1950ms
itr #7: 1950ms
itr #8: 1950ms
itr #9: 1952ms
itr #10: 1946ms
itr #11: 2006ms
itr #12: 1942ms
itr #13: 1950ms
itr #14: 2006ms
itr #15: 1942ms
itr #16: 1957ms
itr #17: 1952ms
itr #18: 1950ms
itr #19: 2003ms
itr #20: 1958ms
itr #21: 1946ms
itr #22: 1955ms
itr #23: 2006ms
itr #24: 1956ms
itr #25: 1957ms
Average of last 10, non-warmup iters: 1964ms
***YJIT: Printing YJIT statistics on exit***
method call exit reasons:
                block_arg    7385351 (42.9%)
    optimized_method_call    4488574 (26.1%)
      iseq_complex_callee    3416159 (19.9%)
              iseq_zsuper    1151797 ( 6.7%)
         args_splat_cfunc     348153 ( 2.0%)
                 kw_splat     314353 ( 1.8%)
      args_splat_non_iseq      50433 ( 0.3%)
      iseq_ruby2_keywords      49996 ( 0.3%)
           refined_method        854 ( 0.0%)
    cfunc_ruby_array_varg        569 ( 0.0%)
                 keywords         92 ( 0.0%)
         iseq_arity_error          7 ( 0.0%)
              send_getter          5 ( 0.0%)
            zsuper_method          5 ( 0.0%)
invokesuper exit reasons:
         block     115158 (69.7%)
    me_changed      50075 (30.3%)
leave exit reasons:
        interp_return   36962066 (95.9%)
    start_pc_non_zero    1577089 ( 4.1%)
         se_interrupt        226 ( 0.0%)
getblockparamproxy exit reasons:
    block_param_modified         97 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons:
    (all relevant counters are zero)
expandarray exit reasons:
            splat     249240 (100.0%)
    rhs_too_small         14 ( 0.0%)
opt_getinlinecache exit reasons:
    miss          3 (100.0%)
invalidation reasons:
          method_lookup        456 (60.1%)
    constant_state_bump        211 (27.8%)
       constant_ic_fill         92 (12.1%)
bindings_allocations:         154
bindings_set:                   0
compiled_block_count:       24722
compiled_iseq_count:         2655
compiled_page_count:          331
freed_iseq_count:              61
freed_page_count:               0
invalidation_count:           759
constant_state_bumps:           0
inline_code_size:         2701992
outlined_code_size:       2701662
freed_code_size:                0
code_gc_count:                  0
num_gc_obj_refs:            20698
total_exit_count:        58037988
total_insns_count:     2063166630
vm_insns_count:         227182057
yjit_insns_count:      1857060495
ratio_in_yjit:              89.0%
avg_len_in_yjit:             31.6
Top-20 most frequent exit ops (100.0% of exits):
                      send:    7568491 (35.9%)
    opt_send_without_block:    7264209 (34.5%)
               invokesuper:    2763835 (13.1%)
               invokeblock:    1903686 (9.0%)
               getconstant:     432848 (2.1%)
                  opt_aref:     350086 (1.7%)
                     throw:     259104 (1.2%)
               expandarray:     249254 (1.2%)
             setlocal_WC_0:     199538 (0.9%)
        getblockparamproxy:      50239 (0.2%)
      opt_getconstant_path:      32604 (0.2%)
               opt_empty_p:        747 (0.0%)
                 opt_nil_p:        355 (0.0%)
               objtostring:        349 (0.0%)
                     leave:        226 (0.0%)
                checkmatch:        193 (0.0%)
                      once:         80 (0.0%)
          opt_newarray_max:         29 (0.0%)
             setblockparam:         15 (0.0%)
               defineclass:          5 (0.0%)
Total time spent benchmarking: 56s

end_time: 2022-10-24 14:57:23 PDT (-0700)
after6: ruby 3.2.0dev (2022-10-24T20:46:43Z yjit-code-gc f31f389855) +YJIT [x86_64-linux]

----------  -----------  ----------
bench       after6 (ms)  stddev (%)
railsbench  1964.4       1.0
----------  -----------  ----------

@maximecb
Copy link
Contributor

One idea might be to use MADV_DONTNEED on dev builds and MADV_FREE for release builds.

I think in general I would avoid having two different implementation paths when we don't have data to back up the idea that having these two separate behaviors yields perf improvements. It's best to minimize complexity and avoid unforeseen strange bugs.

Given the above information, you always need to give a page-aligned address to those system calls. One simple way to achieve it is to only free code pages when an entire page is marked unused.

I agree with that 👍 It might also mean that there's an advantage in having smaller code pages wrt memory usage. We will need to benchmark it.

Because we always start a basic block with a 64-byte address (cb.align_pos(64)), I think we can assume that a single cacheline may not have code for different ISEQs.

This is unfortunately not quite true. We set the alignment to 64 bytes in gen_entry_prologue... Which is an old "optimization" that I put in place but that may not actually improve perf 😅 However, when we do JIT-to-JIT ISEQ-to-ISEQ calls we pay no attention to alignment.

I think your idea of "painting" pages incrementally when generating code and when freeing ISEQs is fairly clever. What I was originally thinking is that we would have a bitmap that is essentially one bit per page. We would zero out all the bits and then iterate through all the live ISEQs to mark the live ISEQs. We have access to all of the YJIT blocks for all the ISEQs so we can map from ISEQ->blocks->address ranges->pages. Maybe we could start with that? Unless you can think of an incremental marking scheme that can work with arbitrary address ranges for ISEQs.

We also discussed the idea to mix inline and outlined code regions. This PR doesn't work on that project, but I believe those projects are kind of independent and could be worked on concurrently. I assume it can be done before or after this change with relative ease.

We may want to think about that fairly early? At least convince ourselves that it will work with our design.

We'll need to split each code page into an inline and outlined half so that the outlined code is always close to the inlined code.

I think that in terms of marking, we basically don't care about outlined code. We can assume that the code in the outlined half belongs to the same ISEQs that are in the inlined half. Therefore, if it's safe to discard the inlined code, it should be safe to discard the outlined code.

@maximecb maximecb requested a review from XrXr September 20, 2022 15:10
@k0kubun
Copy link
Member Author

k0kubun commented Sep 21, 2022

I think in general I would avoid having two different implementation paths when we don't have data to back up the idea that having these two separate behaviors yields perf improvements. It's best to minimize complexity and avoid unforeseen strange bugs.

Makes sense. I prefer just using MADV_DONTNEED for Linux then. MADV_FREE doesn't exist in older environments, so it would force us to maintain two paths.

However, when we do JIT-to-JIT ISEQ-to-ISEQ calls we pay no attention to alignment.

Got it. It was a too optimistic assumption 🙂 If we currently pack different ISEQs in such calls, adding alignment there may have an unwanted impact. Let's avoid designs that need this assumption.

What I was originally thinking is that we would have a bitmap that is essentially one bit per page. We would zero out all the bits and then iterate through all the live ISEQs to mark the live ISEQs. We have access to all of the YJIT blocks for all the ISEQs so we can map from ISEQ->blocks->address ranges->pages. Maybe we could start with that?

That sounds like the simplest solution we could have now. I think we need to newly introduce a list of ISEQs that have been JITed in the past, but given that we're targeting only a few thousands of ISEQs, it's probably fine in terms of both time and space complexity.

Unless you can think of an incremental marking scheme that can work with arbitrary address ranges for ISEQs.

I do think of an incremental marking scheme that works without the alignment assumption though. If we have [u8; N] or [u16; N] where each element maintains the number of ISEQs that overlap a single page, we could do it incrementally. Because each page requires only 1 or 2 bytes, it requires an even smaller size for maintaining it than my first idea.

For this to work, whenever we write new code for an ISEQ, we should be able to know the ISEQ's current set of address ranges and its new one in order to increment only elements for newly modified pages. Being able to know all the code addresses of an ISEQ is needed for a non-incremental design as well, so virtually we have no blocker for taking this design.

Anyway, I'm planning on implementing your non-incremental design first because it's simpler and I think it's better to have a working first version as early as possible and incrementally improve it.

At least convince ourselves that it will work with our design.

It's kind of related to the "only free code pages when an entire page is marked unused" idea. No matter what layout we choose for each page or over multiple pages, we'll need to be able to identify whether a particular page(s) is in use or not. We can make the page usage tracking either fully isolated or tightly integrated with something to manage a code page, but even for the latter case, the core logic of the memory page management would stay the same when the layout is changed.

I'm not saying that we should do that early though. I'm rather suggesting that we could work on them at the same time especially when we have multiple people working on this project. I just prioritized solving the primary goal anyway because I have the above belief.

@maximecb
Copy link
Contributor

maximecb commented Sep 22, 2022

That sounds like the simplest solution we could have now. I think we need to newly introduce a list of ISEQs that have been JITed in the past, but given that we're targeting only a few thousands of ISEQs, it's probably fine in terms of both time and space complexity.

IIRC SFR is on the order of 10K to 20K ISEQs.

Does CRuby have a straightforward way of iterating through all the ISEQs? If so, we could use that. If not, then I guess we have to add a list/set of JITted ISEQs as you suggest.

For a given ISEQ, we can check if there exist block versions in YJIT: https://github.com/ruby/ruby/blob/master/yjit/src/core.rs#L686

And this function gets called when marking an ISEQ:
https://github.com/ruby/ruby/blob/master/yjit/src/core.rs#L559

I do think of an incremental marking scheme that works without the alignment assumption though. If we have [u8; N] or [u16; N] where each element maintains the number of ISEQs that overlap a single page, we could do it incrementally. Because each page requires only 1 or 2 bytes, it requires an even smaller size for maintaining it than my first idea.

IIUC what you are suggesting here is using one integer per page representing the number of ISEQs that overlap on that page? If so then I guess this would be reference counting. Presumably that would work. We might still need to reconstruct the refcount from time to time if we ever decide to throw away all the code because we've reached the limit though.

Anyway, I'm planning on implementing your non-incremental design first because it's simpler and I think it's better to have a working first version as early as possible and incrementally improve it.

Alrighty 👍 :)

No matter what layout we choose for each page or over multiple pages, we'll need to be able to identify whether a particular page(s) is in use or not.

One challenging point is that we don't really track address ranges for outlined code blocks right now. If we split each page between inline and outlined halves, we can guarantee that all of the outlined code for an ISEQ will live in the same page as its inline code. Without that design, then we need to track the outlined address ranges. Maybe not the hardest thing, but something to think about. We do also want to solve the problem of making inline and outlined code closer together (within the range of conditional branches on arm).

I'm rather suggesting that we could work on them at the same time especially when we have multiple people working on this project. I just prioritized solving the primary goal anyway because I have the above belief.

I apologize because I do realize that it's hard to coordinate on this project async and there are a lot of different elements to keep track of.

@k0kubun
Copy link
Member Author

k0kubun commented Sep 26, 2022

IIRC SFR is on the order of 10K to 20K ISEQs.

📝

Does CRuby have a straightforward way of iterating through all the ISEQs?

I currently don't know of such a thing. Iterating through all objects might work if every ISeq has a Ruby object, but I'm not sure if it's efficient enough. At least MJIT has been doing that by itself for years and nobody has pointed out an alternative.

If so then I guess this would be reference counting.
We might still need to reconstruct the refcount from time to time if we ever decide to throw away all the code because we've reached the limit though.

Exactly.

One challenging point is that we don't really track address ranges for outlined code blocks right now. If we split each page between inline and outlined halves, we can guarantee that all of the outlined code for an ISEQ will live in the same page as its inline code. Without that design, then we need to track the outlined address ranges.

For that to be useful, we'd need to assume only memory pages used by inline code are used by outlined code, i.e. outlined code shouldn't grow faster than inline code, or otherwise we'll need extra tracking. It would probably hold if you make each page a half-and-half layout, but once you start thinking about optimizing it like 80% inline and 20% outlined, again, you'll need something.

However, it makes sense to keep thinking about the relationship between those projects to potentially simplify the whole design.

I apologize because I do realize that it's hard to coordinate on this project async and there are a lot of different elements to keep track of.

Thank you for bearing with me being in a different timezone. Let me know if there's anything I can help with to improve our async communication.

@jeremyevans
Copy link
Contributor

Does CRuby have a straightforward way of iterating through all the ISEQs?

rb_objspace_each_objects combined with rb_obj_is_iseq. See rb_iseq_trace_set_all/trace_set_i in iseq.c

@k0kubun
Copy link
Member Author

k0kubun commented Sep 26, 2022

Ah right, I've completely forgotten that we need to do that thing for making every insn a trace_ insn. It's probably efficient enough to be used for it too then. Thank you!

@k0kubun
Copy link
Member Author

k0kubun commented Sep 26, 2022

I pushed my latest code at 08c83fb. Before marking this ready, it worked for a simple synthetic workload locally. I plan to work on including YJIT entry code in inline code addresses and using rb_objspace_each_objects as Jeremy suggested.

While working on this, I realized it's probably useful to change the code layout first because:

  • If we assert inline code size >= outlined code size, you could skip tracking outlined code addresses.
  • This implementation triggers code GC for each CodeBlock separately, but it makes more sense to GC both at once. It could be more naturally implemented if you work on that first.

So, yeah, I'll work on that first 😅

@maximecb
Copy link
Contributor

For that to be useful, we'd need to assume only memory pages used by inline code are used by outlined code, i.e. outlined code shouldn't grow faster than inline code, or otherwise we'll need extra tracking. It would probably hold if you make each page a half-and-half layout, but once you start thinking about optimizing it like 80% inline and 20% outlined, again, you'll need something.

Well, I was thinking, we split the pages half and half. There is a current page that we're generating code into, and when we fill either the inline or outlined half, we switch to the next page, to maintain the invariant that we're always putting the outlined code corresponding with some inline code in the same page. It has the potential to be somewhat wasteful but it should work?

Before marking this ready, it worked for a simple synthetic workload locally. I plan to work on including YJIT entry code in inline code addresses and using rb_objspace_each_objects as Jeremy suggested.

Nice. I will read through the code that you wrote so far. I'm going to be conservative in merging code GC because I feel it's very important that we get this piece right. We should test this on railsbench and other larger benchmarks in yjit-bench as well.

While working on this, I realized it's probably useful to change the code layout first because: If we assert inline code size >= outlined code size, you could skip tracking outlined code addresses. This implementation triggers code GC for each CodeBlock separately, but it makes more sense to GC both at once. It could be more naturally implemented if you work on that first.

In my mind it's also, if we're going to modify the design of our code block system, we might as well tackle this problem. Ever since we started talking about arm support months ago, I've been wanting us to improve the way we do code layout.

@maximecb
Copy link
Contributor

I wrote a few comments but I want to say I think you did a good job and your current implementation makes a lot of sense.

I think the main missing piece is the inline/outlined page split, and it would be interesting to have some numbers in terms of how much memory can be freed on railsbench, erubi_rails after initialization but before the first benchmark iteration (and what percentage of the total allocated that is).

@k0kubun
Copy link
Member Author

k0kubun commented Sep 27, 2022

Well, I was thinking, we split the pages half and half. There is a current page that we're generating code into, and when we fill either the inline or outlined half, we switch to the next page, to maintain the invariant that we're always putting the outlined code corresponding with some inline code in the same page. It has the potential to be somewhat wasteful but it should work?

What I meant is that, if we currently don't know any addresses of outlined code, when we fill the outlined half and only outlined code of an ISEQ goes to the next page, there will be no way to detect the ISEQ is using the next page. So, for that case, I thought tracking the outlined address ranges (or outlined code pages) is needed while you seem to assume it's not needed with your half-and-half idea. Managing the relationship between an ISEQ and code pages hopefully doesn't require a lot of space, so it's probably not a big deal though.

I'm going to be conservative in merging code GC because I feel it's very important that we get this piece right. We should test this on railsbench and other larger benchmarks in yjit-bench as well.

Agreed 👍

@maximecb
Copy link
Contributor

So, for that case, I thought tracking the outlined address ranges (or outlined code pages) is needed while you seem to assume it's not needed with your half-and-half idea.

Yeah I think we either need to go with half and half, or we need to track where the outlined code for an iseq lives. Either way though, we want the outlined code to be close to the inline code it's associated with to avoid jumping too far.

Assuming we don't go with the half and half strategy:

If we assume that we have a current inline page that we generate inline code into and a current outlined page that we generate outlined code into... When we fill the current inline or outlined page, we need to move to the next page. We need some kind of mechanism to allocate the next page. In order to keep the in/out code close to each other, we would ideally want the next pages to be allocated to be close to each other.

The problem here is IMO that we're going to run into fragmentation issues once we start to GC code. I guess this may or may not be a problem in practice. If we keep a list of free pages that is sorted by increasing address, it might work out OK because ARM CPUs can jump +/-1 1MiB. That's actually pretty far, so as long as the next inline/outlined pages are within 1MiB (256 x 4KiB pages), we can still do a one-instruction jump.

Managing the relationship between an ISEQ and code pages hopefully doesn't require a lot of space, so it's probably not a big deal though.

It's a bit annoying to do but it is doable. Would have to keep track of all the CodePtr targets of instructions that get passed to the assembler I think. That or record every call to get_side_exit and also scan the dst_addrs field of the Branch struct for stubs and the entry_exit field of the Block struct 🤔

@k0kubun
Copy link
Member Author

k0kubun commented Sep 28, 2022

I've been experimenting with a few designs for the inline/outlined page split. While still WIP, I filed #6460 for sharing the current design.

@k0kubun k0kubun force-pushed the yjit-code-gc branch 2 times, most recently from 620714f to a92e6e7 Compare October 12, 2022 20:46
@k0kubun k0kubun force-pushed the yjit-code-gc branch 6 times, most recently from 2629505 to 2ccc754 Compare October 18, 2022 00:01
@k0kubun k0kubun force-pushed the yjit-code-gc branch 2 times, most recently from 06d894f to 4486eeb Compare October 20, 2022 18:11
@k0kubun k0kubun changed the title YJIT: Prepare for freeing unused code pages YJIT: GC and recompile all code pages Oct 20, 2022
@k0kubun k0kubun force-pushed the yjit-code-gc branch 10 times, most recently from fe2e930 to 976314f Compare October 21, 2022 00:32
@k0kubun k0kubun marked this pull request as ready for review October 21, 2022 04:41
@matzbot matzbot requested a review from a team October 21, 2022 04:42
when it fails to allocate a new page.

Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
Copy link
Contributor

@maximecb maximecb left a comment

Choose a reason for hiding this comment

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

Awesome work! 👏

@k0kubun k0kubun merged commit b7644a2 into ruby:master Oct 25, 2022
@k0kubun k0kubun deleted the yjit-code-gc branch October 25, 2022 16:07
@yasarcelep
Copy link

so long and thanks for all the fish! :)

tenderlove pushed a commit to Shopify/ruby that referenced this pull request Oct 27, 2022
when it fails to allocate a new page.

Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
@igorwwwwwwwwwwwwwwwwwwww

Makes sense. I prefer just using MADV_DONTNEED for Linux then. MADV_FREE doesn't exist in older environments, so it would force us to maintain two paths.

It's worth noting that golang also moved away from MADV_FREE because it makes memory usage harder to reason about. See: golang/go#42330.

@smcgivern
Copy link

I have created https://bugs.ruby-lang.org/issues/19122 to mirror that Go issue and propose that Ruby also stops using MADV_FREE.

@k0kubun
Copy link
Member Author

k0kubun commented Nov 11, 2022

Yeah, I mean, we decided to use only MADV_DONTNEED in this PR, so we're good.

@smcgivern
Copy link

Sorry, I should have clarified: that issue is for the remaining usage of MADV_FREE in fiber_pool_stack_free.

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.

6 participants