Slide 1

Slide 1 text

プログラミング言語Slack 定期ミートアップ 2021/05/30 RustでつくるRubyのFiber monochrome twitter: @s_isshiki1969

Slide 2

Slide 2 text

Agenda ● RustでRubyの処理系を作ってます。 ● 大きなアプリケーションやライブラリを動かせるよう頑張ってます。 ● その過程で協調スレッド(コルーチン)を実装しました。

Slide 3

Slide 3 text

ruruby (https://github.com/sisshiki1969/ruruby) ● Rust製のRuby実装 ● 仮想マシンインタプリタ ● 他の既存実装・仮想マシンへの依存なし ● ガーベジコレクタを独自実装 ● 3万行ぐらい

Slide 4

Slide 4 text

構成 virtual machine (VM) Rubyコード 抽象構文木 (AST) バイトコード parser codegen a = 1 + 2 PUSH_INT 1 PUSH_INT 2 ADD SET_LVAR ‘a’ Add 1 1 1 2 3 Assign 2 LVAR ’a’

Slide 5

Slide 5 text

Optcarrot ● Rubyで書かれたNESエミュレータ ● 「Ruby3をRuby2の3倍速くする」目標のための準公式ベンチマーク ● 実際にゲームができるモードと、ベンチマーク専用のモードがある ● ベンチマークモードでは外部ライブラリを使用しない ● ≒6500 LOC repo: https://github.com/mame/optcarrot ● 紹介スライド: https://www.slideshare.net/mametter/optcarrot-a-pureruby-nes-emulator

Slide 6

Slide 6 text

https://www.slideshare.net/mametter/optcarrot-a-pureruby-nes-emulator NESの仕組み 本来はパラレルに動くCPUとGPUの処理を簡潔に記述するためにコルーチンを使用

Slide 7

Slide 7 text

Fiber (coroutine in Ruby) f = Fiber.new { .. } f.resume(200) A B Fiber.yield ←100 f.resume(100) C f (child fiber) f = Fiber.new {|x| [ A ] Fiber.yield [ C ] } f.resume [ B ] f.resume [ D ] parent fiber D x ←200 〇2つの実行コンテキストが切り替わりながら処理が進む。 〇親は複数の子を生成できるが、親は1つのみ。

Slide 8

Slide 8 text

仮想マシンにおけるFiberの実装 f = Fiber.new { .. } f.resume A B Fiber.yield f.resume C f (child fiber) parent fiber D ● Fiberの生成 ● resume(親Fiber→子Fiber) ● yield(子Fiber→親Fiber)

Slide 9

Slide 9 text

OSスレッドによるFiberの実装 https://github.com/sisshiki1969/ruruby/bl ob/aa72942d46337574448dafb493ee27 bb4defb69d/src/value/fiber.rs#L124 欠点:重い、遅い

Slide 10

Slide 10 text

アセンブリによるFiber切り替えの実装 ● Fiberの生成 ○ Fiber用のマシンスタック領域を確保 ● resume(親Fiber→子Fiber) ○ レジスタを親スタックに保存 ○ 子のスタックへ切替 ○ レジスタを子スタックから復帰 ○ ret ● yield(子Fiber→親Fiber) ○ レジスタを子スタックに保存 ○ 親のスタックへ切替 ○ レジスタを親スタックから復帰 ○ ret Fiber情報へのポインタ ガード関数ポインタ VM起動関数ポインタ レジスタ保存領域 子Fiberスタック -8 -16 -24 -80 中継関数ポインタ -32 ←sp

Slide 11

Slide 11 text

nix / x86-64 darwin / aarch64