2023/1/23
前回 → 2023/1/11
Ruby Association Grant Targets
Ruby Association Grant: Targets
Developing Pf2
✅ Simultaneous observation of multiple Ruby Threads
✅ Improved visualization
Automated testing
✅ GVL observation
✅ When did the Thread acquire the lock?
Contention information
How long are Threads waiting for the lock?
GC observation
✅ Markers
今回の進捗
中間報告を出した
pf2 0.2.0 をリリース
https://rubygems.org/gems/pf2/versions/0.2.0
誰が106回もダウンロードしてるんだろう……
引き続き中身をだいぶ書き直しつつ
安定しているはずの scheduling algorithm だけを収録してリリース
Pf2.profile { your_code_here } API の追加
macOS サポート (TimerThreadScheduler のみ)
Merging C-level backtraces の最低限の実装が動いた
これは Mysql2::Client#query の呼び出しをプロファイルしている様子
https://scrapbox.io/files/65af0dee889bfd0023296a44.png
rb_mysql_query() (mysql2 gem), <no debug symbols> (たぶん libmysqlclient), SSL_write() (openssl), write() (libc) などが見えていて面白い
Rust の std::backtrace でもいけるじゃんって思ったけど(↑ はそれで実装した)、やはり関数のアドレスなどはほしいので libbacktrace がよいかも
https://ruby.slack.com/archives/C062N8HC5ST/p1705950633676579
Native の下に Ruby がくるケースはある?
rb_vm_exec か vm_call_cfunc_with_frame を見つけたらそこで表示を打ち切ってるだけ
なのでいいアルゴリズムを考えてあげる必要がある
現時点の実装は stack top にある C frame を乗せてあげてるだけ
間の Ruby cfunc の部分を Native code にしようとするともっと賢い実装が必要
C extension から rb_funcall されるケース
cfp に iseq があれば Ruby で実装されていることが分かる
algorithm
cfp を順番に見ていく
ec (execution context)
ec->cfp を たどっていく
cfp rb_control_frame_t ->iseq
iseq があったら Ruby だと思っていい
なかったら cfunc (の可能性がある)
ない場合は vm_exec_core() の外側にいることは間違いない (Ruby ではない)
C のスタックを差し込みたい場所の可能性が高い
動かなさそうなケース
vm_exec の中に vm_catch_execption (例外) みたいな関数を通ったとき?
↑ 一番上だけでなく、中間の部分の C func の部分にも C stack を差し込むのをやってみる
次にできそうなこと
普通にプロファイラとしての使い勝手を上げていく
オプションとか Rack middleware
interval, scheduler, time mode
wall time + cpu time の両方を計測することはできそう
2つのタイマーを動かすだけである
その分オーバーヘッドは普通に大きい
wall time で一定の interval で取りつつ、wall time のタイマーが発火するごとに cpu time のクロックを見て、一定時間が経過していたら直前の sample の重み (?) を足す、みたいなことでも近似はできそう
GC の扱いについて
そもそも GC に関するメトリクスの適切な気にしかた(= 何を収集するといいのか)が分からない
GC time をチューニングする際は memory profile を取ってアロケーションの数を見たりする、が
GC time のどれぐらいが scan にかかっているか? みたいなことが分かるとうれしいかもしれない
が、それはスタックとかを見ても別に分かることではなさそう
GC time + GC の種類 (major or minor) ぐらいは取れてもいいかも
major GC が多い場合やりたいチューニング、みたいなのはある
RUBY_REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO の調整? とか
GC.stats の中に major_gc_count はあるので、どこかでは判定できてはいるはず
あるフレームが JIT されているかは分かる?
https://github.com/ruby/ruby/blob/8c3eb47fffc6d274ad9c65f507676f8ae906a948/vm_backtrace.c#L1617-L1619
eBPF 方面がなんとなく気になっているが特に知見はない
https://ruby.slack.com/archives/C062N8HC5ST/p1705421363505229
call site ごとに山を分けるか?
異なる呼び出しは分けたい
プロファイルの見方
Call graph, Flamegraph → その関数自体を最適化する
Upside down (inverted) → 呼び出し回数を減らす