5月15日から17日の3日間、RubyKaigi 2024が沖縄県那覇市で開催されました。
タイミーには世界中で開催されるすべての技術系カンファレンスに無制限で参加できる「Kaigi Pass」という制度があります。今年もこの制度を活用してタイミーから総勢12名のエンジニアが参加しました。
前回に引き続き参加レポートの2回目として、各エンジニアが印象に残ったセッションの感想をお届けします。
YJIT Makes Rails 1.7x Faster
ShopifyでYJITの開発をしているk0kubunさんによるRuby 3.2から3.3(と3.4)にかけてのYJITの進化についての発表でした。YJITはRubyの実行速度を向上させるJITコンパイラで、Ruby 3.3ではいくつもの改善が施されています。 その中でも特にパフォーマンス改善にインパクトがあったものとして強調していたのが、メソッド呼び出しのフォールバック、例外ハンドラのコンパイル、スタックに置いていた値のレジスタ割り当て、メソッドのインライン化の4つでした。 こうした地道な改善の積み重ねにより、Ruby 3.3のYJITはRubyプログラムのパフォーマンスを10-20%も向上させることができるようになったそうです。今後のRubyの高速化に期待が持てるセッションでした。
3月末にRubyを3.3.0にバージョンアップした際にYJITのことを少し調べていたので自分ごととして聞けたセッションでした(タイミーではYJITはRuby3.2のときに有効化しています)。 リリースノートのYJITの"Major performance improvements over Ruby 3.2"というセクションにも目を通してはいたのですが、そのときは「なんかいろいろ最適化して速くなったんだろうな」くらいの気持ちで読み流していました。 このセッションでリリースノートの1行1行の背後で具体的にどういう改善を行ったのかがわかりやすく解説されたおかげで、自分の中でYJITに対する理解が少し深まった気がします。 同じくYJITに関する@maximecbさんの「Breaking the Ruby Performance Barrier」も聞いていて、YJITを最大限活用するようなコードの書き方やgemの選定といった観点がパフォーマンス文脈では重要になるかも?などと思ったりしました。 が、本来はあまりそういうことを意識しなくてもふつうにRubyのコードを書いてYJITが有効だったら速い、が理想な気もしますし、YJIT開発チームの精力的な活動を見ていると自分の想像を超えるようなブレイクスルーが起きる気もしていて今後が楽しみです。
(須貝)
RuboCop: LSP and Prism
セッションの大きなテーマは以下の二つでした。
- Rubocop and LSP
- Rubocop and Prism
「Rubocop and LSP」ではLSPがどう実装されているかやVSCodeやvimなどのEditorでどう利用されているか事例などの話がありました。加えて、LSPを利用しないと毎回CLIなどでコマンドを叩かないといけないが、LSPを利用するとその流れが省略されるのでフィードバックが早いメリットがあることや、VSCodeでは既にYJITを利用している話もありました。
メリットなどの話もありましたが、LSPの対応で新しく発生したイシューもありました。CLI上では作業を完了してからコマンドを実行するので問題ないが、Editorだとどのタイミングで作業が終わったのか判断が難しいというところです。現在Parser Gemではエラー耐性設計(Error Tolerance)をサポートしてないのでこの問題が解決できないが、それをサポートしているのがPrismで、「Rubocop and Prism」で関連の話がありました。
「Rubocop and Prism」ではどのタイミングで作業が終わったのか判断が難しい問題を解決するための説明やどういうメリットがあるかなどの話がありました。Prismではエラー耐性設計(Error Tolerance)をサポートしているため、作業が終わるタイミングの判断の問題が解決できるかつ、Parsingスピードが速くなるらしいです。
最後には現在はParser Gemのインターフェースを利用してPrismを利用しているが、将来的にはそれを無くして直接Prismを利用したい話、Prismを利用すると得られるメリットの話でセッションが終わりました。
rubocopがなんとなくパーサーで動いているんだろくらいは考えていたがどういう原理や技術で動いているか分かってなかったので、話を聞くだけでも非常に勉強になりました。現在vimを利用していて、素早くrubocopの結果がEditor上で表示されるのは非常に助かっていますが、それがLSPのおかげだということがわかりました。まだPrismではエラー耐性設計を対応してないので、すぐprismを利用するのは難しいかと思いますが、対応出来次第もっと速くなったrubocopを利用するのが楽しみです!
From LALR to IELR: A Lrama's Next Step
概要
RubyのParserであるLramaは次にIELR化に取り組むよという話。
課題
現行のLramaのParserはLALR Parserであるが、Lexerの状態管理に関して課題がある。
事例として
p 1.. || 2
が正しくパースできない問題は、 シンボルを"||"
と解釈するか"|" + "|"
と解釈するかの問題に起因しており、これが文脈に寄って異なるというLexerとParserの複合問題である。これを現状のLALR Parserで対応しようとすると当該Parseロジックに個別カスタム処理を書く必要があるが、構文を管理するparse.yは既に現時点で16kLを超えており個別カスタム対応には限界がある。
解決策
この問題はLexerとParserを直列に動作させていることに起因しており、LexerとParserが相互に連携しながら解析することで解決可能である。
これを実現するアルゴリズムがPSLRアルゴリズムである。
実装
PSLRアルゴリズムは Canonical LR parser で実装可能であるが、 Canonical LR parser は動作が重たいという問題がある。
LALR Parser は Canonical LR parser を軽量化したものであるが、軽量化の代償としてPSLRアルゴリズムを実現するのに必要な表現能力を失っている。
そこで両者のいいとこ取りをしたIELR Parser というものがある。
着想としては高速な LALR Parser を基本としつつ、表現能力を超えた場合に Canonical LR parser を動かすと言ったシンプルなものもの。
感想
Parserが順当に進化しているなという所感。
言語工学の基本としては字句解析と構文解析は直列に動かすものですが、実際の課題としてそれでは解けない問題が存在するというのは興味深かったです。
LR Parserの改善の文脈でありLR Parserの知識があればベターですが、実際には字句解析処理の文脈依存性の問題です。非常に豊富な文法をもつRubyらしい課題であり、他言語ではなかなかココまで言語工学の研究に踏み込むことは無いのではないか思われます。
問題の複雑さに対して、実際の解法はシンプルに既存の技法を組み合わせるものであり、これは言語工学における先行研究の成果物の豊富さを物語っていると感じました。
(@Kazuki Tsunemi - tsunemin)
Cross-platform mruby on Sega Dreamcast and Nintendo Wii
概要
mrubyをドリームキャストとWiiで動かしてやったぜという内容。
なおスライド自体もWiiを動かして表示していた。
ドリームキャストは2020年、Wiiは2023年にmrubyで正式サポートされている。
なぜこの2つを選んだかと言うとドリキャスとWiiをスペック対決させるため。両者はハードウェアアーキテクチャからSDKに至るまでが全く異なるのだが、ともにFreeSDKがあるので採用された。
実機デバッグは非常に辛いものがあるので、エミュレータが非常に助かるなどの苦労話があった。実機で動作デモも行われた。
感想
登壇者のSEGA愛がすごい。
SEGAロゴデザインのRUBYシャツ(多分自家製)を着て現れた時点ですべてを察しました。Wiiは如何にドリームキャストがすごいのかと伝えるために引き合いに出された感じもあります。
mrubyのv3.3.0のリリースノートを見るとNintendo Wii、Dreamcastがきっちり記載されています。(AndroidやMS-DOSと同じ並びにあることで異様さが際立つ)
ゲーム機は汎用PCとは違って独特のハードウェア構成をしており、特にVRAM周りの深い見識がないと動作させるのは難しいためハードウェアの話がメインになりました。
ドリームキャストはビジネスとしてはヒットこそしませんでしたが、技術としては凄いものがあったらしくハードウェアオタクの心を未だに掴み続けているようです。尺の都合でハードウェアの深い話にまでは踏み込まず、使われている技術の違いの話がメインでした。
エミュレータ開発を含めてゲーム機ハック界隈は独特の文化が醸成されていますが、ローレベルプログラミングの深淵を除きたい人はいい窓口かもしれません。
Rubyでやる理由は謎に思いつつ、Rubyでなければマージされないような気もします。
それだけRubyが「懐の深い言語」ということでしょうか。
備考
Ruby Conf in Taiwanで同様の発表が行われていた模様
(@Kazuki Tsunemi - tsunemin)
Finding Memory Leaks in the Ruby Ecosystem
メモリデバッグやメモリリークの検出に使われるValgrindをRubyプログラムに適用して、メモリリークを検出するアイデアを実現した発表でした。
ValgrindをそのままRubyプログラムで用いるとRuby自体がシャットダウン時に全てのリソースを解放しないため、Valgrindのレポートで数千のエラーが誤検知され調査が困難な状態でした。そこで、 ValgrindのXML出力オプションを有効にし全てのエラーを標準出力し、その結果を1つずつ解析してRuby自体のメモリリークではなく、Rubyプログラムのメモリリークを検知できないかというアイデアから生まれたのが [ruby_memcheck](https://github.com/Shopify/ruby_memcheck)
です。
ruby_memcheckでは、Valgridの出力したエラー1つずつのリークされた各メモリ割当てのスタックトレースを走査し、Ruby起因のエラーかネイティブGem起因なのかを確認しています。実際にこのツールによってnokogiriやprotobufといったGemでメモリリークを検知し、修正されたそうです。アイデア実現のアプローチも面白かったのですが、個人的にはその後が興味深かったです。
彼らはそこで終わらず、そもそもRubyインタプリタが終了する際に確保されたメモリ全てを解放してくれればメモリリークの検知は今より容易になるということで問題提起を行い、Ruby3.3から RUBY_FREE_AT_EXIT
という機能が導入されました。このオプションを有効にすると、Rubyインタプリタ終了時に確保されたメモリを解放するため、より効率的にメモリリークの検出ができると言うものです。これによって最新の ruby_memcheck
のメモリリーク検出のコードもスッキリしているのも良かったです。
Day3のMatzのKeynoteで触れられていた様々な側面のパフォーマンスの話のように、YJITとRubyアプリへの直接的なパフォーマンス改善だけでなく、今回のようなメモリリークの検出を手助けする開発者体験向上文脈のRubyの発展の一例を知れて面白かったです。
今回の発表者の1人であるPeter Zhuさんが、最近RubyのGCのチューニングに使える示唆を与えてRailsアプリを高速化をサポートするAutotunerというGemに関する記事を公開していたので早速使ってみたいなと思っています。
(ぽこひで)
https://blog.peterzhu.ca/assets/rubykaigi_2024_slides.pdfblog.peterzhu.ca
最後に
パート1に引き続き、興味深いセッションが並びました。
あらためて、スピーカーの皆様に感謝をお伝えさせていただきます。貴重なお話をありがとうございました!
RubyKaigi 2024への参加で得たさまざまな知見を「タイミー」の開発にも活かしていきたいと思います。
Written by:須貝, @JinsuKim26, @Kazuki Tsunemi - tsunemin, ぽこひで