RubyKaigi 2019 Keynote レポート

Jeremy Evansさん「たのしいRubyの先に,はやいRubyがある。Work, Correct, Fun! Fast」 〜RubyKaigi 2019 3日目 基調講演

この記事を読むのに必要な時間:およそ 3 分

4月18日から20日に開催されたRubyKaigi 2019。最終日,3日目のキーノートには,Jeremy Evansさんが登壇しました。JeremyはSequelRodaのメンテナーです。これらが開発されているGitHubのレポジトリは,Open issueがつねに0であることが特徴です。これらの貢献が評価され,2015年にはRuby Heroに選ばれています。

今回,Jeremyは「Optimization Techniques Used By the Benchmark Winners」というタイトルで発表しました。SequelとRodaでなされたパフォーマンス最適化のテクニックや原則を紹介し,RubyKaigi参加者のRubyコードのパフォーマンス最適化の手助けとなることを目的としていました。

画像

Sequel,Rodaとは何か? Active Record,Sinatraとの比較

ここで,SequelとRodaについて紹介します。Sequelとは"The Database Toolkit for Ruby"と紹介されており,RailsでのActive Recordに相当します。Rodaとは"Roda is a routing tree web toolkit"と紹介され,Sinatraに相当します。Railsでは,"Rails is omakase"にもあるように,Webアプリケーションを作成するのに必要な道具がすべて入っています。それに対し,SequelとRodaは必要最小限の機能のみを本体が持っており,必要な機能拡張は開発者がプラグインを取捨選択するスタイルをとっています。

オープンソースで3,000人以上のcontributorがいるRailsに対して,SequelやRodaはJeremy Evansのコミットがほとんどです。Open issueが0であることは前述したとおりですが,Jeremy本人がほぼすべてを把握しているからこそ,このようなメンテナンスポリシーが可能なのだと思います。

パフォーマンス向上の原則

インスタンス変数をnilやfalseで初期化しない

まず,新しいモデルを作成する場合に,SequelのcallメソッドとActive Recordのinit_with_attributesメソッドでのコードの対比を行いました。Sequelはインスタンス変数がnilまたはfalseで初期化されないようにしています。一方,Active Recordは,8つのインスタンス変数を初期化しており,その多くはnilまたはfalseです。マイクロベンチマークで6つのインスタンス変数をnilまたはfalseにしないことで150%の性能向上が見られ,現実に近いベンチマークでは数%の性能向上があったとのことです。この結果を受け,ruby -wで初期化されていないインスタンス変数が警告されないようにするパッチを送ったがrejectされたとのことです(おそらく,このissueと思われます⁠⁠。

実行を本当に必要なときまで遅らせる / すべてを遅延させるのではなく,かならず必要になる処理であれば初期化に行い,実行時には行わない

Active Recordの最初のインスタンスはそのモデルに対して,attributeメソッドを作らせます。これは,Active Recordのモデルクラスの生成時にはattributeメソッドが生成されないためなのですが,すべてのインスタンスはattributeメソッドが存在するかどうか,確認を行う必要が出てきます。一方,Sequel::Modelではクラスを作成したときにattributesを定義するため,インスタンスはモデルのattributeメソッドが作られていることが前もってわかっており,モデルクラスに確認する必要がありません。

オブジェクトの生成を減らす

文字列の生成を減らす

Sequelでは,Ruby 2.3から利用できるマジックコメントfrozen-string-literal: trueの導入で性能向上につながりませんでした。これは以前からfreezeした文字列を定数に代入しており,SQL文を生成していたためです。これらのコードは性能的には十分だったものの,たいへん読みにくいコードとなってしまっていました。そこでfrozen-string-literal: trueの導入によって,読みやすいコードに変更しています。

ハッシュの生成を減らす

Sequelでは,多くのメソッドが引数にHashをとっています。ここではHash自体ではなく,OPTSという定数に,空の凍結されたHashを代入し,引数に渡すようにしました。これにより2倍程度の高速化が図られています。また,他のメソッドにその引数を渡す場合でも,ハッシュの再生成を避けることに成功しています。

キーワード引数ではsplatをさける

RodaもSequelもオプションとしてHashを利用しており,Rubyのキーワード引数を利用していません。単純なケースでは,キーワード引数のほうがハッシュよりも速いのですが,splat(**opts)を利用した場合に遅くなるため,キーワード引数を利用する場合はsplatを利用せず,すべての引数を明示することにより,性能劣化を避けています。ただし,これは引数を追加したときに,すべての関連するメソッドの引数を書き換える必要があるため,広く推奨できる手法ではないこともあわせて紹介しました。

著者プロフィール

本多康夫(ほんだやすお)

Active Record Oracle enhanced adapterのメンテナー /
Ruby on Rails contributor /
Oracle ACE

freee株式会社所属。Asakusa.rb,OSSパッチ会,OSS Gate東京ミートアップ for Red Data Toolsに参加している。

Twitter:@yahonda
GitHub:@yahonda