てくすた

ピクスタ株式会社のエンジニア・デザイナーがつづるよもやまテクニカルブログです

RubyKaigi 2019 2日目まとめ

おはようございます!ピクスタ開発部です。

f:id:Yasaichi:20190419101129j:plain

RubyKaigi 1日目に引き続き、2日目も実況ツイートをしました!
それでは、2日目の開発部のメンバーが選りすぐりのセッションを紹介します。

All bug-fixes are incompatibilities

RubyKaigi 2019 2日目のKeynoteは「All bugfixes are incompatibilities」と題してCRuby(以下、「Ruby」)の安定版をどのようにメンテナンスしているのかというお話でした。

Rubyでは安定版ごと(現在だと、Ruby 2.6とRuby 2.5)ごとにブランチがあり、それを安定版メンテナーが担当します。基本的に安定版で発生しているバグの修正はtrunkブランチに適用されます。

そのため、安定版メンテナーは主に以下の3つのことを実施します。

  • trunkブランチで修正された(した)バグを、安定版にバックポートする
  • バックポートされた内容をもとに、安定版のパッチバージョンをリリースする
  • 脆弱性対応

また、Rubyのメンテナンス期間は安定版メンテナーの人数が影響しています。現在だと3年はメンテナンスが可能とのことでした。

しかし、Rubyも1年ごとに年をとりますが安定版メンテナーも1年ごとに年をとるため(笑)、将来的に安定版メンテナーの世代交代は必須です。そのため、次に、将来の安定版メンテナーの方々のために伝えておきたいことのお話がありました。

安定版メンテナーはRubyのメンテナンスポリシーへの理解が必要です。また、バグや脆弱性対応をする際には本当にRubyの問題で修正が必要なのか?や修正するタイミング(例えば、すでに安定版のプレビューが公開されている場合はI/Fの修正ではなく警告を出す)を判断したりと、落とし所を決める力も必要になってくるとのことでした。

最後に安定版メンテナーとして活動されてきた中での教訓についてお話がありました。以下の4つが教訓としてあるとのことでした。

  • パフォーマンス改善はバックポート対象ではない。安定版ではあくまでも互換性を維持するべし
  • メソッド/定数探索への修正は他のバグを生んでしまう可能性があるので、大きなバグではない場合は様子を見たほうが良い場合もある
  • "Refinements"は仕様が成熟していないので、バグ修正の場合は特に注意する
  • parse.yは"魔境"。さらにSyntaxErrorが発生するとそもそもコードが実行されないので重大な問題になりやすい

Rubyの安定版をメンテナンスするにあたる、思考の過程や教訓からの学びを知ることができ、満足度の高いセッションでした。

Six Years of Ruby Performance History: But How to Measure...?

f:id:Yasaichi:20190419200622j:plain 本セッションのスライドはこちらです。

MJIT により Ruby 2.6 は Ruby 2.0 に比べて2.8倍早くなりましたが、Rails はむしろ MJIT を入れると遅くなってします。
今後改善はされていくはずですが、とはいえ今は遅いです。
そして、Rails の速度を改善することは、世の中にとって価値のあることです。
ではそもそも Rails の速度はどのように計測するのでしょうか・・・?

そういうわけで、本セッションでは Ruby の速度を Rails の速度計測によって確認する方法と実際に計測してみた結果が紹介されていました。

一般に Rails の速度計測に使われている RRB(Rails Ruby Bench)は、並列性の高い現実世界を想定しています。具体的には10プロセス60スレッドで動かしています。
すなわち、Large なベンチマークです。
一般にベンチマークには Small なものと Large なものがあり、前者は個別具体的なケース("Specific")を想定したもので、後者は計測対象を代表するひとつの包括的なケース("Representative")、という違いがあります。
RRB は Large なベンチマークのため、「どの程度早いの?」ということには答えてくれますが、「どの部分が遅いの?」というような具体的なことを知るためには力不足です。

そこでセッションのスピーカーである Noah Gibbs 氏は、RSB(Rails Simpler Bench)というものを開発しました。
セッションでは、Specific な具体例として、まず1プロセス1スレッドで hello world を出力するだけの Rails アプリケーションの速度計測結果を表示し、その後プロセス数とスレッド数が変わるとどのように結果が変わるのかを説明していました。

その後実際にプロファイルを取るデモが行われました。
コマンド一発で速度計測でき、オプショナルに変更可能な変数については環境変数によりで簡単に設定できるようでした。
Ruby 3 の目玉の一つである「Performance」の改善を進めるにあたって Rails の速度問題は無視できない問題なので、このようなツールがあると便利そうだなと感じました。

Better CSV processing with Ruby 2.6

www.slideshare.net

標準ライブラリの一つであるCSVの性能面の改善についてのセッションでした。
Ruby 2.6のCSVはRuby 2.5と比較して1.5~3.0倍高速になりました。
高速化のポイントは大きく3点あります。

  1. StringScannerを使用する
  2. loopではなくwhile trueを使用する
  3. 処理を遅延させる(必要になるまで処理をしない)

StringScannerを使用する

Ruby 2.5ではString#each_lineと、String#split(line(","))を使用し一行ずつ処理を行なっています。
これをStringScannerに置き換えて高速化したのが、Ruby 2.6 における主な変更です。
また、単純にStringScannerの正規表現がボトルネックになることを避けるために、後続の文字列を予測した条件分岐にすることで正規表現による処理回数を減らしました。

loopではなくwhile trueを使用する

StringScannerを使用することで大幅に速度向上しましたが、それだけではなくloopを使った処理をwhile trueにすることで高速化しています。
loopでは、1処理ごとにスコープが変わってしまう事に対し、while trueではスコープが変更されないため、その差分で効果が出ているようです。

処理を遅延させる(必要になるまで処理をしない)

これまで、CSVインスタンス生成時に読込用の初期化処理と書込用初期化処理を行なっていたものを、読み込みだけ行う場合は、読み込み初期化処理だけを行い、その逆も同様の処理を行うように変更しました。
また、これに関連して、大量の行を書き込み場合はCSV.generate_lineではなく、CSVオブジェクトを生成し、そのインスタンスに対して<<で書き込みを行と高速化されます。

Ruby 2.6 では、CSVの処理をStringScannerで正規表現を用いた処理に変更するだけではなく、ベンチマークを取りながら細かい処理にまで目を向けて変更を加えたことによりCSV処理がより高速化されました。

Ovto: Frontend web framework for Rubyists

speakerdeck.com

OvtoはRubyで書けるWebフロントエンドフレームワークです。React/Reduxのように仮想DOMベース、シングルステートという特徴を持ちます。Reactの経験がある方にはお馴染みですが、State, Actions, Componentという3つのクラスを覚えるだけで実用的なアプリが作れるため、学習コストが低いというメリットがあります。ただし、サーバー側の機能は持たないため*1、RailsやSinatraと併用されることが想定されています。

OvtoはOpalを使って、RubyのコードからJSを生成します。Ovtoの内部のruntime.rbには超軽量なJSライブラリのHyperappのコア部分がほぼそのまま入っており、Ovtoで作成した仮想DOMをHyperappに渡して描画します。またアクションの実行時に、WiredActionsという仲介役のオブジェクトがstateの更新と再描画を行うことを把握しておくと、Ovtoへの理解がより深まるとのことです。

開発者の方が「Ovtoの原則は楽しくプログラミングできること。プログラミングは元々楽しいものなので、辛いことは取り除いていけばいい」と仰っていたのが印象的でした。こちらにサンプルコードと詳しい説明があるので、興味がある方はぜひご覧ください。

State of Sorbet: A Type Checker for Ruby

f:id:Yasaichi:20190419200113p:plain 本セッションのスライドはこちらです。

Stripe社のJake Zimmerman氏とPaul Tarjan氏による、同社が中心となって開発しているType CheckerのSorbetの現在に関するセッションでした。

Sorbetは2017年の終わりころから開発されており、昨年のRubyKaigiでも発表がありました
この時の筆者(id:Yasaichi)の印象としては、公開されたデモの完成度が高く驚いた一方で、「Stripeの開発で起きている問題に対処するためにこんなことをやっているよ!」という感じの雰囲気もあり、Sorbetの成果がRuby 3に対してどう取り入れられていくのかは不透明なところがありました。

しかし現在は、同じくRubyのType CheckerであるSteepの作者の松本さんやType Profilerを実装している遠藤さんと連携していることや、Stripe社以外にもShopify社やCoinbase社、Sourcegraph社のエンジニアも開発に携わっているとのことで、1年前と比べるとかなり体制が整ってきたことがわかりました。

このように、Sorbetの開発体制はとても良い方向に向かっていることがわかりましたが、Sorbet自体はあれからどのように進化したのでしょうか?
昨年からのアップデートの中でも特筆すべきものとしては、Sorbetのエディタ向けのプラグインの実装が進んでいることでしょう。 セッションではVS Codeでの例が示されていましたが、エラー箇所に下線を引く、マウスカーソルをhoverするとエラー内容が見れる、定義元ジャンプ、入力補完など、一通りの機能が揃っており、十分実用的なものになっていると感じられました。

現在、Sorbetはプライベートベータの状態ですが、今年の夏には公開したいとのことでした。
なお、ドキュメントは既に https://sorbet.org で公開されていて、これを見ると、sorbet(CLI)とsorbet-runtime(Sorbet本体)の2つのgemの形で提供されるようです。

Stripeの巨大なコードベースのかなりの部分で既に適用されているという事実に加えて、エディタ向けのプラグインの実装、丁寧なドキュメントの整備など、Ruby 3の3つの目玉のひとつであるStatic analysisの実現がかなり近づいていると実感できたセッションでした。

おわりに

3日目も実況ツイートしていきます!よければフォローもお願いします!

twitter.com

* * * * *

ピクスタではエンジニアを募集しています!

recruit.pixta.co.jp

*1:要望があれば、サーバーサイドレンダリングやルーティングのサポートを検討するとのことです