Slide 1

Slide 1 text

がんばれPHP FIBER がんばれPHP FIBER いがらし いがらし

Slide 2

Slide 2 text

話すこと 話すこと Fiberの紹介

Slide 3

Slide 3 text

私は誰ですか 私は誰ですか プログラマー 34才 ⽣まれも育ちも仙台 好きな⾔語はC

Slide 4

Slide 4 text

FIBERとは FIBERとは 協調マルチタスクのユーザスレッド コルーチン/グリーンスレッドとも呼ばれる Fiberという名前はWin32APIとかRubyとかで使わ れている

Slide 5

Slide 5 text

PHP FIBER PHP FIBER 去年から上海のlvhtさんが作ってる拡張 PHPのコアにFiberを⼊れようとしてる https://github.com/fiberphp/fiber-ext

Slide 6

Slide 6 text

PHP RFC: Fiber https://wiki.php.net/rfc/fiber

Slide 7

Slide 7 text

何ができる︖ 何ができる︖

Slide 8

Slide 8 text

単純な例 単純な例 function func() { return Fiber::yield(1); } $fiber = new Fiber(function ($a) { $b = Fiber::yield($a); $c = func(); return $c.$b; }); echo $fiber->resume(2); // echo 2 echo $fiber->resume("world"); // echo 1 echo $fiber->resume("hello "); // echo "hello world"

Slide 9

Slide 9 text

function func() { return/* 11 */ Fiber::yield(1)/* 8 */; } $fiber =/* 1 */ new Fiber(function ($a) { $b =/* 6 */ Fiber::yield($a)/* 3 */; $c =/* 12 */ func()/* 7 */; return $c.$b;/* 13 */ }); echo/* 4 */ $fiber->resume(1)/* 2 */; // echo 3 echo/* 9 */ $fiber->resume("world")/* 5 */; // echo 1 echo/* 14 */ $fiber->resume("hello ")/* 10 */; // echo "hello

Slide 10

Slide 10 text

Fiber = 中断/再開できる関数 Generatorのyieldと違いネストした呼び出しから 抜けられる 何が嬉しいか︖ 使い道は⾊々、I/O多重化とか

Slide 11

Slide 11 text

I/O多重化(てきとー) I/O多重化(てきとー) // query()やfetchAll()が内部でノンブロッキングI/OとFiber::yield()を $scheduler = new Scheduler(); $scheduler->add(new Fiber(function() { $db = ConnectionPool::getConnection(); var_dump($db->query('なんかSQL1')->fetchAll()); })); $scheduler->add(new Fiber(function() { $db = ConnectionPool::getConnection(); var_dump($db->query('なんかSQL2')->fetchAll()); })); while ($scheduler->run()) {} // 中でFiberをresume

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

DBALでノンブロッキングI/OとFiber::yield()を利⽤ DBALからFiber::yield()で抜ける先となり、次の処 理を選ぶ部品を⽤意(Scheduler) その間へ差し込む層には既存FW等のコードもそ のまま使える(←とても重要)

Slide 14

Slide 14 text

実験的にDoctrine ORMの⾮同期版を作る試み amphp/mysqlとamphp/green-threadを利⽤ https://github.com/joelwurtz/doctrine-async

Slide 15

Slide 15 text

説明⽤のよくある表 説明⽤のよくある表 プロセス OSスレッド Fiber メモリ空間 分離 共有 共有 タイムスライスで 切り替え あり あり なし I/Oブロックで 切り替え あり あり なし ⽣成コスト 重い 中くらい 軽い 切り替えコスト 重い 中くらい 軽い 複数コア利⽤ できる できる だめ

Slide 16

Slide 16 text

RFCの現状 RFCの現状 ⽅針の固まってない部分がある RFCが少し⼝ベタ

Slide 17

Slide 17 text

ASYNC/AWAIT? ASYNC/AWAIT? async/await欲しいよね https://github.com/fiberphp/fiber-ext/issues/10

Slide 18

Slide 18 text

Promiseのような⾮同期データ取得の抽象化まで ⼿を出すのは話がデカすぎ Future Scope送りに https://github.com/fiberphp/fiber-ext/issues/31

Slide 19

Slide 19 text

マシンスタックの扱い マシンスタックの扱い 現状実装はVMスタックのみ切り替え array_map()等の C → PHPで抜けられない やろうとしたら例外を⾶ばす

Slide 20

Slide 20 text

$f = new Fiber(function () { return array_map(function ($i) { return Fiber::yield($i); // 例外飛ばす }, ['a', 'b']); });

Slide 21

Slide 21 text

マシンスタック(Cスタック)も切り替えるPR Pros: どっからでもyieldで抜けられる Cons: ポータビリティが失われる メモリ消費量が増える https://github.com/fiberphp/fiber-ext/pull/30

Slide 22

Slide 22 text

オリジナル版の⾜りないところ オリジナル版の⾜りないところ C → PHPでのFiber::yieldをいつでも避けられる か、というとそうでもない 既存FWの利⽤時も何気なく発⽣ call_user_func()系とか怪しい PHPUnitも\ReflectionMethod::invokeArgs()とか finallyが今のところちゃんと動いてない

Slide 23

Slide 23 text

MARTIN版の⾜りないところ MARTIN版の⾜りないところ PHP処理系は動作環境を明⾔してない ポータブルでない何かをコアへ⼊れるのは⼤変 そう︖ 「必要な時だけCスタック確保」は難しく、メモ リも多めに確保せざるを得ない

Slide 24

Slide 24 text

解決策を緩く募集

Slide 25

Slide 25 text

PHPでFIBER何うれしい︖ PHPでFIBER何うれしい︖ PHP単体でサーバとかマイクロサービスみたいな のが作りやすくなる ErlangとかAkkaみたいなActorもやりやすめに なるよ︕

Slide 26

Slide 26 text

PTHREADS拡張どうよ PTHREADS拡張どうよ PHPでスレッドを使えるものの、扱いがめんどい pthreads拡張はZTSにもとづいてるため、スレ ッド間で基本はデータが共有されない 静的プロパティ等の扱いに癖があり、既存FW 等の資産が使いづらい印象 癖への対処法が確⽴されていればよいが、利 ⽤例もあまり⾒つからない

Slide 27

Slide 27 text

FORKしたらいいんじゃね︕ FORKしたらいいんじゃね︕ プロセスで済むならプロセスでも別にいい プロセスは重め、データ共有も⾟いめ

Slide 28

Slide 28 text

PHPでやらなきゃいいんじゃ︖ PHPでやらなきゃいいんじゃ︖ そんな野暮なことを⾔わない︕ できないよりできた⽅が楽しいでしょ︕

Slide 29

Slide 29 text

動作原理 動作原理

Slide 30

Slide 30 text

実⾏コンテキスト 実⾏コンテキスト Fiberは実⾏コンテキストを差し替えて動く 実⾏コンテキストとは︖ マシンが次に何を実⾏するか、その次に何を 実⾏するか……を定める状態 メモリとCPUのレジスタ状態でだいぶ表せる

Slide 31

Slide 31 text

メモリについて メモリについて CPUが扱う命令/データを⼊れとく場所 1バイトのデータ⼊れる箱がたくさん並んでるイ メージ

Slide 32

Slide 32 text

CPUについて CPUについて CPUはメモリ上の機械語/データを解釈して実⾏ PCやIPと呼ばれるレジスタを持つ 「今メモリ上のどこを実⾏してます」を指す 1つ命令を⾷っては次の番地の命令、と動く

Slide 33

Slide 33 text

ジャンプ ジャンプ 「次の番地の命令」以外へPCを動かす ちょっと前の番地へジャンプすればループに 「データの状態がAなら指定番地へジャンプ、そ うでなければ次の番地に進む」的な分岐命令も

Slide 34

Slide 34 text

C関数呼び出し C関数呼び出し 次のPCの値をどこかに保存 よくあるのはスタック 関数の命令列の先頭へジャンプ returnで保存しておいた元の番地へ戻る

Slide 35

Slide 35 text

スタック スタック コンピュータ⽤語、FILOのデータ構造を指す データをぽんぽん突っ込めて、突っ込んだのと 逆順に取り出してなかったことにできる

Slide 36

Slide 36 text

マシンスタック マシンスタック 多くのCPUはメモリ領域のどこかをスタックとし て扱う命令を持つ これのためにスタックの現在位置を指すレジス タ(SP︓スタックポインタ)がある

Slide 37

Slide 37 text

C⾔語ではよく関数の戻りアドレスの他、ローカ ル変数もスタックに保存

Slide 38

Slide 38 text

実⾏コンテキストはスタック 実⾏コンテキストはスタック に︖ に︖ 各実⾏コンテキストで別々のスタックを持つよ うにする PC/IPとSPを含むCPUのレジスタを保存、別コン テキストのを復帰してジャンプ できた︕コンテキストスイッチできた︕単純︕

Slide 39

Slide 39 text

PHPではもう⼀⼯夫要る

Slide 40

Slide 40 text

VMスタック VMスタック PHPコードは処理系によりVMのオペコードにコ ンパイルされて実⾏

Slide 41

Slide 41 text

PHPのVMはマシンスタックと別にVM独⾃のスタ ックを持つ VMスタックも切り替える必要がある

Slide 42

Slide 42 text

というか、実はPHPの世界だけならVMスタック だけ切り替えればよい PHP Fiberのオリジナル実装はコレ PHP処理系は通常のPHP関数呼び出しで再帰的な VM呼び出しをせず、実マシンでいうPC変更に相 当する挙動(opline切り替え)のみ⾏い、オペコ ードを1つ1つループで取り出して実⾏

Slide 43

Slide 43 text

問題はPHP -> C -> PHPのような呼び出しでCの世 界での実⾏コンテキストも保存が必要なケース

Slide 44

Slide 44 text

オリジナル実装はvm_interruptという機構を利⽤ オペコード1つ実⾏するごとに処理系が割り込 みフラグを確認し、⽴ってれば割り込みハン ドラを呼び出すような仕組み 割り込みハンドラ内でVMの実⾏状態を切り替 えて復帰 martin版はswapcontextまたはインラインアセン ブラのジャンプで素直にコンテキスト⼊れ替え

Slide 45

Slide 45 text

まとめ︓がんばってほしい まとめ︓がんばってほしい 「Node.js使え」「Go使え」的な⾯⽩みのない話 に負けないでほしい ⾯⽩いのでみんなも触ってみてほしい

Slide 46

Slide 46 text

おしまい おしまい