並列で重い処理をブン回す
by kiridaruma at PHPer kaigi 2022
自己紹介
@kiridaruma
-
1998/3/11生まれ
-
ピクシブ株式会社
-
趣味でHaskell, Elm, Rust
自己紹介
@kiridaruma
- ミリタリー関連
- 米軍のマニュアル読んだり
- シミュレーターで遊んだり
- サバゲーしたり
重い処理
- Webサーバで処理が1秒を超えることはほぼない
- ただし、バッチ処理で大量のデータを扱うと
時間がかかる原因
-
原因はいろいろ
-
原因によって対応方法が変わってくる
CPUが何をしているかの違い
-
計算量が原因の場合
-
ディスクやネットワークなどが原因の場合
CPUが動いているか待っているか
-
基本的に計算量が大きい処理は速くできない
-
CPUが待っている時間が長い処理(IOなど)
→Generator
PHP8.1からはFiberも
実際にあった例
-
Webページの表示速度の計測を行うバッチ
-
計測には時間がかかる
- まず計測するAPIを実行
- その後、結果を取得するAPIを実行
実際にあった例
-
この場合、ボトルネックはCPUの外部にある
-
計測対象のページは複数
-
→Fiberでまとめて並行処理を行うようにした
並行
タスク1→タスク2→タスク3→タスク1→タスク2→…
並列
タスク1→タスク1→タスク1→…
タスク2→タスク2→タスク2→…
タスク3→タスク3→タスク3→…
並列
-
基本的に計算量が大きい処理は速くできない
-
重い処理を同時に実行して全体を早くはできる
PHPで並列処理
-
GeneratorもFiberも並行処理
-
そもそもPHPだけでは並列処理できない
OSの並列処理
-
スレッド
- 他スレッドとメモリを共有する
- 上手くやらないと意図しない挙動を起こす
-
プロセス
- 他プロセスからメモリは隔離される
- 入出力周りに気を付ければ何とかなる
PHPでスレッド
- pthreadsもparallelもPHPのモジュール
- なので別途有効化する必要がある
- 場合によってはPHPの再ビルドも
PHPでプロセス
-
標準でいくつか使える関数がある
-
Linuxなら大体すぐ使える
スレッドとプロセス
-
スレッドをちゃんと動かすのは大変
-
「プロセスよりスレッドの方が早いんでしょ?」
- そんな差はない(大抵は気が付かないレベル)
- そもそも重い処理をしてる時点で変わらない
-
プロセスの方が扱いやすい
- メモリが隔離されてるのでバグを生みにくい
- プロセス間通信は少し考えないといけない
マルチプロセスなコードの書き方
-
基本的に2つのプロセスに分けて考える
- 仕事をするプロセスを生成するプロセス
- 実際に仕事をするプロセス
-
よくあるミドルウェアもだいたい同じ構成
プロセスを生成する
-
別プロセスを実行する関数
- exec(), passthru(), system()など
- popen()
- proc_open()
-
色々ある
exec(), passthru(), system()など
- 一番簡単に外部コマンドを実行できる
- ただし、外部コマンドの操作等はできない
- 標準出力だけ取れる
popen()
- ファイルと同じように読み書きできる
- プロセスの標準入出力を扱える
- ただし、1方向(入力or出力)だけ
proc_open()
- 他の関数と比べて一番詳細に設定できる
- 標準入出力のパイプ
- カレントディレクトリの設定
- 環境変数の設定
何を使うべきか
- A. やりたいことによる
- ので、php.netを良く読むのが良いです
- 特に入出力が大きく違う(個人的意見)
実際にあった例
- DBで大規模なデータ移行の必要があった
- テーブルのサイズの合計は4TB以上
- 100億レコード以上
- 愚直に回すと1ヶ月かかる計算
実際にあった例
-
マルチプロセスで並列で実行した
-
オンプレの専用の強いマシンを使用
強いマシン
-
オンプレ環境は調達すれば強いマシンが使える
-
クラウドの場合は分散させることが多い
- lambda, functions, cloud runなど
長時間のバッチでありがちなこと
-
「あれ、良く分からないけど失敗してる」
-
バッチ処理はログの出力が大事
ログの出力
-
基本はプロセス毎にログを出力するのが楽
- プロセスごとに詳細なログが追える
- ファイル等に出力する場合、ロックに注意
- 外部に送る場合はあまり考えなくてよい
-
そもそもプロセス同士の依存を減らした方が楽
シグナル
-
シグナルハンドラは必ず設定する
-
オンプレ/クラウド関わらずシグナル処理は大事
- killした時に子プロセスもまとめて止めるなど
- インスタンス停止時にシグナルが送られるものも