🏖️

RubyKaigi2024で印象に残ったセッション

2024/05/25に公開

はじめに

先日RubyKaigi 2024 in 沖縄に一般参加しました。初めてのRubyKaigi参加でした。

ある程度私にも理解できて、かつ印象に残った9セッションの概要と感想を書きます。余談はトグルで折りたたんでおきました。

Rubyの使用歴は5年くらいですが、Ruby自体の内部仕様には全く詳しくありません。アルゴリズムと語学が好きなので (得意ではない😭)、アルゴリズムや英語セッションの話がやや多めです。

参加のきっかけ

参加したきっかけは、Ruby on Railsを使った開発歴も5年くらいになったので、コミッターの発表を見て更に言語知識を深めたり、日々の開発のモチベーションを高めたかったからです。

と言いたいところですが、正直に言うとノリです。前前前職で一緒だったエンジニアの友人2人とオールナイトでマリオカートをしていたら "RubyKaigi沖縄だから行くしかない!" という話になり、しかもそのタイミングでJALセールがちょうどやっていたので、航空券とRubyKaigiのチケットを購入して参加が決定しました。

そんな軽薄な動機で参加したRubyKaigiでしたが、とても楽しく勉強になる3日間でした。

Day 0

Day 0

RubyKaigi前夜に那覇に着きました。那覇空港の手荷物受取所にはRubyKaigiのポスターが貼ってありました。写真撮ってる人は絶対RubyKaigi参加者だよな、と思って声を掛けたくなりましたがやめました。掛けてみれば良かった。

夕飯は、定番かつ家庭的な沖縄料理をいただきました。店員さんが気さくにお話してくれて、ジーマーミー豆腐の "ジーマミー" は "地豆" (ピーナッツ)が由来とのこと。

https://maps.app.goo.gl/tZFQHg7bfPQCX2Bt8

Day 1

Writing Weird Code

スライド:
https://drive.google.com/file/d/1Dkx15u_5UAGoFqJHCeAuj2FXS-z_U7EE/view

タイトル通り、奇妙なRubyコードが沢山紹介されました。会場がどっと沸いて盛り上がったキーノートでした。
実行するとアニメーションになるRubyコードや、.bmpで一見画像に見えるけれど実は実行できるRubyコード、短くて奇妙なコードなどが紹介されました。

https://x.com/MikeMcQuaid/status/1790567129567334847

アニメーションはQuine (自身のソースコード自体を出力するプログラム) で書かれています。正直仕組みはほぼ理解できていないですが (GitHub Copilotに説明させようとしてみましたがもちろん無理でした)、sin, cos, 複素数によって回転する動きを実現したり、 Time.now を上書きすることで実現しているようです。Rubyはモンキーパッチしやすい言語、とおっしゃっていました。

各コードは発表者のtompngさんが公開しているので、ぜひ手元で実行してみてください。数十行のRubyコードを実行するだけで、こんなに複雑なアニメーションを実現できるんだ…と驚きます。例えばkurage.rbは、クリックするとその位置を避けて動きます。

https://github.com/tompng/selftrick2024/blob/main/dangerous/kurage.rb

ちなみに BEGIN というblockを書くと、そこからRubyコードは実行される、ということを初めて知りました。

奇妙なコードを普段から書くことで、OSSにコミットするときに奇妙な挙動やコーナーケースに気づけたり、言語の理解が深まる、とのことでした。

The grand strategy of Ruby Parser

スライド:

当日はParser自体が何なのか?すら理解していなかったので難しかったのですが、この記事を書くときに復習してみたら、発表者の金子さんの記事や昨年のスライドがわかりやすかったです。
https://gihyo.jp/article/2024/01/ruby3.3-lrama
https://speakerdeck.com/yui_knk/the-future-vision-of-ruby-parser?slide=8

ざっくり表現すると、RubyのParserは、Rubyのスクリプトを解析して、AST (Abstract Syntax Tree: 抽象構文木) に変換するものです。

Rubyの表現力や自由度は高いですが、それゆえに構文解析は大変なのだな、と分かりました。
例えば || はORとして使われることが一般的ですが、Rubyだと省略されたブロック引数としても使えます。

10.times do ||
end

lexer (字句解析器) は通常ステートレスですが、このような判定には lex_state というステートが必要になるとのこと。しかしできれば状態管理はParserにさせたくないとのことでした。

C言語で実装したParserを色々な言語で使っているが、本当はその言語用のParserはその言語で書くべきではないか (RubyのParserならRubyで書くべきではないか)、という考えがあるそうです。Ruby 3.4ではUniversal Parserを完成させたい、とのことでした。

Strings! Interpolation, Optimisation & Bugs

8行削除しただけでstringクラスの処理を2倍高速化できた、というお話でした。RubyKaigiのセッションでは珍しく、抽象的な教訓にまでに落とし込んだプレゼンだったので、普段の開発に活かせそうな考え方を知れて面白かったです。

実際のPRと該当コードはこちらだったと思います。
https://github.com/ruby/ruby/pull/6965/files#diff-430d86fdb6c4a558ab0f1b6648bbfae1720e8bde84f026e95a52740014752040L3356-L3364

マジックナンバーとして条件分岐に使われていた MIN_PRE_ALLOC_SIZE 48 という数字は、書かれた当時のマシンスペックをカバーするためのものだと判明しました。今はもう必要ないと分かったので、シンプルにマジックナンバーと条件分岐を削除したら、それだけで処理が2倍高速化したそうです。

string.c
#define MIN_PRE_ALLOC_SIZE 48
// ...
    if (LIKELY(len < MIN_PRE_ALLOC_SIZE)) {
        str = rb_str_resurrect(strary[0]);
        s = 1;
    }
    else {
        str = rb_str_buf_new(len);
        rb_enc_copy(str, strary[0]);
        s = 0;
    }

string.c
    str = rb_str_buf_new(len);
    str_enc_copy_direct(str, strary[0]);

教訓として "小さい変更が大きな成果になり得る" と同時に、裏返せば "どんな変更でも予期せぬ影響になり得る" とおっしゃっていました。
また "常に思い込みを疑うべき"、 "多様性のある考え方で問題を解決できることがある" ともおっしゃっていました。

Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜

スライド:
埋め込みのspeakerdeckより、slidesの方がフォントが化けていなくて見やすいそうです。
発表者kokuyouさんのZenn記事はこちら

rbs-gooseというツールを開発して、LLMでRBSの型推測を試みた、というお話でした。発想が面白いなと思いました。

この日の前日5月14日にちょうどOpenAI GPT-4oが発表されていました。他社モデルも含めて各LLMでRBS生成結果を比較したら、OpenAI GPT-4oは最速なのに理想的な出力で、更にコストも安く、非常に優秀な結果だったとのことでした。本題から少し逸れますが、GPT-4oを今すぐ使わなければ!という気持ちになりました。

Day 2の発表 "Embedding it into Ruby Code" のrbs-inlineを使って、インラインコメントとして型を書くようになったら、GitHub Copilotが補完を上手くしてくれそうなので、今回作ったRBS gooseはもしかしたらいらなくなるかも…でももう少し開発を続けてみたい、とのことでした。

ごはん

ごはん

Lunch

韓国料理屋さんで冷麺を食べました。
https://maps.app.goo.gl/Zprw5mAGM8BCoyZE6

Dinner

前職 (かつ副業先) つながりの友人2人と楽しく5時間ほど飲み続けていたら、写真を撮り忘れました。海鮮がおいしい居酒屋でした。
https://maps.app.goo.gl/4CW2fYpAKgmJ8ZX79

Day 2

Finding Memory Leaks in the Ruby Ecosystem

スライド:
https://blog.peterzhu.ca/assets/rubykaigi_2024_slides.pdf

内容は以下で、とてもC言語なセッションでした。実際のIssueはこちら

  • Ruby自体のメモリリークを防ぐために、終了時にメモリを解放する RUBY_FREE_AT_EXIT という環境変数をRuby 3.3で追加した
  • その環境変数 RUBY_FREE_AT_EXIT を有効にすることで、native gem開発者用のメモリリーク検出gem ruby_memcheckで、natvie gem自体のメモリリークを効果的に検出できるようになった
    • nokogiri, liquid-c, protobuf, gRPC などのメモリリークを実際に検出した

native gemのコントリビューターなら RUBY_FREE_AT_EXIT を使ってメモリリークを見つけてね!とのことでした。

メモリリークの例として挙げられたCコードは以下です。ループ内でメモリを割り当て続けます。

int main() {
  while(1) {
    char * name = malloc(256); // Allocate in Loop
    if(fgets(name, 256, stdin) == NULL) {
      break;
    }
    printf("%s", name);
  }
}

これを以下に修正すれば、ループ外のメモリ割り当て & メモリ解放になり、メモリリークを防げます。

int main() {
  char * name = malloc(256); // Allocate once
  while(1) {
    if(fgets(name, 256, stdin) == NULL) {
      break;
    }
    printf("%s", name);
  }
  free(name); // Free before exit
}

gem ruby_memcheckでは、メモリリーク検出にValgrindという検出ツールを使っています。ただRuby自体のリークが多く検出されるため、native gem自体のメモリリークはどれか?が分かり辛かったそうです。

そこでRuby 3.3に RUBY_FREE_AT_EXIT 環境変数を導入して、 export RUBY_FREE_AT_EXIT=1 で有効にすれば、Rubyはプログラム終了時にメモリを解放するようになりました。先ほどのCコードの例で言うと free(name); // Free before exit を行うイメージです。
これによりRuby自体のメモリリークが大幅に減少して、native gem自体のメモリリークはどれか?の特定が簡単になりました。

最終日のMatzキーノートで、Matzが "この2人はパフォーマンス改善に大きな貢献をしてくれた" と言及していました。

Breaking the Ruby Performance Barrier

YJITの登場によりRubyの処理が高速化した、というのは他のセッションでも触れられていて周知の事実です。このセッションは、YJITでRubyが高速化したことにより、今までC言語で書かれがちだったgemもRubyで書いていける、という内容でした。

Rubyで実装されたgem (pure-Ruby gem) の例として、以下が挙げられていました。

例えばRubyで書かれたprotobufはまだ実験段階のgemだそうですが、Cで書かれたGoogle本家のprotobufより9倍も速いそうです。YJITが入る前は7倍遅かったそうです。YJITすごい。

Rubyでgemを実装する良さとして、YJITでCに近いパフォーマンスが出せることはもちろん、便利なstring methodsが多いことも挙げられていました。パフォーマンス重視のgemはCで実装しなければ、という常識が変わった、とのことでした。

Squeezing Unicode Names into Ruby Regular Expressions

Unicodeの名前 (Unicode Name Property) をそのままRuby正規表現に使えないか?そうするときにどうやってメモリを節約するか?というお話でした。まずその発想がなかったですし、Trieが出てきたりとてもアルゴリズム的な内容で楽しかったです。

Unicodeの名前 (Unicode Name Property) というのは、例えば絵文字 🤬SERIOUS FACE WITH SYMBOLS COVERING MOUTH 、ひらがな HIRAGANA LETTER A という名前でUnicode登録されていて、その直感的に理解できる文字列のことを指します。

現状Rubyでは、例えば /あ//\u3042/ ( のUnicodeは U+3042) は有効な正規表現です。

// =~ "とらいあすろん"
=> 3

/\u3042/ =~ "とらいあすろん"
=> 3

しかし /HIRAGANA LETTER A/ と書くことはできません。これを実現するには HIRAGANA LETTER A という文字列を U+3042 に何とか変換する必要があります。Unicodeの文字種類は多いですし、Unicodeの名前はとても長いこともある (80字以上のものもある) ので、メモリをうまく節約したいです。

そこでTrieを使います。例えば、 SERIOUS FACE WITH SYMBOLS COVERING MOUTH 🤬 に1番近いUnicodeの名前は SERVICE MARK で、近い5個の文字を1つのエントリーにまとめることでメモリを節約するそうです。Twitterで "百人一首の決まり字" と表現している方がいて上手いと思ったのですが、 SER の次の文字が決まれば名前を特定することができます。

またTrieの葉から、sparse nodes (子を持たないノード) を減らすことで、全bitを有効に使うアプローチなども紹介されていました。

最後に "Unicodeの名前を正規表現に使いたかったら教えてね!" とおっしゃっていて、今更感が面白かったです。たしかに需要が気になりますよね。

ごはん

ごはん

Lunch

ノンアルコールのルートビアで有名なA&W。湿布を食べたことはないですが、ルートビアは湿布の味がしました。
https://maps.app.goo.gl/E6NKLueHxhzFrRHn6

Coffee

パンナコッタとアイスコーヒーで休憩しました。
https://maps.app.goo.gl/3AbqP1c6XpaMhKu48

Dinner

沖縄そばも食べられるキムチ屋さんに行きました。
https://maps.app.goo.gl/FhssJKhK6tmBNkrs8

Day 3

Speeding up Instance Variables with Red-Black Trees

Ruby 3.4で、インスタンス変数へのアクセスの高速化を、Red-Black Trees (赤黒木) を用いたキャッシュで行った、という内容でした。実際のPRは確かこちらだったと思います。
https://github.com/ruby/ruby/pull/8744

赤黒木をそもそも知らなかったのですが、このYoutube動画がとても分かりやすかったです。4分で学べます。
https://youtu.be/qvZGUFHWChY?si=mU4-plKyyqsbayVt

赤黒木は二分探索木の一種で、各ノードに赤または黒の色が付いています。以下のような条件があり、それによりself balancing (自己平衡) が実現されます。木のバランスが保てているため、検索が簡単という良さがあります。

  • 赤ノードは赤の子を持たない。黒の子のみ持つ
  • 葉ノード(末端のノード)はいつも黒
  • 任意のノードからその子孫の葉ノード(末端のノード)までの全パスで、黒ノードの数は同じ

Ruby 3.4では、インスタンス変数が既に定義されているか?を赤黒木を用いたキャッシュで確認していて、これによりキャッシュミスの場合のパフォーマンスが良くなるそうです。

  • Ruby 3.2: インスタンス変数にキャッシュがそもそも使われていなかった
  • Ruby 3.3: キャッシュが使われるようになり、キャッシュヒットの場合のパフォーマンスが良くなった。ただ赤黒木を使えていなかったので、キャッシュミスの場合のパフォーマンスはあまり改善しなかった
  • Ruby 3.4: 赤黒木が使われるようになり、キャッシュミスの場合のパフォーマンスも良くなった

だからRuby 3.4は速いですよ!とのことでした。2024年5月25日現在、まだRuby 3.4.0はpreviewなので正式リリースが楽しみです。

Matz Keynote

和やかだけれど歴史の重みを感じたMatzのキーノートでした。Rubyの会議だと思ったらCの会議だったみたいな感じですよね、と最初におっしゃっていて、初参加でまさにそう思っていた私はほっとしました。

Matzが思うRubyの良さは以下とのこと。

  • 書いていて楽しい。言語仕様が理不尽で叫ぶようなことは他の言語より少ないと自負している
  • スタンダードの機能が多くて、こういうmethodがあったら良いのに、と思うmethodが全部あること。かゆいところに手が届く
  • 制限が少ない

Day 1のセッション "Namespace, What and Why" のnamespaceが入ったら、バージョンをRuby 4.0にしても良いかも、とのことでした。

ちなみにスライドには、ウサギとカメでスライド進捗と経過時間を表現するRabbitというRuby製のプレゼンツールが使われていて、可愛いし実用的なので使ってみたくなりました。

ごはん

ごはん

Lunch

飲んだあとの締めステーキで有名なステーキヒカルを、あえてランチに食べました。
https://maps.app.goo.gl/rMKsaioKNkgtDMLKA

Coffee

黒ごまサーターアンダギー。お土産も買えておすすめです。

https://maps.app.goo.gl/iJu5WFdzNBBSqYj9A

Dinner

会場目の前のおしゃれなハンバーガー屋さんで、オレンジハラペーニョバーガーを食べました。

https://maps.app.goo.gl/65fLM9eEcagQvwMh9

Day 4

Day 4

散歩して海を見に行って、タコライスを食べて、空港でお土産を買って & 駆け込みで冷やしぜんざいを食べて帰宅しました。

https://maps.app.goo.gl/fr176XW8TRpEZEX9A

RubyKaigi初参加の感想

予習&復習までがRubyKaigi

今回はノリでRubyKaigiに初参加して、まずはRubyKaigiの雰囲気やレベル感を知れたので良かったといえば良かったですが、終始セッションに理解が追いつかないもどかしさがありました。Rubyで普段開発していても、必ずしもRubyの内部的な言語仕様自体に詳しくなるわけではないので、予習はした方が楽しめると思います。せめて頻出の単語の意味を調べておくと良さそうです。

一方で復習の観点だと、つたない自分のメモからこの記事を書くのには時間がかかりましたが、セッションのスライドを見返したり、色々な参加者の記事を読むことで、当日よりも内容を理解することができてとても良い勉強になりました。

来年は愛媛県の松山で開催ということで、ロケーション的にはぜひ行きたいですが、やはりRubyKaigiの本体はセッション。せっかく参加するなら今回よりRuby自体に詳しくなった状態で、かつ各セッションを予習してから参加したいです。

体力勝負、時間もない

意外と休憩時間が短いですし、1,000人以上の参加者がいるので、会場にいるだけで体力は消耗します。個人的に会場の匂いが車っぽくて、少し酔ったりもしました。
またセッションがどれも難しくて、集中して聞かないとすぐ置いていかれる緊張感もあるので、3日間の全時間帯のセッションを聞くのはかなり厳しいと思います。体力おばけじゃなければ、ある程度セッションを諦めた方が良さそうです。

隙間時間に少しは仕事できるかなーと予想していたのですが、実際は時間も体力も全然余裕がありませんでした。参加前に周りの同僚にカバーをお願いしておいて、RubyKaigi中はRubyKaigiに集中する、が1番良さそうです。会期中ものすごくカバーしてくれた同僚には感謝しきれません。

私は時々会場近くのホテルに帰って休憩したり、午前よく寝て午後から参加したりと気ままに過ごしていましたが、その結果無事に3日間乗り切れて良かったです。周りの友人は数人体調を崩していて辛そうでした。

英語はできた方が良い


参加者の出身地マップ

日本語と英語のセッションは半々くらいで、翻訳機も用意されているので、英語は必須ではありません。ただ、スライドは日本語セッションでも英語表記で統一されていますし、英語話者がほとんどのグローバル企業のブースがあったり、海外のRuby開発者やコミッターも多く参加していて交流チャンスがあるので、英語はできるに越したことがないです。なにより英語セッションをそのまま聞けるのは、煩わしさが無いです。

私は英語を使うのは好きなので、その点でもRubyKaigiは楽しめました。でも毎日私の英語ひどいなーとウケながら生きているので、来年もっと上手くなって参加できたら嬉しいです。

ごはんやイベントの戦略

会場周辺のランチはとても混み合います。そしてみんな開発者で情報通なので、Google Mapで高評価のお店はどこも行列でした。昼休み開始直後に会場を足早に出れば人気店にも入れたりしたので、ランチは早めに動くことをおすすめします。

今回私は色々な友人と内輪でごはんに行ったり、孤独のグルメをしたり、シンプルにイベントの応募に乗り遅れたりで、オフィシャルイベントには全く参加しませんでした。オフィシャルイベントにも次回は参加してみたいです。
アフターパーティーは外から少し覗いたのですが、とにかく人数が多い (900人!) ですし、登壇者など錚々たる方達がカジュアルに飲んでいて怯みますが、誰か1人でも友人/知人がその場に参加していると入って行きやすそうな印象でした。

Discussion