メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックカンパニーのエンジニアブログです。読者に有用な情報発信ができるよう心がけたいので応援のほどよろしくお願いします。

RubyKaigi 2023 「Reading and improving Pattern Matching in Ruby」 感想

こんにちは、サーバーサイドエンジニアの草分です。
先日のRubyKaigi 2023に参加された皆様お疲れ様でした!

"感想記事を書くまでがRubyKaigi" ということで、今回は1つのセッションを掘り下げた感想記事を投稿します。

rubykaigi.org

このセッションではRubyのパターンマッチの機能を題材に、Rubyの機能の実装を「読んで理解する」そして「パフォーマンスを向上させる」といったプロセスを、いかにして進めていくのか。その方法について紹介されていました。

このセッションはRubyのパターンマッチやメタプログラミングを知った状態で聞くと、より深く理解することができます。 それらの前提知識を軽くおさらいしつつ、セッション内容を振り返っていきましょう。

パターンマッチとは

パターンマッチとは、「データ構造による条件分岐」「構成要素の取り出し」という要素を備えた機能です。

Rubyでは case/in 構文で記述します。
対象のオブジェクトとマッチさせるデータ構造(パターン)を比較し、マッチした場合処理が行われます。

また、マッチした場合はパターンに記載した各変数に値がバインドされ、内部の処理で利用することが可能となります。

User = Struct.new(:role, :name)

def hello(user)
  case user
  in role: 'member', name:
    puts "ようこそ、#{name}さん"
  in role: 'guest'
    puts "ようこそ、ゲストユーザーさん"
  else
    puts "ようこそ"
  end
end

hello(User[:member, "太郎"])  # => ようこそ、太郎さん
hello(User[:guest, "ゲスト"])  # => ようこそ、ゲストユーザーさん
hello(nil)                   # => ようこそ

Arrayパターンについて

複数あるパターンマッチの型の内の1つで、オブジェクトの #deconstruct が配列を返す場合、その配列と指定パターンがマッチするかを検査する機能です。

class Array
  def deconstruct
    self
  end
end

case [0, 1, 2]
  in [0, *a, 2]
    puts a #=> 1
end

※実際にはArray#deconstructはデフォルトで利用可能です

その他のパターンマッチの使い方については過去に解説記事を書いています。 こちらも是非ご覧ください。

tech.medpeer.co.jp

tech.medpeer.co.jp

Arrayパターンを改善していく

Rubyのソースコードは構文解析などによりAST(抽象構文木)に変換され、そこからYARV命令列に変換され実行されます。
実際にソースコードがどう変換されるかは RubyVM::InstructionSequence を使うと簡単に表示することができます。

puts RubyVM::InstructionSequence.compile("puts 2 + 2").disasm
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,10)> (catch: false)
0000 putself                                                          (   1)[Li]
0001 putobject                              2
0003 putobject                              2
0005 opt_plus                               <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
0007 opt_send_without_block                 <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0009 leave

パターンマッチのソースコードがどういうYARV命令に変換されるかを確認すると… とてつもない量の命令が出力されてしまったとのこと。
(本当に量が多かったので本記事では省略)

落ち着いて、ASTをYARV命令列に変換しているcompile.cファイルを見てみましょう。とのこと。
パターンマッチの部分には素晴らしいコメントが記載されており、理解の手助けになるようです。果たして本当でしょうか。

compile.cは1万行をゆうに超える上に、ASTノードの変換を行う関数 iseq_compile_each0 には100近い分岐のswitch文が存在し、それぞれの処理を行っています。

いやはや、何の知識もない状態で読んでも全く理解できないことでしょう。 実際にファイルを開いてみます。 https://github.com/ruby/ruby/blob/8d242a33af19672977dcdcb8d32e9ad547bc0141/compile.c#L6363

compile.c

おお!読めるぞ!?(一部分のみ)

さすがに実際のコードはすぐに読み取れる訳ではありませんが、 読む範囲が定まっており、読みやすいコメントが記載されていれば、なんだかいけそうな気になりますね!(気のせいかも)

改善方法について

Array patternでパターンマッチする場合、検査対象が#deconstructを持つかどうか、その戻り値がArrayかどうかのチェックが実行されます。
しかし対象がArrayの場合は#deconstructの検証は不要なはず、その処理をスキップすれば速度改善になるのではないか。

といった仮説から改善が行われました。

結果、ベンチマークでは速度が2倍に向上!やった!
しかしながら、Array#deconstructがオーバーライドされているケースで問題があったとのこと。

オーバーライドによる問題点

Rubyには「オープンクラス」という機能があります。 既存のクラスを任意の場所で再オープンし、メソッドの修正や追加を行うことができる機能です。 組み込みクラスのArrayも例外ではなく、既存メソッドの挙動を好き放題書き換えることができます。

オープンクラスについてはメタプログラミングRubyなどの書籍を読むと詳しく理解できるでしょう。

前述の改善方法は、Array#deconstructの内容の検証をスキップするものです。 オープンクラスによりメソッドが書き換えられていた場合、検証をスキップすると期待値と異なる動作をしてしまいますね。

さて、その問題に対してはどう対処するかというと……

Do not override Array#deconstruct !

といったオチに繋がるセッションでした。

感想

コーナーケースへの対処の難しさ

Array#deconstructの上書きですが、普通にRubyを使っている場合はおそらく書かないコードでしょう。
Rubyの利用者からすればあまり考慮する必要はなさそうです。

一方で、Ruby言語としては許されている実装です。
言語の改善のためにはそういったコーナーケースへの対処も必要なようです。

「これらを全て考慮するのは大変そうだぞ……」という気分になってしまいますね。

Array patternはArrayだけのものでない

パターンマッチのArray patternはArrayだけのものではありません。
以下のように、#deconstructが定義済の任意のオブジェクトで利用することが可能です。

Spot = Struct.new(:latitude, :longitude)

def latlng(spot)
  case spot
    in [0.. => lat, 0.. => lng]
      puts "北緯#{lat.abs}度 東経#{lng.abs}"
    in [..0 => lat, 0.. => lng]
      puts "南緯#{lat.abs}度 東経#{lng.abs}"
    in [0.. => lat, ..0 => lng]
      puts "北緯#{lat.abs}度 西経#{lng.abs}"
    in [..0 => lat, ..0 => lng]
      puts "南緯#{lat.abs}度 西経#{lng.abs}"
  end
end

latlng(Spot.new(35, 135))   # => 北緯35度 東経135度
latlng(Spot.new(-35, -135)) # => 南緯35度 西経135度

この場合のパフォーマンスはどうなってしまうのでしょう。 チェック処理が増えた影響はあるのでしょうか。 手元での検証はできていませんが、ひとつの事柄を掘り下げていくと次の疑問点も見えてきますね。

おわりに

RubyKaigiのセッションはRubyの内部に踏み込んだ内容が多く、前提知識がないと理解すらままならないこともあります。 ということで今回は、1セッションの前提知識の内容も含めた記事を投稿いたしました。 今後のRubyKaigiのセッションを理解するための一助になれれば幸いです。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023 セッションレポート Day3

こんにちは!サーバーサイドエンジニアの伊藤(@yuma_ito_bd)です。

RubyKaigi 2023に参加されていた皆さん、お疲れ様でした。 3日目(5/13)のセッションの中で印象に残った発表をご紹介します。(現地参加したエンジニアの複数人による共同執筆になります。)

Day 1のレポートはこちら tech.medpeer.co.jp

Day 2のレポートはこちら tech.medpeer.co.jp

タイムテーブル

タイムテーブルは以下から確認できます。

rubykaigi.org

Ruby Committers and The World

(執筆:伊藤)

YJITやパーサ、GC、Ractorなどの各トピックに対してRuby 3.3で実装したい機能に関してコミッター同士の公開トークがありました。 Ruby 3.3だけでなく、その次のバージョンに向けた将来的な話もあり、興味深かったです。 5/12(RubyKaigiの2日目)にRuby 3.3.0-preview1がリリースされたようなので、自分でも触ってみようと思います!

www.ruby-lang.org

セッションの最後にはコミッターの方々の写真撮影タイム 📸

Ruby Committers

The Adventure of RedAmber - A data frame library in Ruby

(執筆:貞元)

Rubyでデータフレームを取り扱うRedAmberについてのお話しでした。 RedAmberはApache Arrow の Ruby 実装である Red Arrowと連携し、Rubyらしいインターフェース用意し、Rubyistsにとって扱いやすいデータフレームとなっています。

https://github.com/red-data-tools/red_amber https://arrow.apache.org/

RedAmber on Red Arrow

RedAmberの以下のような機能の紹介があり、メソッド名など使用しやすく感じました。

  • Aggregation: vec.sum
  • Element-wide: vec.cumsum
  • Binary Element-wide: vec > 3
  • pick, drop(列選択, 列削除)
  • slice remove(行選択, 行削除)

Methods

また、RubyKaigiが行われた都市のデータを用意し、都市と緯度経度を結合した内容をプロットした例もあり、より実践的な例でイメージしやすい内容でした。

RubyKaigi Locations

Ruby向けのデータフレームはまだ触ったことがなかったので、これを機会にRedAmberを使用してみたいと思います。

Let's write RBS!

(執筆:千葉)

speakerdeck.com

このセッションでは、RBS 3.1で導入された新機能と既存アプリケーションにRBSを導入する方法について紹介されました。

紹介されたRBS 3.1の新機能は以下の2つです。

  • rbs subtract: 複数ファイルでRBS定義が重複している場合に片方を削除してくれる機能
  • rbs parse: RBSファイルをパースし、Syntax Errorを教えてくれる機能

これらの機能は、既存アプリケーションにRBSを導入する際に詰まりがちな部分を補ってくれるため、今後RBSを導入する予定がある方にとっては非常に重宝する機能になります。

また、既存アプリケーションにRBSを導入する方法については以下の手順でデモンストレーションが行われました。

  1. rbs collectionの導入
  2. steepの導入
  3. rbs subtractの導入
  4. RBSの記述方法

デモンストレーション形式で実際にコーディングを行いながら型についての解説が行われたため、これまでRBSに馴染みがなかった人でも理解しやすいセッションでした。

今回のセッションを通じて、RBS導入への敷居がまた一段と低くなったと感じたため、今後さらに多くのプロダクトでRBSが導入されることが期待できると思いました。

Load gem from browser

(執筆:熊木)

ruby.wasmでサードパーティ製のgemを動かしたいというお話しでした。 ruby.wasmとはRubyスクリプトをブラウザ上で実行できるようにする技術です。

サンプル github.com

現状の課題として、ruby.wasmでは標準gemは使用可能ですが、サードパーティ製のgemが使用できない点が挙げられていました。

その理由は、ruby.wasmのファイルシステムが読み取り専用であり、あらかじめ用意されたgemしか読みこめないためです。 この問題への対応策を考えるに当たり、JavaScriptの歴史が参考になったそうです。その結果、以下の2つのアプローチが提案されました。

  • 実行前にバンドリングして一つのファイルにまとめる
  • import-mapsのように実行時にgemを読み込む

しかし、スクリプト言語の特性を維持したいなどという考えから、後者の方法が採用されることになったようです。

後半では、現在の実装内容や困難な点が紹介されました。

まずrequire_relativeを使ってgemを呼び出す様子がデモで示されました。

デモの様子

現在、Rubyのファイルから別のファイルを呼び出すことは可能ですが、再帰的な読み込みはまだできないことが示されました。 また、ブラウザでファイルをダウンロードする際に使用するFetch APIから返却されるPromiseがRubyではないため、これを適切に扱うのに苦労したとのことでした。

将来的な展望として、ブラウザ向けのプログラミングをRubyでJavaScriptと同等の簡便さで行えるようにしたいと述べられました。 具体的には、UNpkgのような仕組みでgemを実行可能にし、デプロイを容易にし、初心者でも簡単にアプリケーションを公開できる環境を作りたいとのことでした。

発表の中で、何度も「Rubyプログラミングをより楽しくしたい」とおっしゃられていたのが印象的で、 Rubyが楽しく書ける言語として評価されるのは、こういった開発者の方々の努力のおかげだと、強く感じる発表でした!

Ruby + ADBC - A single API between Ruby and DBs

(執筆:内藤)

slide.rabbit-shocker.org

Red Data Tools (Ruby でデータ処理ツールを提供するプロジェクト)で開発されている ADBC(Apache Arrow Database Connectivity) API を用い 大量( 1カラム100万レコード以上)のデータ読み書きを Ruby で実施する話です。

データ交換処理では、通常、シリアライズ・デシリアライズに伴うデータ変換が行われます。 この変換処理時間は少量のデータでは無視できるのですが、大量のデータの場合には無視できない時間になります。

ADBCを用いることで、下記の三つの点から大量データを扱うのに最適化しているとのことでした。

  1. Apache Arrow データFormat を使う:データ交換コストがめっちゃ安い
  2. 結果セットの分割:並列で分割して読み込みを行っているので、高速なデータ読み込みが可能
  3. バルクインサート:高速なデータ書き込みを実施

なお、Apache Arrow データFormat を使うとなぜ良いのかは、下記の資料を読んで下さいとのことでした。

slide.rabbit-shocker.org

アーキテクチャ

ADBC Architecture

Apache Arrow データ形式を返す形で、各種DBにアクセスするための Single(単一形式) API を定義しているため、(Active Record のように)DBを変更してもクライアント側のコードを変更は不要です。

ただし、このAPIはそのままでは Active Recordから使えないので、 Active Record ADBC adapter を開発中(ただ、普段 Active Record を使っていないので、協力してくれるメンバーを募集中)とのことです。

利用可能な Driver は下記になり、MySQLの Driver は存在しないようです。

ADBC Available drivers

Current ADBC

現状、1000万レコードの処理で libpq と比較して ADBC(libpq Driver)は変換のオーバーヘッドがあり、まだ最適化されていないので遅く、ADBC(Flight SQL Driver) はArrow フォーマットを使った高速RPC通信ができているので、2倍速い結果となっています。

Flight SQL(Apache Arrow Flight) は下記の特徴を持っており、Arrowフォーマットのまま(=シリアライズ・デシリアライズに伴うオーバーヘッドが無い)並列転送をしているのがポイントのようです。

Apache Arrow Flight

今回、 PostgreSQL で Flight SQL を話せるようにPostgreSQLの拡張機能を用いたアダプタも作ったそうです。

前述のセッションレポートで述べた RedAmber が ADBC で処理できるようなるとDBに大量データが格納されている場合でも、全てApache Arrow データFormat で処理できる事になるのでオーバーヘッドが無く、高速に処理できそうですね。

Ruby でのデータ処理に興味を持った方は、ぜひ Red Data Tools に参加しましょう!

Parsing RBS

(執筆:伊藤)

rubykaigi.org

3日目のkeynote講演です。 RBSの型定義ファイルで文法エラーが発生していても抽象構文木(Abstract Syntax Tree、以下ASTとする)を取得できるようにパーサを改善したお話でした。

正しいRBSのコードの場合、パーサによってコードに対応するASTを得ることができます。

RBSのコードと対応する抽象構文木(AST)

文法エラー時にツリーを返すように変更

既存のパーサでは文法エラー時は例外が吐かれるように設計されており、抽象構文木を得ることができない仕様でした。 そこで、文法エラー時はMissingTreeというツリーを返却するように変更しました。

これによって、文法エラーが発生していてもASTを得ることができるようになりました。

最後にパースが成功した結果を利用して正しいASTを得る

文法エラーが発生している場合、コーダーの意図とは異なるASTを取得してしまうパターンがありました。

例えば以下のようなConferenceクラスを定義したRBSのコードがあるとします。

class Conference
  def initialize: (String, Integer) -> void
end

そして、以下のようにTalkクラスの定義を(途中まで)追加します。

class Conference
  class Talk

  def initialize: (String, Integer) -> void
end

class Talkに対応するendがないので文法エラーとなっています。

ここでinitializeメソッドはもともとConferenceクラスのメソッドだったのですが、Talkクラスのメソッドとして解析されてしまっていました。

以下のようなイメージです。

- class Conference
  - class Talk
    - def initialize

そこで、最後にパースが成功したASTの結果を用いて、変更があったトークンによるツリーを挿入することで正しくASTを取得できるようにしました。

先程の例では最後にパースが成功したASTは

- class Conference
  - def initialize

なので、そこからclass Talkというトークンが追加されると

- class Conference
  - class Talk
  - def initialize

のようなASTを取得することができます。 このようなアプローチをすることで文法エラーが発生していてもコーダーの意図に近いASTを取得でき、文法エラーであることを適切に表示することができるようになりました。

私は今回のRubyKaigiでパーサに関するセッションをいくつか聞いたのですが、どのセッションでも文法エラー時のエラートレランスについて言及しており、パーサ共通の課題であり難しい部分であることを認識しました。 パーサが改善されることによって、IDEで適切にエラー表示されて開発者体験が向上するので、今後もパーサについて着目してみようと思いました。

おわりに

3日間に渡るRubyKaigi 2023が終了しました。 どのセッションも難しい内容でしたが、普段使っているRubyという言語がどのような実装であるのか、これからはどのような方向を目指して成長していくのか知ることができたので、非常に有意義な3日間でした。

さて、最後のセッションの後のClosingでは、次回のRubyKaigiの日程と場所が発表されました!

次回のRubyKaigiはなんと!!

2024年5月15日から5月17日、場所は沖縄県那覇市です!

次回のRubyKaigiも楽しみですね!

After RubyKaigiを開催します!

最後に、弊社では「After RubyKaigi」を3社共同で今年も開催します! まだご参加登録は間に合いますので是非お申し込みください!

  • After RubyKaigi 2023
  • 概要:RubyKaigi 2023に関するLT・ディスカッション
  • 共催:株式会社ZOZO、ファインディ株式会社、メドピア株式会社
  • 時間:5/18(木)19:00~21:30
  • 場所:メドピア株式会社本社 および オンライン

RubyKaigiで登壇されたスピーカーの方もお呼びします! 詳細および参加登録についてはconnpassよりご確認いただけます。

zozotech-inc.connpass.com

読んでいただきありがとうございました。


是非読者になってください!


メドピアでは一緒に働く仲間を募集しています。
ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023 セッションレポート Day2

バックエンドエンジニアの貞元勝幸(@greendrop269)です。
RubyKaigi 2023で長野県松本市に来ています。2日目(5/12)に聞いたセッションについて、いくつか紹介していきたいと思います。

タイムテーブル

タイムテーブルは以下から確認できます。 rubykaigi.org

How resolve Gem dependencies in your code?

How resolve Gem dependencies in your code? - RubyKaigi 2023

RubyGems, Bundlerがどのように依存を解決しているかというお話しでした。

普段、rubyを書く中でgemを使用するためにgem install や bundle installを使用していると思います。 使用する側は、使用したいgemを指定するだけですが、そのgemが依存しているgemも取得する必要があります。 その依存を解決しているResolver Engineは、RubyGems, Bundlerで異なっています。

  • RubyGems: Molinillo
  • Bundler: v2.4からPubGrub
    • PubGrubはDart言語のパッケージマネージャーpubで使用されている次世代の依存解決アルゴリズム

また今後は、RubyGemsもPubGrubになるそうです。

RubyGems, BundlerのResolver Engine

そして、以下のようなRubyGems, Bundlerの例から、複雑さが垣間見れました。

  • C Extensionのgemで、リモートではOKだが、ローカルではNGとなる
  • bundle update --conservative rails で期待通りのgem以外が対象になる

Rubyで開発する中で、RubyGems, Bundlerは当たり前のように使用しており、日々進化していてとても支えられていると感じました。

Revisiting TypeProf - IDE support as a primary feature

Revisiting TypeProf - IDE support as a primary feature - RubyKaigi 2023

現在開発中のTypeProfのv2に関するお話でした。 TypeProfとは、Ruby 3.0から標準搭載されている静的型解析器のことです。 型解析ができることにより、変数の型チェックやメソッドの補完など開発者体験を向上させることができます。

発表ではまず最初にTypeProf v2を使ってVSCodeでの開発デモがありました。

推論された型情報が表示されている様子

推論された型情報が表示されたり、型注釈を記述した状態で型が合わない場合にエラーがリアルタイムに表示されるデモでした。

TypeProf v1では、解析速度に大きな課題がありIDEでのサポートも難しかったようです。

そこで、TypeProf v2では解析速度の向上とIDEでのサポートを目標として再開発することになりました。 v2ではデータフローグラフを用いて、解析結果を差分更新することで、パフォーマンスの向上を目指しました。 Rubyのコードをデータフローグラフに変換し、それを用いて型情報を伝播させることで、型推論を行います。 呼び出すメソッドが変更された際は、変更があった部分のみ新しいグラフを作成してその部分のみ型情報を更新します。

コード変更時のデータフローグラフ

v1ではコードの変更があった際にコードすべてに対して型推論を行っていたのに対し、 v2ではコードの変更があった部分のみ型情報の伝搬を行うことでコード変更時の型推論の速度向上が見込めます。

その結果、解析にかかるパフォーマンスは以下のように大きく改善されたようです。

  • v1: 約3秒
  • v2: 初回は約1秒、コードの変更時は0.029秒

今後の展望としては、キーワード引数や例外などの対応やIDE機能の改善などを行い、Ruby 3.3までに実用可能にすることを目指すようです。

私の感想としてはVSCodeでのデモを見たときにリアルタイムに型情報が更新されてとても驚きました。 最近はGitHub Copilotなどのツールで開発者体験の向上が話題になっていますが、 静的型解析によってより正確により安全に開発することができ、開発者体験がさらに向上するのではないかととても期待が高まる発表でした。

Introduction of new features for VS Code debugging

Introduction of new features for VS Code debugging - RubyKaigi 2023

VS Codeを使用したデバッグの新機能、Trace inspectorとDebug Visualizerのお話でした。

まず、Trace inspectorについてですが、ブレークポイント間の処理内容を保存しておき、それを表示したものとなります。 これがない場合、ブレークポイントから一つずつステップインしながらデバッグすることになり、どのメソッドが呼ばれたかなど忘れてしまうことがあるので、Trace inspectorはとても便利だと感じました。 デフォルトの設定では、呼ばれたメソッドの引数と戻り値を記録していますが、「record and replay」を有効にすることでローカル変数の状態も記録できるようになっていました。

https://github.com/ruby/debug/pull/959

Debug Visualizerは、すでにVS Codeの拡張機能であり、それをRubyに対応させたものとなります。 デモでは、ArrayやHash、NokogiriオブジェクトやActiverecordオブジェクトをグラフや表、ツリー形式などで表示されており、見やすいなものとなっていました。

https://github.com/hediet/vscode-debug-visualizer

Trace inspectorやDebug Visualizerを使用することで、さらに開発者が行う調査や生産性が向上すると感じました。

Optimizing YJIT’s Performance, from Inception to Production

Optimizing YJIT’s Performance, from Inception to Production - RubyKaigi 2023

YJITのパフォーマンス最適化についてのお話しでした。 この中で、印象に残ったものを紹介したいと思います。

まず、パフォーマンス最適化を行うためにYJIT用のペンチマークを準備されていました。 これによりYJIT開発者は簡単にベンチマークを実行できる環境となっていました。

https://github.com/Shopify/yjit-bench

YJIT-Bench

次に、メモリ使用に対するコードGCです。 初期化処理など一度しか実行されないようなものは、マシンコードが開放されていました。

コードGC

ベンチマーク手法やメモリ使用の改善などによるパフォーマンス最適化がRuby 3.2 よりさらに進んでいるので、Ruby 3.3のYJITが楽しみに感じています。

おわりに

私が参加したDay2のセッションでは、IDE向けの機能やYJITなど、今後使ってみたいと思うものが多かったように感じました!
Day3の記事も公開予定なのでぜひご覧ください。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

RubyKaigi 2023 セッションレポート Day1

松本駅にあるRubyistを歓迎する横断幕こんにちは!サーバーサイドエンジニアの中村(@_naka_0)です。

RubyKaigi 2023に現地参加しています。 1日目(5/11)に聞いたセッションの中でいくつかをピックアップしてレポートしていきたいと思います。

タイムテーブル

タイムテーブルは以下から確認できます。 rubykaigi.org

Matz Keynote

Matz Keynote - RubyKaigi 2023

Rubyが生まれてから30年が経過し、そこで学んできた教訓についてのお話でした。

その中でも印象に残った教訓についていくつか紹介していきたいと思います。

良い名前を選ぶ

Rubyは1993年2月24日に「Ruby」という名前が決まったそうですが、この名前が決まる前に「Coral」、「Tish」のような名前の候補があったそうです。 もし、「Ruby」ではなく「Tish」という名前であったら、ここまで多くの人に使われることはなかったかもしれないとのことでした。 この時のことを振り返り、良い名前を選ぶことの重要性を学んだそうです。

最初に決めた基本的原則は保持する

実際にMatzの手元にある一番古いコードは最新のRubyのコードと大きくは変わっておらず、開発当初の時点で基本の部分はすでに完成していたとのことです。

他の人の意見を聞いて、自分の視点では得られない気づきを得ること

1995年12月にRubyをインターネットに公開する前に有志の人に意見を募ったところ、20名程度の人の意見が集まったそうです。 その際、他の人の意見を聞くことで自分の視点では得られない気づきを得ることができたとのことでした。

人と人とのつながりの重要性

2001年9月に行われたJAOOというデンマークのカンファレンスにMatzが参加した際に、当時学生インターン生として参加していたDHH(Railsの生みの親)と話していたそうです。

この後、DHHはRailsを作ることになったそうですが、「この時は後にその人がRailsを作ることになるとは思ってもみなかった」というエピソードが特に印象に残りました。

これらの他にも本の出版や様々な人との出会いが今のRubyにつながっているという素敵な話でした!

リーダーシップとビジョンの提示

Ruby3x3(Rubyを3倍早くする)という目標を掲げた際、Matzは確実にそれが実現できるとは思っていなかったそうです。

しかし、Matzがビジョンを提示したことでMJITやYJITに繋がり、結果的にRubyの性能改善を実現できたそうです。

develop chrome extension with ruby.wasm

develop chrome extension with ruby.wasm - RubyKaigi 2023

本セッションの登壇者の方は実際にruby.wasmを使用してChrome拡張機能を開発してみたそうですが、Rubyらしい書き心地が得られないことに課題感を感じたそうです。

そこで、Rubyのスクリプトを読み込むだけでChrome拡張機能を作成できるようなフレームワークである「unloosen」を開発することに至ったとのことでした。

https://github.com/aaaa777/unloosen

このフレームワークを使用してChrome拡張機能を開発すると、上記リポジトリのREADMEの「Quickstart」に書かれているように少ないコード量とシンプルな構文で拡張機能の開発が実現できるとのことでした。 今後の展望としては、このフレームワークのドキュメント充実化やmanifest.jsonを自動生成できるようにする仕組みを挙げられていました。

また、このフレームワークはGemでは配布しておらず、npmとして配布しているそうです。

https://www.npmjs.com/package/unloosen-ruby-loader

まだ作成したばかりとのことなので、コントリビュートして貢献するのも良さそうだなと感じました!

Power up your REPL life with types

Power up your REPL life with types - RubyKaigi 2023

Ruby irbの補完は便利ですが、メソッドチェーンやブロックパラメータの補完が正確ではない(誤った補完やありとあらゆる候補が出てきてしまう)といった問題があります。 そこで、より正確な補完にするため、irbとRBS(Rubyの型定義を提供する機能)を組み合わせたGemである「katakata_irb」を作ったというお話でした。

「katakata_irb」のリポジトリは以下です。 https://github.com/tompng/katakata_irb

この「katakata_irb」をrequireすることで、メソッドチェーンしてても、ブロック引数・変数などを使ってても、ある程度正しく型を推測して補完候補を出せるようになるとのことでした。 他にはプロダクトにRBSをきちんと書いている場合、rails consoleでもこの補完機能が使えるようになるようです。 また、Gemに型定義がされている場合(rbs_collectionにも型定義されている場合)でも「katakata_irb」の恩恵を受けることができるとのことでした。

既存のプロダクトでも型を定義していればrails consoleでも使えるとのことなので、RBSを導入して型情報を増やしていけると開発体験が上がりそうだと感じました。

Lightning Talks

LT - RubyKaigi 2023

1発表5分で合計12のLT登壇がありました。

その中でも私が一番興味深いと感じた「RBS meets LLMs - Type inference using LLM」というセッションについてご紹介します。

このセッションでは、LLM(大規模言語モデル)を使って型の推測を行うことができないかという発想から、ChatGPTを使ってコードの型を推測する方法をデモを交えながら紹介していく発表でした。

ChatGPTにコードの型を推論させようとすると、出力されるRBSの構文がおかしいことや不要な出力がある等の課題が出てきたそうです。 そこで、Fewshotを用いて意図した出力が得られるように改良していった様子が紹介されていました。

Matz KeynoteでもLLM等で型を推論できないかどうかの話があったため、個人的に今後の発展に期待が持てそうな分野だと思いました。

おわりに

セッションの中では自分の理解が追いつかなかった部分もありましたが、普段は学べないことを学べたり、多くの刺激を受けることができた1日目でした! Day2以降の記事も公開予定なのでぜひご覧ください。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

Rails APIサーバーで Ruby 3.2 の YJIT を有効化してみた。

サーバーサイドエンジニアの内藤(@naitoh) です。

Rails で構築された小規模な APIサーバー(Rails の API モードで構築したもの)で、Ruby 3.2 の YJITを有効化する事で性能アップすることができましたので、喜びを分かち合いたく共有させて頂きます。

shopify.engineering

We’re able to measure real speedups ranging from 5% to 10% (depending on time of day) on our total end-to-end request completion time measurements.

YJIT を開発した Shopify では 5%から10%の処理速度の改善があったという事で、以前から弊社でも本番で運用を開始したかったのですが、比較的検証のし易い APIサーバーで本番投入の準備が整ったので導入を実施してみました。

YJIT 有効化までの道のり

1. Ruby 3.2 へのアップグレード

YJIT は Ruby 3.1 では 実験的機能の位置付けでしたが、Ruby 3.2 以降では正式にサポートされました。

www.ruby-lang.org

  • YJIT は実験段階ではなくなりました
    • 1年以上にわたって本番環境でテストされ、安定して稼働する実績があります。

そのため、事前に Ruby 3.2 までアップグレードする必要があります。

2. YJIT 有効でBuildされた Ruby を用意する。

Ruby の DOCKER OFFICIAL IMAGE は YJIT 有効でBuildされているので、そちらを利用します。

$ docker pull ruby:3.2.2
$ docker run -it ruby:3.2.2 ruby --yjit -e 'p RubyVM::YJIT.enabled?'
true

--yjit オプションで YJIT有効で起動できる事が確認できました。

--yjit オプションを付けないで起動すると YJIT 無効で起動された事がわかります。

$ docker run -it ruby:3.2.2 ruby -e 'p RubyVM::YJIT.enabled?'
false

なお、YJIT無効でBuildされたRuby の場合は下記のようなエラーが出るので、この場合はYJITを利用できません。

$ ruby --yjit -e 'p RubyVM::YJIT.enabled?'
ruby: warning: Ruby was built without YJIT support. You may need to install rustc to build Ruby with YJIT.
-e:1:in `<main>': uninitialized constant RubyVM::YJIT (NameError)

p RubyVM::YJIT.enabled?
        ^^^^^^

※ 補足:Ruby 3.2 の YJIT は Rust-lang で実装されているため、Build 時には Rust-lang が必要ですが、実行時は Rust-lang は不要なため、Docker IMAGE には Rust のパッケージは含まれていませんでした。

3. YJIT 有効化

環境変数 RUBY_YJIT_ENABLE=1 でも YJIT有効化が可能なので、deploy 時の環境変数に設定する事で有効化しました。

YJIT 有効化後と有効化前(1週間前の同一曜日、グラフ破線部分)との比較になります。

Rack Request : p95 Latency 比較 (低い方が性能が良い)

Rack Request

ave: 35.6 ms -> 34.6 ms (2.8% 短縮) とLatency が改善された事がわかります。

Shopify の実績の 5%〜10%の改善に比べると半分程度の改善になりますが、今回はAPIサーバーでフロント周りの処理が無いので妥当かもしれません。

この Rack Request の結果が、Latencyの状況全体を表しているのですが、もう少し詳細を見ていきます。

ActiveRecord Instantiation : p95 Latency 比較 (低い方が性能が良い)

ActiveRecord Instantiation

Ave: 105.8 μs -> 77.9 μs (26.3% 短縮) と、こちらは改善度合いがかなり高いです。

Render Template : p95 Latency 比較 (低い方が性能が良い)

Render Template

Ave: 791 μs -> 701 μs (11.3% 短縮)と、こちらも改善度合いが高いですね。

Action Controller : p95 Latency 比較 (低い方が性能が良い)

Action Controller

Ave: 33.1 ms -> 32.7 ms (1.2% 短縮) と、こちらは効果は低めで、オーダー的にもここが改善されると効果が顕著に出ると思われます。

Request/CPU/Memory 状況

Rack Request 状況

Rack Request Hits

Max は 308 hits -> 282 hits と下がっていますが、Ave 116 hits -> 117 hits と、ほぼ同様なアクセス状況です。

CPU 使用率 (低い方が性能が良い)

Rails Container CPU

Ave 0.195(各コンテナの平均) -> 0.17(各コンテナの平均) とCPU使用率が低下しています。

Memory 使用量 (低い方が性能が良い)

Rails Container Memory

Ave 143(各コンテナの平均) -> 174.5(各コンテナの平均) と Memory 使用量が 22.0% 増加しています。 今回は Memory に余裕があったため大丈夫でしたが、事前にメモリの使用状況を確認してメモリに余裕がある状態でYJITを有効化した方が良さそうです。

まとめ

Memory 使用量が増える可能性はありますが、YJIT を ON にするだけで全体で 2〜3%のレイテンシ改善が見られました。 最新のRuby に追随することで Ruby 開発者の成果の恩恵を受けられるのは非常にありがたいです。

今年のRubyKaigi も直前ですが、どのような発表があるのか期待しております。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

開発合宿に行ってきました!@軽井沢

こんにちは!サーバサイドエンジニアの森です。

開発合宿は2019年11月の以降、コロナの影響でしばらく開催できておりませんでしたが、 約3年半ぶりに4月19日から21日まで3日間、開発合宿を開催しました!

今回は参加者数が多いため、神奈川県湯河原と長野県軽井沢の2箇所に別れて開催しました。

この記事では、軽井沢(BEB5軽井沢)での開発合宿の様子をご紹介します!

※BEB5軽井沢: www.hoshinoresorts.com

※湯河原の様子: tech.medpeer.co.jp

※前回の開発合宿の記事: tech.medpeer.co.jp

1日目 開発スタート!

12時に軽井沢駅へ集合、お昼を食べて、開発がスタートしました!

私は3人1チームとして参加していたので、ゴール設定や作業分担について話し合い、それから作業を開始しました。 直前の天気予報では小雨だったのですが、快晴だったのでテラスで会議しています。 BEB5は自然で覆われており、テラス席では小鳥のさえずりを聞きながら作業できます!

日が落ちてからは焚き木に癒やされつつ、夜中まで開発しました。

2日目 全力開発!

2日目は朝から晩までひたすら開発です! 前日の疲れもあるので、朝やお昼など開発の合間に自然の中を散歩をしてリフレッシュしながら進めました。

夜は温泉、サウナで回復します。 そして結局、2日目も夜中まで開発しました!

3日目 ラストスパート!

とうとう最終日。 朝からホテルのチェックアウトの時間ギリギリまで、もくもくと作業しました。

最後はみんなでお昼を食べて解散!

私たちのチームは軽井沢駅近くのカフェで、発表資料の最終調整をしました。 他のメンバーの進捗が気になります!

成果発表

土日休みを挟み、開発合宿の成果発表会をオンラインで実施しました。 エンジニア以外のビジネスサイドの方々も参加しており、60名ほど集まりました!

約半数の発表がChatGPTに関連しており、今回の開発合宿を通じて、ChatGPTを活用して実現可能なことや、導入が難しいと思われることなど検証できた点が多くあります。 各メンバーの試行錯誤した箇所や成果を見て、今後の業務においてChatGPTなどを効果的に活用するための知識やアイデアを得ることができました。

私はフィッツプラス所属の3人で1チームとして、「ChatGPTを利用した特定保健指導用のメール文面の下書き作成機能」の開発に挑戦しました。フィッツプラスの全体会議で発表も行い、今後の業務オペレーションへの取り入れを目指しています!

おわりに

今回、私が一番学びになったことは、 開発から発表資料の作成までを3日間で行うという時間制限の中で、「期間内でどのように形にするか」「実装する要件」「諦める要件」などを先輩エンジニアと検討したことです。日々の要件定義や開発に活かして行きたいです。

そして、自然の中での開発は最高でした!(個人での開発旅行も良さそうだなと思っています)

開発合宿の復活を機に、今後も定期的に開催したいです。

そのためにも、得たものを業務効率や質の向上につなげ、次回も開発合宿へ快く送り出していただけるようにしていきます!

以上、メドピア開発合宿@軽井沢の様子でした!


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp

3年半ぶりの開発合宿に参加してきました

はじめに

はじめまして。今年の4月に新卒として入社した金淵(Twitter : @Deee_U_ )です! 今回は4/19 ~ 4/21に開催された開発合宿の様子を記事にしました。

開発合宿とは、メドピアが年に2~3回ほど日常業務外で業務の改善や技術研鑽を行う為に開催している合宿のことです (前回の開発合宿の様子はこちら↓)。 tech.medpeer.co.jp

今回は湯河原、軽井沢の2チームに分かれて3年半ぶりの合宿を行いました。私は湯河原の合宿に参加させて頂いたので湯河原の様子をお届けします。 (軽井沢の開発合宿の様子はこちら↓) tech.medpeer.co.jp

湯河原駅前でパシャリ

旅館

今回、湯河原の合宿ではおんどや惠(めぐみ)さんに宿泊させて頂きました。メドピアは過去に何度かこの旅館に宿泊させて頂いことがあり 今回も湯河原の「おんやど 惠(めぐみ)」さんにお邪魔しました。 おんどや惠(めぐみ)さんは開発合宿プランがあり、その場合会議室は13時から翌日正午まではほぼ1日中(23時間)利用が可能であり、連泊での利用の場合はなんと最終日以外は24時間利用できるので開発に打ち込める環境が揃っているのが魅力的です!ちなみにモニターも別途料金を支払うことで借りられるのでモニターを用意しなくても大丈夫!

おんどや惠(めぐみ)さんの開発合宿プラン↓ www.onyadomegumi.co.jp

開発の様子

旅館についたのちに各々が合宿でやる内容の発表をしてから開発がスタートしました! 皆さん持ち寄ったテーマを黙々と進めていますね。

勿論、折角の湯河原での合宿なので足湯駆動開発も積極的に取り入れてきました。 オフィスでは出来ない貴重な開発手法を取り入れられるのも合宿の良いところですね!

新卒の私たちも開発を頑張りながら、先輩たちの開発の様子を見させて貰ったり自分達のコードに意見を求めることができたりと良い経験をした合宿でした!

ご飯の記録

合宿の朝夕は旅館から食事が提供されるのでご飯に困ることなく美味しいご飯が食べられるのが素敵。。。 旅館に入る前にステーキを食べたり2日目のお昼は海鮮丼を食べたりと旅館以外のご飯も美味しいので探索する価値があります! (他にも海鮮丼を食べたけど写真が....)

成果発表LT

成果発表会は後日オンライン上で行いました。 合宿の特徴として今話題の「ChatGPT」などのOpenAI APIを使った開発を行なった人が多い印象です。

私は新卒のメンバーと一緒に「タスク管理アプリ」を作成しました。 少しハプニングがありつつもメンバーの力を借りながら何とか皆んなで発表するところまで達成しました!

(事前に設計や要件定義をまとめて合宿に入れたので動くものは作れたのでほっとしてます。。。)

他にも業務改善や気になっていた技術の調査、ロボを動かすなど皆さん自由に開発をしていていました。 発表には社内の方が約50人ほど傍聴に来て貰えワイワイとした成果発表でした。

最後に

旅館の前でパシャリ

開発合宿では、普段の業務では触らない技術などの技術研鑽は勿論、普段話せないチーム外の人との交流もできるので新たな発見ができる良い機会でした。また今回改めて自分に何が能力として何が足りないか(自分は特にRuby力...)であったり、次回の開発ではもっとこうするべきでは?などやるべき宿題に気づけた良い合宿だと思います。 以上!2泊3日の湯河原側の開発合宿の様子でした。


是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

medpeer.co.jp

■エンジニア紹介ページはこちら

engineer.medpeer.co.jp