Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ふつうのWebサービス開発者がRubyKaigiを楽しむためのRubyの知識
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Yudai Tanaka
April 25, 2024
4.4k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ふつうのWebサービス開発者がRubyKaigiを楽しむためのRubyの知識
https://smarthr.connpass.com/event/315394/
Yudai Tanaka
April 25, 2024
More Decks by Yudai Tanaka
See All by Yudai Tanaka
コードレビューやめた
ytnk531
0
520
Refactoring Kataを使ってRubyのリファクタリングを練習する会 - 説明資料
ytnk531
0
220
リモートで生産性を上げるスクラム開発事例
ytnk531
0
610
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
22k
Building Adaptive Systems
keathley
44
3k
What does AI have to do with Human Rights?
axbom
PRO
1
2.2k
Designing for Performance
lara
611
70k
Claude Code のすすめ
schroneko
67
230k
Abbi's Birthday
coloredviolet
2
8k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
320
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
180
WCS-LA-2024
lcolladotor
0
620
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
560
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Transcript
ふつうのWebサービス開発者が RubyKaigiを楽しむためのRubyの知識 2024.4.25 Thu. RubyKaigi 2024事前勉強会@SmartHR 田中 悠大 SmartHR プロダクトエンジニア
SmartHR 田中悠大 (ytnk531, yudai) • 「ふつう」のWebサービス開発者 • Ruby on Railsで開発
• Rubyにコントリビュートしたことない RubyKaigi • 2019年に初参加 • 最高!でも全然わからねぇ… 自己紹介 2
最近のRubyKaigiの議題について広く浅く説明 • なんとなく言ってることわかるかもしれねぇ!を目標 • スライド枚数は48枚です 頑張って備えてたけどわからねぇ…はある • わからねぇ…を楽しもう 今日の話 3
高速化 • JIT • メモリ管理 • 並列処理、並行処理 Rubyをより便利にする • パーサーの拡張
• 静的型付け • debug gem 最近のRubyKaigiの話題 Rubyの裾野を広げる • WASM対応 • 家庭用ゲーム機で動かす 4
高速化 • JIT • メモリ管理 • 並列処理、並行処理 Rubyをより便利にする • パーサーの拡張
• 静的型付け • debug gem 最近のRubyKaigiの話題 Rubyの裾野を広げる • WASM対応 • 家庭用ゲーム機で動かす 5
Rubyプログラム実行の基礎 6
Rubyのコードを実行するためのプログラム • CRuby(MRI) • JRuby • TruffleRuby • mruby •
PicoRuby Rubyの処理系 7
CRubyのプログラム実行の流れ 8 def three 1 + 2 end RubyVM パーサー
コンパイラ ソースコード AST(抽象構文木) Instruction Sequence(ISeq) CRuby
JITコンパイラ 9
JIT プログラム実行中にマシンコードを生成 • 実行時の情報を用いて最適化できる JIT(Just-In-Time)コンパイラとは 10 mov eax, 1 ;
EAXレジスタに整数1をロード add eax, 2 ; EAXレジスタに整数2を加算(EAX = EAX + 2) ret ; 呼び出し元に戻る マシンコード(疑似x86-64アセンブリ) Instruction Sequence 0000 putobject_INT2FIX_1_ 0001 putobject 2 0003 opt_plus 0005 leave ※実際のマシンコードとは異なる
mov eax, 1 add eax, 2 ret ; 呼び出し元に戻る プログラム実行中にマシンコードを生成
• 実行時の情報を用いて最適化できる JIT(Just-In-Time)コンパイラとは 11 JIT Instruction Sequence mov eax, 3 どうせ3なんだから 計算しなくて いいじゃん ※実際のマシンコードとは異なる 0000 putobject_INT2FIX_1_ 0001 putobject 2 0003 opt_plus 0005 leave マシンコード(疑似x86-64アセンブリ) ※実際の挙動とは異なる
MJIT • Ruby 2.6~3.2 YJIT • railsを速くする目的で作られたJITコンパイラ • Ruby 3.1~
RJIT • Rubyで書かれたJITコンパイラ • Ruby 3.3~ RubyのJITコンパイラ 12
性能 13 k0kubun, Ruby JIT Hacking Guide@RubyKaigi 2023より引用
今日の話 14
15
並行処理・並列処理 16
並行処理・並列処理とは 17 引用元: Goでの並行処理を徹底解剖!. https://zenn.dev/hsaki/books/golang-concurrency
カーネルレベルスレッド • OSが管理するスレッド • 生成コストが高い • 複数のCPUコアで並列処理されることがある ユーザーレベルスレッド • プログラムの中で管理するスレッド
• 生成コストが低い • 1つのCPUコアで実行 カーネル/ユーザーレベルスレッド 18
Rubyの並行・並列処理 19 笹田 耕一. Rubyによる 並行並列プログラミング . RubyWorld Conference 2023より引用
カーネルレベルスレッド ユーザーレベルスレッド
FiberがIO待ちになった際の処理を制御できる仕組み Async • I/O待ちになると自動的にFiberを切り替える • Threadクラスより低いコストでコンテキスト切り替えできる Falcon • Asyncを利用したRack互換なサーバー •
サーバーはI/O待ちが多発するため高パフォーマンス Fiber Scheduler(Ruby 3.0〜) 20
1つのプロセスで並列処理するための仕組み • カーネルレベルスレッドを利用 • データ共有に制約を設けてスレッド間の競合を防ぐ M:N スレッドスケジューラ(Ruby 3.3〜) • 大量のRactorを生成できるようにする仕組み
• Ractorとユーザーレベルスレッドを使ようにしたThreadを利用 Ractor(Ruby 3.0〜) 21
22
23
24
メモリ管理 25
自動でメモリ管理する仕組み • 利用していないオブジェクトを見つけてメモリを再利用する • Rubyは世代別インクリメンタルGC ◦ マーク&スウィープを利用する ガベージコレクション 26
Rubyのヒープページ 27 RVALUE (オブジェクト) ヒープページ(16KiB) RVALUE (オブジェクト) RVALUE (オブジェクト) スロット(40
Bytes) ・・・ RVALUE (オブジェクト) 409個
マーク&スウィープ 28 A B C D E F G H
root
マークフェーズ 29 A B C D E F G H
root
スウィープフェーズ 30 A B C 未使用 (T_NONE) E 未使用 (T_NONE)
未使用 (T_NONE) 未使用 (T_NONE) root
メモリの空間的局所性 • CPUがアクセスするデータが狭い範囲に収まっている • CPUのキャッシュ(L1, L2…)は小さいが高速 • ヒープページにデータがあるとキャッシュが使われやすい 課題: スロットに収まらないオブジェクト
• OSから新たにメモリを取得する(高コスト) • ヒープページではない場所に保存する(メモリの局所性が低い) メモリの局所性とその課題 31
空のスロットを埋める仕組み • メモリ使用量を削減する • メモリの局所性を上げる GC.compactで実行可能 • Ruby 3.0から自動実行も可能(デフォルト無効) コンパクション(Ruby
2.7〜) 32
スウィープ後の状態 33 A B C 未使用 E 未使用 未使用 未使用
root
コンパクション 34 A B C E 未使用 未使用 未使用 root
未使用 OSに返却できる
40 bytesを超えるデータをヒープページで扱う仕組み • 3.2からデフォルトで有効 • 3.3で使えるクラスが増えた 可変幅アロケーション(VWA, Ruby 3.1〜) 35
…
36
37
パーサーの改善 38
パーサー 39 Lexer Parser ソースコード parse.y (文法規則) パーサー ジェネレータ AST
トークン Bison Lrama Ruby 3.3 〜
移植性 • CRubyのパーサーは外部から使用できない • 複数のパーサーの実装が開発されている エラー許容性 • LSP等で壊れたコードでも部分的に解析したい メンテナンス性 •
parse.y(文法規則)は魔境らしい パーサーの課題 40
Lrama (Ruby 3.3〜) • エラー許容性を持つパーサーを生成するパーサージェネレーター • CRubyに依存のないパーサーをビルドできるようにする • parse.yのリファクタリング Prism
(YARP, Ruby 3.3〜) • エラー許容性を持つ手書きのパーサー • 独立して外部から使用できる • --parser=prism で実験的に利用できる 2つのアプローチ 41
Lramaのアプローチ 42 Lexer Parser ソースコード parse.y (文法規則) パーサー ジェネレータ AST
トークン ここをいい感じにする
Prismのアプローチ 43 Lexer Parser ソースコード parse.y (文法規則) パーサ ジェネレータ AST
トークン これをいい感じにする
44
45
46
47
高速化 • JITコンパイラ • 並行処理・並列処理 • メモリ管理 まとめ 48 Rubyをより便利に
• パーサーの改善 本番ではもっと深い話がたくさん聞けます 会場で一緒にわからねぇ…楽しみましょう!!!
• Pat Shaughnessy. “Ruby Under a Microscope” • Takashi Kokubun.
“Ruby JIT Hacking Guide”. RubyKaigi 2023 • さき(H.Saki). “Goでの並行処理を徹底解剖! ” • 笹田 耕一. “Rubyによる並行並列プログラミング ”. Ruby World Conference 2023 • hachi8833. “Rubyのメモリ管理方法1: 基本概念(翻訳) ”. TechRacho. https://techracho.bpsinc.jp/hachi8833/2022_06_02/118259 • hachi8833. “Rubyのメモリ管理方法2: Ruby 3.1の文字列の可変幅アロケーション(翻訳) ”. TechRacho. https://techracho.bpsinc.jp/hachi8833/2022_06_08/118447 • Matt Valentine-House. “Plug & Play Garbage Collection with MMTk”. RubyKaigi 2023 • Yuichiro Kaneko. “The future vision of Ruby Parser”. RubyKaigi 2023 参考文献 49
We Are Hiring!! 50
付録 Fiber Schedulerのサンプル 51 Fiber.new do |io| message = io.read_nonblock
5000 @selector.register Fiber.current, io Fiber.yield io.write_nonblock "response" @selector.register Fiber.current, io Fiber.yield end class Scheduler def io_wait(io, events, timeout) @selector.register Fiber.current, io, events end end Fiber.set_scheduler(Scheduler.new) Fiber. schedule do message = io.read 5000 io.write "response" end