Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
ZJITでの最適化: Recompile編 / Wakate.rb #7
Search
Nozomi Hijikata
June 23, 2026
Technology
38
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ZJITでの最適化: Recompile編 / Wakate.rb #7
https://wakate-rb.connpass.com/
Nozomi Hijikata
June 23, 2026
Other Decks in Technology
See All in Technology
その Valkey 使いこなしていますか?
onk
PRO
5
1.5k
OptunaHub: ブラックボックス最適化のためのプラットフォーム
pfn
PRO
0
140
AIにシークレットを見せない、WSL2で安全な開発環境
yazm
0
300
実践!Cloud Run ハンズオン!! / Cloud Run handson
y0hgi
2
280
Tofu-Controller lightning talk @ Containerday 2026
gionn
0
110
継続的なAIコスト最適化
bgpat
1
340
⾮構造化データを価値に変える AI エージェント基盤とその組織
ivry_presentationmaterials
0
190
Kiro Crewをはじめよう
kizawa2020
1
160
しろおびから始める!ログ活用の第一歩
shumei_ito
0
950
AI時代のPRレビューにどう向き合うか?
kairim0
1
240
コンテナ運用におけるネットワーク・コンテナコストの削減方法/How to Reduce Network and Container Costs in Container Operations
masakiokuda
1
150
『SAR衛星データ解析入門』のご紹介とその応用
syuchimu
0
120
Featured
See All Featured
First, design no harm
axbom
PRO
2
1.3k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
800
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
600
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1k
Done Done
chrislema
187
17k
A Modern Web Designer's Workflow
chriscoyier
699
190k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
1
280
Visualization
eitanlees
153
17k
Intergalactic Javascript Robots from Outer Space
tanoku
273
28k
Abbi's Birthday
coloredviolet
4
10k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.3k
Transcript
1 ZJIT での最適化: Recompile 編 wakate.rb #7 LT 2026/06/22
1 自己紹介 Nozomi Hijikata (@nozomemein) プロフィール 株式会社Techouse でエンジニアをしています Ruby/Rails 書いたり、Rust
書いたり、モバイルアプリやったり、インフラやったり… 24 卒 / 1 児のパパ 好きなもの Compiler / 最適化 勉強(古代オリエントにはまってます)
1 今日のテーマは
1 Ruby Internal
1 ZJIT
1 軽くおさらい
1
1 今日はZJIT で導入されているRecompile について話します
1 前提 - ZJIT はプロファイルをためてその情報を元に最適化をする e.g. より早い処理で置き換えたり、結果をたたみ込んだり - VM 側からYARV
の命令列をプロファイル用に一時的に差し替える形でプロファイルをする iseq->body->iseq_encoded[insn_idx] = (VALUE)insn_table[zjit_insn]; // Convert a given ISEQ's instructions to zjit_* instructions void rb_zjit_profile_enable(const rb_iseq_t *iseq) { // This table encodes an opcode into the instruction's address const void *const *insn_table = rb_vm_get_insns_address_table(); unsigned int insn_idx = 0; while (insn_idx < iseq->body->iseq_size) { int insn = rb_vm_insn_addr2opcode((void *)iseq->body->iseq_encoded[insn_idx]); int zjit_insn = vm_bare_insn_to_zjit_insn(insn); if (insn != zjit_insn) { } insn_idx += insn_len(insn); } }
1 集めたプロファイルを元にそのパスの最適化度合いを決める → @foo = 4 def test defined?(@foo) end
test;test v10:StringExact|NilClass = DefinedIvar v6, :@foo Initial HIR: fn test@../test.rb:2: bb1(): EntryPoint interpreter v1:BasicObject = LoadSelf Jump bb3(v1) bb2(): EntryPoint JIT(0) v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): CheckInterrupts Return v10 v16:HeapBasicObject = GuardType v6, HeapBasicObject v17:CShape = LoadField v16, :shape_id@0x4 v18:CShape[0x80008] = GuardBitEquals v17, CShape(0x80008) v19:StringExact[VALUE(0x103ba9440)] = Const Value(VALUE(0x103ba9440)) Optimized HIR: fn test@../test.rb:2: bb1(): EntryPoint interpreter v1:BasicObject = LoadSelf Jump bb3(v1) bb2(): EntryPoint JIT(0) v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): CheckInterrupts Return v19 論点から逸れるのでここでは最適化の中身自体は触れませんが、Compile 時の情報を元に結果を畳み込んでいると思ってください
1 これでプロファイルをもとに、 高速なパスに落とし込むことができました
1 めでたしめでたし
1 本当に、 、?
1 もし仮に前提条件を壊すパターンがくるとどうなる? v18:CShape[0x80008] = GuardBitEquals v17, CShape(0x80008) Optimized HIR: fn
test@../test.rb:2: bb1(): EntryPoint interpreter v1:BasicObject = LoadSelf Jump bb3(v1) bb2(): EntryPoint JIT(0) v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): v16:HeapBasicObject = GuardType v6, HeapBasicObject v17:CShape = LoadField v16, :shape_id@0x4 v19:StringExact[VALUE(0x103ba9440)] = Const Value(VALUE(0x103ba9440)) CheckInterrupts Return v19
1 JIT 側ではハンドリングできないので、VM に戻す
1 Side Exit JIT でハンドリングできない場合※ に、VM に処理を戻す 他の処理系だとdeopt(de-optimization) とか言ったりします ※
そもそもJIT でハンドリングできない理由はまちまち (プロファイルとは違う条件での実行、未対応のバイトコード、メソッド再定義、割り込み... )
1 当然のようにSide Exit はやりたくない JIT 側での処理を増やせば増やすほど早くなる(逆も然り) The first rule of
just-in-time compilers is: you stay in JIT code. The second rule of JIT is: you STAY in JIT code! ref: https://railsatscale.com/2026-03-27-using-perfetto-in-zjit/
1 ではどうするか
1 プロファイルをやりなおしたらいいじゃないか
1 Recompile
1 プロファイルを新しく取り直して、Compile をやり直す 1. Side Exit するたびにプロファイルをとる 2. 古いISEQ を無効化し、ISEQ
単位でCompile をやり直す これにより、プロファイルがないケースや、漏れているケースのカバーができるようになる ※ もうちょい細かく分岐があるんですが、ざっくり書くとこんな感じです
1 具体的な例で考えてみる def test = defined?(@a) class C end obj
= C.new obj.instance_variable_set(:@a, 1) 50.times { obj.test } # @a 存在化でプロファイル v18:CShape[0x80008] = GuardBitEquals v17, CShape(0x80008) v19:StringExact[VALUE(0x1017194d0)] = Const Value(VALUE(0x1017194d0)) CheckInterrupts bb1(): EntryPoint interpreter v1:HeapBasicObject = LoadSelf Jump bb3(v1) bb2(): EntryPoint JIT(0) v4:HeapBasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:HeapBasicObject): v17:CShape = LoadField v6, :shape_id@0x4 Return v19 このような単一プロファイルしかないパスをMonomorphic と言ったりします
1 具体的な例で考えてみる2 obj = C.new obj.instance_variable_set(:@b, 1) # @b しか持たないインスタンス
50.times { obj.test } # b はcompile されたJIT コードに存在しないので、Side Exit...!! class C def test = defined?(@a) end obj = C.new obj.instance_variable_set(:@a, 1) 50.times { obj.test } # @a 存在化でプロファイル v18:CShape[0x80008] = GuardBitEquals v17, CShape(0x80008) # ここでSide Exit する bb3(v6:HeapBasicObject): v17:CShape = LoadField v6, :shape_id@0x4 v19:StringExact[VALUE(0x1017194d0)] = Const Value(VALUE(0x1017194d0)) CheckInterrupts Return v19
1 Recompile すると、 、 、
1 具体的な例で考えてみる3 class C def test = defined?(@a) end obj
= C.new obj.instance_variable_set(:@a, 1) 50.times { obj.test } obj = C.new obj.instance_variable_set(:@b, 1) 50.times { obj.test } bb4(v11:StringExact|NilClass): CheckInterrupts Return v11 bb3(v6:HeapBasicObject): v12:CShape = LoadField v6, :shape_id@0x4 v13:CShape[0x80009] = Const CShape(0x80009) v14:CBool = IsBitEqual v12, v13 CondBranch v14, bb5(), bb6() bb5(): v16:NilClass = Const Value(nil) # @b だけのパス Jump bb4(v16) bb6(): v18:CShape = LoadField v6, :shape_id@0x4 v19:CShape[0x80008] = Const CShape(0x80008) v20:CBool = IsBitEqual v18, v19 CondBranch v20, bb7(), bb8() bb7(): v22:StringExact[VALUE(0x1017194d0)] = Const Value(VALUE(0x1017194d0)) Jump bb4(v22) bb8(): v24:StringExact|NilClass = DefinedIvar v6, :@a # Fallback の汎用パス Jump bb4(v24) ※ 生成されるHIR は観測回数に依存して並べられ、今回は同数で新しいものが前になる形
1 いい感じですね
1 実際にこの理屈でRecompile 基盤※ を使って ※Recompile 基盤は k0kubun さんが既に整備してくれていたので、そちらを活用する形をとっています🙏
1 Side Exit をいくらか減らすことができた!!🎉 ref: https://github.com/ruby/ruby/pull/16881
1 時間の都合でだいぶ駆け足& 端折っていますが
1 とっても面白くないですか? 他にももっと面白いテーマがたくさんある...!!! (SSA, (Extended)Basic Block, CSE/LVN, GVN, Constant Folding,
Method Inliner, Reg Alloc…and more!!!)
1 ZJIT に少しでも興味を持ってくれたら嬉しいです
1 自分も隙間を見てコツコツ改善をしていたりします