RubyKaigi 2017 の3日目に参加したので、そのメモです。
朝
時間があれば広島平和記念資料館に行こうかと思っていましたが、宿を出るのが遅くなってしまって時間がなかったので行けませんでした。
Compacting GC in MRI
- 自己紹介
- 草生える の英語解説で有名になった
- Copy on Write Optimization
-
require 'objspace'
とObjectSpace.memsize_of(obj)
で CoW の確認 - fork と CoW
- CoW Page Fault
- Unicorn での話
- Page Fault を減らす方法
- 共有メモリの変更が原因
- Garbage Collector の影響
- Object Allocation
- 空きがあるとオブジェクトのサイズは OS のページより小さいので、1オブジェクトの生成でもページ全体のコピーが発生するのでコンパクションする
- Two Finger Compaction (2本指コンパクション) というアルゴリズムを選んだ
- 欠点: 遅い、オブジェクトがランダムな場所に移動する
- 利点: 簡単!
- アルゴリズム: オブジェクトの移動をして、参照の更新
- Free Pointer と Scan Pointer で左右からみていってオブジェクトを左に空きを右に固まるように交換していく
- オブジェクトを順番に見ていって参照を更新
- 移動先番号にしていたところを空きに変更
- unicorn の fork 前に rails の読み込みと GC.start をするようにした
- gc.c への変更の詳細解説
- C 実装されている中にオブジェクトへのポインターを持っていると更新できないので、移動するとクラッシュする
- そのため、そういうオブジェクトは移動しないようにピン留めしておく
-
hash_key(オブジェクト)
がメモリアドレスなので、移動できない - Dual References
- C と Ruby の両方から参照されている場合も移動するとクラッシュする
- グローバル変数もヒューリスティックスにピン留めする
- 文字列リテラル
- リテラルを移動すると bytecode を変更する必要があるが、難しいのでまだ対応していない
- 結局何も移動できない?
- ほとんどの問題は解決できる
- 現在使用中の Rails のコードでも 46% のオブジェクトは移動可能
- コンパクション前後のグラフ
- メモリ検査ツール
-
ObjectSpace.dump_all
: JSON でファイルに書き出せる - ヒープの断片化のグラフ
-
/proc/${PID}/smaps
はスキップ - まとめ
- 小さいプログラムにはあまり節約にならないのでプロダクションでテスト中
- コンパクションは不可能だと思っていたが、その質問に答えることができなかったので、もっと早く試せばよかったと思った
- 不可能と思っても可能かもしれない
Irb 20th anniversary memorial session: Reish and Irb2
- 勝手に20周年記念講演
- ちょっと irb の話と Reish の話をする
- Ruby 考古学の復習と追加の話
- irb の昔の名前の rbc の由来は bc コマンド
- 他に perl -de 0 や sample/eval.rb が開発のきっかけ
- REPL (Read-Eval-Print-Loop)
- Lisp の
(loop (print (eval (read))))
- Ruby 的には
loop{p eval gets}
- 複雑な構文の言語では初ではないか
- REPL の用途
- Ruby の振る舞いを確かめる
- ライブラリなどのデバッグ用コンソール
- 情報科学入門 Rubyを使って学ぶ という本では irb を使っているらしい
- しかし irb 上で生活している人はいなさそう
- そこで Reish
- shell の上でも ruby 的な生活を満喫するため
- Enumerable, Iterator (たぶん今ではブロックと呼ばれるもの全般をさしてそう), OO, ほか
- 何がシェルっぽいか: comma なし
""
とかなくても良い文字列ベース - Reish のコードは Ruby に変換して実行
- パイプラインは lazy enumerator で繋がっている
- ブロックも使えるが do を予約語にはしにくかったので
ls -l --do
という感じになっている ({}
は普通) - デモ
- https://github.com/keiju/reish
- job コントロールもある
- job ごとに Thread を起動している
- 補完
- irb 再考
- 長い間開発は停止していたが Reish の開発で irb の方も構想が見えてきた
- irb の lexer は使っている gem があるので、大幅に変更は難しそう
- Reish は完全なパーサーを持っていて、高機能なことができている
- irb2 = reirb ?
- zsh の
ls --a[TAB]
で出てくるヘルプのようなことができるとうれしそう - zsh は 複数行の編集機能 (zle) があったり
- Reish 用のマルチラインエディタ Reidline を開発中
- Q(次の時間の発表者のaycabtaさん) メモ取れず
昼休み
途中の時間でセッションを諦めて見にいくのは難しいかなと思って、コンビニで軽く食事をして、広島平和記念資料館に行ってきました。(Twitter で RubyKaigi 2017 の名札を見せると無料で入れるという情報があったため)
本館はリニューアル工事中で、東館だけが開いているということのようでした。
小学校の修学旅行できた覚えがあるのですが、最初の上の階をぐるっと見て回る常設展示は最近の情報もあって、昔来た時には絶対なかったものだなあと思いつつ見ていました。
最後に階段を降りた後にあった企画展示室のところは、昔トラウマになった白黒写真などが少しありました。もらったパンフレットには、閉館中の本館で展示していた資料などを展示していると書いてあるので、本館が復活すれば、昔見たようなものがたくさんあるのではないかと思いました。
Ruby Parser In IRB 20th Anniversary…Now Let Time Resume
- https://bugs.ruby-lang.org/issues/11389
- asakusa.rb とか
- IRB: 1997, RDoc: 2003
- RDoc は IRB の lexical analyser を使っていた
- legacy になってしまってメンテナンスコストが増大していた
- private def foo のような書き方の対応とか
- IRB: 1997, RDoc: 2003, Ripper: 2004
- parse.y, IRB, RDoc の3個の Ruby パーサーが重複して含まれている
- 2016年12月から作業開始
- RDoc がクラッシュしたので直した
- 古い実装と書き直した実装で出力を比べて見たら、 RDoc がバグっていたので古い lexer で新しいものに対応した
- 最終的にマージされた
- 後置 if と普通の if の区別などに
lex_state
が重要 - https://github.com/ruby/rdoc/pull/512
- RDoc 6 beta
- 質疑応答 (翻訳は zzak)
- Q(ujm):
lex_state
を古い ripper では pure ruby で実装したのはどうやったみたいな質問っぽい → メモ取れず - Q(かねこさん): ripper は使っているバージョンの ruby で動くので、古い ruby バージョンのドキュメントを処理すると問題が起きることもあるのでは? → 壊れることもあるかもしれないが、大丈夫なのではないかという感じ(?)
- Q(tenderlove): ripper はコード内のコメントを保存していますか? → AST の段階ではコメントは含まれないが、tokenize しかしていないので、開始、中身の謎の文字列、終端のようになって、中が何を意味するかには踏み込まないので、情報が消えたりはしない。
- Q(ujm): RDoc は既存のものと互換性があるように ripper に置き換えたが次は何に挑戦する予定? → Ruby コードの情報をとるライブラリ、 irb の TAB キーでの補完でドキュメント (RDoc) も出るようにしたい
- Q: irb のパーサーをどうにかする予定は? → nobu が ripper に置き換えようとしたが諦めた。多分できそうだが、大変そう。メソッドドキュメントの表示をしたい。
- nobu の irb は syntax highlight がついていた。
- syntax highlight もやりたい。
- Q: Cのファイルのドキュメントは? → 今回の変更とは無関係。
- C extension のドキュメントの方も必要に応じてみた方が良いのかもしれないが、まだ見ていない。
- Q(ujm): 6ページあった修正点の中からピックアップするなら? → legacy な lexer で、新しい ruby syntax のサポートが大変だった
- テストが壊れた
- Q: 聞き取れず → 答えられない
- Q: 1日どれくらいやっている? → 1ヶ月ぐらい、1日16時間?
一回休み
セッションは諦めて休憩していました。
Writing Lint for Ruby
- SideCI
- Lint ツール: RuboCop, Reek, Brakeman, Querly, …
- Rules of Lint, Lint tools をかけるようになることがこのプレゼンの目標
- lint tool があればより安全にプログラムが作れるようになる
- Lint とは何か?
- もともと C 言語用のツールの名前だったが、今ではいろんな言語にある
- RuboCop の例を紹介
-
if 10 < x < 20
は実行するとエラー -
foo bar { body }
やx *y
は曖昧 - そういうコードをチェックする
- どう動くか?
- AST (Abstract Syntax Tree) で扱う
- parser gem を使って AST に変換している
- parser gem の AST の node は type と children を持つ
- 行、桁、ソースなどのメタデータも持っている
- 他のパーサー
- Ripper は標準で含まれているが、パースで想定するバージョンと実行する ruby のバージョンが同じになってしまう
- ruby 2.4 で ruby 2.0.0 の lint ができない
- ruby_parser は使ったことがないが parser gem と似たような感じらしい
- Traverser: Depth-first search をして node ごとに
on_send
などのon_#{node.type}
を呼び出す - if の条件に Integer リテラルのみを直接書いた場合に警告を出す例
- Lint の限界 (Ruby の場合)
- ローカル変数はただの変数
-
num = 1; if num; something; end
のようなコードは対象外 - 不可能ではないが複雑になる: たとえば Brakeman では追跡している
- メソッドの定義やクラスや定数が正確にはわからない
-
sprintf('%s, %s', str)
は警告されるがsprintf
が再定義されていると意味がない可能性があるが、そんなことはしないだろうと想定して動いている - 動的なものは Lint より Test の方が向いている
- Lint は AST がわかる
- Lint は実行せずに解析できる
- トレードオフがある
- RuboCop に cop (ルール) を追加する
- 汎用的なものはヘルパーメソッドがあるので書きやすい:
if 1 ; end
,ruby -cw
のようなもの - デモ:
rake new_cop[Lint/LitInCond]
- 特定のフレームワークに対して追加したい場合
- RuboCop Plugin としてかける
- 例: backus/rubocop-rspec
- 1から作る場合
- たとえば Lint + Git Diff, Ruby + YAML 用など
- 新しく作った方が作りやすい
- 質疑応答
- Q: sprintf の再定義のような
Kernel
に再定義しているのを検出する cop は? → ない。ある程度は作れそう。本体には入りそうにない。 - Q: デフォルト false では? → デフォルト false のものはメンテされないという現実があるので入れたくない。
- Q(mame): RuboCop のゴールは?
10 < x < 20
のような初心者が陥りがちなものだけではなく、コーディングスタイルを押し付けられる感じがある? → 二面性がある。スタイルチェッカーと Lint としての側面がある。どっちも増えそう。 - アップデートが辛い問題はツールを作っている。Style 系をオフにするとか。
- Q: 汎用的なもののようにみえるが、プロジェクト固有のものはどうすれば? → 自社用のプラグインを作る or soutaro さんが作っている YAML でかくものがあるのでそれを使う のが良いのではないか
- Q: カスタムコップを作りたい場合のドキュメントがなさそうな理由は? → 現状カスタムコップ、プラグインを作る仕組みが整備されていない。issue が止まったままなので頑張りたい。
- Q:
new_cop
タスクは stable? → 本体に入っているので大丈夫だが、将来的に変更される可能性はあるかも。 - Q: Rails は本体に config が入っているが? → 現状では特にロードマップはない。個人的には rails 関連は外に切り出したい。
- Q: 一部は
ruby -cw
にあった方が良いのでは? → 本体に持っていく動きは今のところない。ruby -cw
とかぶっているものもある。パフォーマンス的にできるのかというものもある。 - Q: メモ取れず → ローカル変数をトレースするのはやりたいが、良い方法を思いついていないので、手がついていない。
- Q: 速度が遅いのが気になっている。 parser gem が遅いのではないか。バージョンをわけたいという希望はないので ripper で高速化できないか? → ライブラリの CI で困りそう。インターフェースが違うので簡単に移行できない。
- Q(znz): feature request の issue を書いたことがあるが、request だけだと難しい? → できるだけ実装するか、無理なら無理と返信したいが、たまっていてなかなか手が回っていない。
How to write synchronization mechanisms for Fiber
- dRuby 本まだ買えます
- Web 版もあります
- Process - Thread - Fiber
- 80s はプロセスへの憧れがあった
- 90s のノンプリエンティブな協調型マルチタスクって Fiber じゃん?
- 実際のシステムは、プロセスだけ隔離しても結局はダメだった
- ほかのプロセスとどうにかして共有が必要だった
- IPC はめんどくさい
- 説明や使うのがめんどくさい
- Thread: 多くの人にはプロセスの方がいいと思う
- ほかのスレッドのメモリを触らないようにものすごく気をつける必要がある
- Thread を使うには OS よりうまく扱えるという傲慢さが必要
- Fiber: ほとんどの人には Thread の方がいいと思う
- Fiber を使うには Ruby よりコンテキストスイッチをうまく扱えるという傲慢さが必要
- Fiber → 自動的にスイッチしてよ → Thread → メモリを自動的に保護してよ → Process
- Fiber を使ったフレームワークがすごいらしい
- Fiber を使うプログラムを自分でも作って見たいと思った
- Thread よりも難しいと思ったら、その通りだったので共有したい
- Fibonacci の例
- ループで書くと簡単
- Fiber で書く例 (初日であったような感じ)
-
Fiber.new
,Fiber#resume
,Fiber.yield
- new しただけでは実行されない
- resume で Fiber にコンテキストを移す
- 一度目は先頭へ
- 二度目以降は
Fiber.yield
したところへ - これは何度目の resume かな…
- resume と yield の概念がとっつきにくい
- Enumerator で書き直す
- おすすめ
- 次のテーマは同期 (待ち合わせ)
- Thread での Queue を介した協調の Fiber 版を書きたい
- rendez-vous (Rdv) の実装
- イディオム: とりあえず resume、行き詰まったら yield
- Fiber.new してすぐに resume すると Thread.new のように使える
- やれることがなくなったら yield して Fiber.current を覚えてもらう
- 実装例: push, pop
- Fiber だと条件検査の排他制御が不要
- Thread だと危険
- 動きの例
- 同期の条件検査に排他制御が入らないので、Fiber のつもりが Thread だったというのは危険なので、Auto Fiber のようなものより、Thread のつもりが Fiber だったの方が安全ではないか
- Multiplexer: socket のある例
-
read_nonblock
を使う - ブロックしそうな時 (
IO::WaitReadable
) に yield してメインループに戻る - block-ish nonblock I/O
- ライブラリを作った: https://github.com/seki/bartender
- 実際のアプリケーション例
- tiny_drb の抜粋
- 昔よく見たバグの話
- デモは作って見たが、見てもよくわからなかったので、説明
- Multiplexer のまとめ
- Fiber 入門のまとめ: 難しいよね
- Fiber は難しいところが面白い
- 全体が暗記できるぐらいのサイズだったので罠を避けられた
- すみずみまでわかっていないとハマることがある
- ハマる可能性があると覚えておけば大丈夫
- 例: twitter クライアントの gem が全部読んで String にしてから JSON.parse するのでハマったので、ストリーム対応の JSON.parse はあるのかな?
- ハマりそう: RDBMS のドライバ
- 質疑応答
- Q: 読み書きしようとして安直にするとブロックするが、読めるだけ読むのがあるが書く方は? → 書く方もかけるだけ書くというのがある
- Q: gets とか使えなくて不便? → 聞き取れず
- Q(cuzic): Fiber の方が良いユースケースは? → Enumerator が良い例
- Q(cuzic): 外部イテレーターではなく agent のようなパフォーマンスがよくなるという例は? → ゲームのキャラクターを動かすような誰かがビートを刻むような例は良さそう。 I/O は向いてなさそう。 Auto Fiber や co-routine の例をみると良い例があるのかも。
- Q(joker): Thread で書いた場合は GIL で1コアでしか動かないが、(聞き取れず)だと別実装だと複数コア使えるとかあるが、Auto Fiber とかだと使えるようになる? → Fiber は Thread の中で動くので無理ではないか
- Q(joker): 外部イテレーターにするとか見せ方の変更はできるが、マルチコアでパフォーマンスの改善という用途ではない? → そう理解している
- Q: Thread と Fiber は違うのになぜ Fiber という名前なのかという感じの質問っぽい → Windows でそういう名前だから
MEDLEY のスポンサーセッション
- 会場アンケート
- 知っている人? → 少ない
- 昨日呑んだくれて疲れている人? → それなり?
- 遠隔診療が実質解禁されたので CLINICS というアプリを作った
- 医療 x IT への挑戦
Towards Ruby 3x3 performance
- Ruby 3 performance goal
- RTL (register transfer language) insns
- RTL insns と stack insns の比較
- RTL insns の精製方法
- RTL insns のオペランド
- RTL complications
- RTL insn combing and specialization
- Speculative insn generation
- RTL insn status and future work
- Possible JIT approaches
- Option 1: Writing own JIT from scratch
- メンテナンスするのが大変
- Option 2: Using widely used optimizing compilers
- コンパイルが遅い
- Option 3: Using existing JITs
- License issues and patent minefield
- Own or existing JITs vs GCC/LLVM based JITs
- How to use GCC/LLVM for implementing JITs
- MJIT: MRI JIT, Method JIT
- Example
- MJIT performance results
- どれが何かわからないグラフ
- 途中からちゃんと下まで出てきた
- Recommendations to use GCC/LLVM for a JIT
- MJIT status and future directions
- RTL と MJIT が MRI の一部になるかはわからないがこの方向性で頑張る
- 質疑応答
- Q(tenderlove): (メモ取れず)
- Q: inline 化の話っぽい
- Q: メモリ使用量の話っぽい
- Q(ujm): make check が通らない理由は? → 並列実行とか callcc とか GC が関わるものとか(?)
- Q: モチベーションは? → (よくわからず)
- Q(ko1): GCC でループが最適化が測地になる例があったが、再定義対応は? → (よくわからず)
- matz のコメント
クロージング
- MINASWAN 乙
- 936枚チケットが売れた
- Next: Sendai May 31 (Thu) - June 2 (Sat)
全体の感想
とりとめもなく色々と。
Twitter で @takatayoshitake さんとかの情報提供が色々と便利でした。(特に最終日のロッカー情報)
Twitter のハッシュタグ #rubykaigi
は流れが速いと全然追いきれないので、あとで togetter でみれるかなと思っていたら、今年は特になさそうで残念な思いをしました。 RubyKaigi 全体のハッシュタグも、部屋ごとのハッシュタグも入り口に書いてあっただけで、特にアナウンスはなかった気がするので、部屋ごとのハッシュタグはつけずに #rubykaigi
だけのツイートを、その人がどの部屋に参加していたか識別してまとめに振り分ける、という判断をしつつ、まとめを作ってくれる人が必要そうな気がします。
台風が移動に影響したり、カープ優勝でセールがあったりなど、外部要因でもいろんなことがあったのが印象深かったです。
3トラックあったので、興味があったのに見に行けなかったものも多いので、あとでチェックしたいと思いました。
昼の弁当スポンサーがなかった代わりに、夜の懇親会が大量にあったのがよかったです。
LT の発表は通訳の人との打ち合わせがない分、気軽だった代わりに、ちゃんと時間で切られる LT が久しぶりだったので、非常に緊張しました。 そして、実際には練習より早口だったのか、予定よりたくさん喋っても時間が余ってしまうという感じになってしまいました。 ネタが多い分には、後でRuby関西で使うなりブログに書くなり、他の発表場所はあるので、今後機会があれば多めに用意して調整できるようにしてみたい、と思いました。 日本語での発表でも、発表資料は頑張って英語で書いておいたからか、英語での反応があったのが嬉しかったです。 基本的に何か反応があると嬉しいものなので(承認欲求)、発表とかしないという人でも、積極的にいいねをしたり、スターをつけたりすると良いと思います。
開催の頭が連休だと、休みの部分を観光に、と思っても、期間中に出て来たオススメ情報を活用できないので、来年のように、後に休みが続く方が嬉しいと思いました。 今回の場合は、台風で日曜に観光は無理だったということがありましたが、広島平和記念資料館に無料で入れるという特典が、前日に知らずに行っていたら無駄になる、ということがおきたのではないかと思いました。
基本的に人を覚えるのが苦手なので、今回会った人で、次回忘れていたらすみません。
Workshop は気になったのと、普通の発表は後で録画をみることもできる、というので、できれば参加したかったのですが、 https://github.com/rubydata/rubykaigi2017 の事前準備をしようとしたら、前日に止まっていたホテルだと帯域制限らしき状態になってしまって、 rake docker:pull
できなかったということもあって諦めました。 Afternoon Break の時間に重なっていた方も、 LT の前の接続確認をする必要があったので、諦めました。
LT の接続確認は、なぜか最大化したときの表示がリサイズ前の範囲しか表示されない、という現象がなおらず (以前は次のページに移動したり、一覧ページに移動したりすればなおっていた)、発表準備期間中に n0kada さんが rabbit の方に終了時に落ちる (正常終了ではなく SEGV 的な終わり方をする) のを報告していたので、同じ macOS っぽいとわかっていたので、どうしていたのかきいてみたら、 rabbit -S 1920,1080 hoge.md
のように -S
で画面サイズを指定して起動すれば良いと教えてもらって解決しました。ありがとうございました。
まとめ
色々と大変なこともありましたが、大きな問題もなく、無事終わって良い RubyKaigi でした。