1
/
5

【TECH BLOG】RubyKaigi 2022参加レポート 〜エンジニアによるセッション紹介〜

こんにちは!バックエンドチームマネージャーの@tsuwatchです!

2022/9/8〜10に三重県にて開催されたRubyKaigi 2022でプラチナスポンサーとして協賛し、スポンサーブースを出展しました。


ZOZOはRubyKaigi 2022にPlatinumスポンサーとして協賛します!|ZOZO DEVELOPERS BLOG
こんにちは!CTOブロックでDevRelを担当している @ikkou です。 ZOZOは、2022年9月8日から10日にかけて3日間開催される、プログラミング言語「Ruby」の一大カンファレンス「RubyKaigi 2022」にPlatinumスポンサーとして協賛します。 ZOZOが運営するファッションコーディネートアプリ「WEAR」は、2020年からRuby on ...
https://technote.zozo.com/n/n634e4d6c6bf7


RubyKaigi 2022参加レポート 〜エンジニアによるセッション紹介〜 - ZOZO TECH BLOG
こんにちは!バックエンドチームマネージャーの@tsuwatch です! 2022/9/8〜10に三重県にて開催されたRubyKaigi 2022でプラチナスポンサーとして協賛し、スポンサーブースを出展しました。 弊社からは WEAR を開発するバックエンドエンジニア、SRE、PdMなど合計10名ほどが現地で参加しました。 ...
https://techblog.zozo.com/entry/rubykaigi2022#:~:text=technote.zozo.com-,technote.zozo.com,-%E5%BC%8A%E7%A4%BE%E3%81%8B%E3%82%89%E3%81%AF


弊社からはWEARを開発するバックエンドエンジニア、SRE、PdMなど合計10名ほどが現地で参加しました。

我々が運営しているファッションコーディネートアプリ「WEAR」のバックエンドはRuby on Railsで開発しています。2013年にVBScriptで作られたシステムですが、2020年くらいからVBScriptのシステムをコードフリーズし、リプレイスをはじめました。現在もリプレイスを進めながら、新規の機能もRubyでどんどん開発しています。

また弊社ではRubyコミッタのsonotsさんがいたり、顧問としてMatzさんにもご協力いただいており月に一度、Matzさんに何でも聞く会をやっていたり、積極的にRubyを活用しています。

今回もエンジニアによるセッションの紹介とブースでの取り組みについて紹介します。

エンジニアによるセッション紹介

弊社エンジニアによるセッションの紹介をします。

How fast really is Ruby 3.x?


RubyKaigi2022/20220908-RubyKaigi2022.pdf at 38fe29222e9a717f32d29eb6e604a387c6b18382 · fujimotos/RubyKaigi2022
RubyKaigi 2022 resources. Contribute to fujimotos/RubyKaigi2022 development by creating an account on GitHub.
https://github.com/fujimotos/RubyKaigi2022/blob/38fe29222e9a717f32d29eb6e604a387c6b18382/20220908-RubyKaigi2022.pdf


高久です。Fujimoto Seijiさんのセッション「How fast really is Ruby 3.x?」についてご紹介します。

このセッションでは、Rubyの過去バージョンと比較しRuby 3.xではどれくらい早くなったかを、実際のアプリケーションを用いて検証した結果を報告しています。検証対象のアプリケーションはFujimoto Seijiさんがコミッターを務めるFluentdです。

検証の背景について。まず前提としてRuby 3は「Ruby 3×3」という「Ruby 2.0の3倍早くする」ことを目指して開発が進められました。そして過去のRubyKaigiでも同様に実際のアプリケーションを使った検証の話がいくつかありました。しかし、どれもRailsアプリで作られたものでは3倍にならないという話でした。なぜならRailsアプリは基本的に、アプリケーションの多くの時間を占めているのがRubyの処理ではなく、DBなどの外部処理によるものだからです。Rubyを早くしたとしても、その他にかかる時間が大きいため、全体の処理時間の短縮化に大きく寄与するものではありませんでした。

そこで今回、多くの処理をRubyで行なっているFluentdを対象に測定してみたらどうなるか? というのがテーマとなっています。

検証は読み込ませるファイルごとに2パターン行なっています。

  • LTSVファイル
  • nginxログファイル

検証した結果Ruby 1.9と比較しRuby 3.2(YJIT有効)は、LTSVファイルで約3.15倍、nginxログファイルで約2.5倍のスループットが出ることを確認できました。Ruby 1.9と3.2の比較にはなりますが、概ね「Ruby 3×3」は実現しているのではということが、Fujimoto Seijiさんが伝えたかった内容になります。

セッションでは、更に他の言語と比較してどうかを述べていました。気になる方は是非スライドをご確認ください。

コミッターさんたちがRubyをより良くするために開発をし、Rubyが日々進化していることを実感した発表でした!


Make RuboCop super fast



小山です。RuboCopメンテナの@koicさんによる「Make RuboCop Super fast」の発表を紹介します。

RuboCopは2012年4月21日がファーストコミットで今年10周年を迎えました。RuboCopは現在1.36.0が最新バージョンでありますが、2.0系リリースに向けてマイルストーンを掲げており、この発表はそのマイルストーンのうちの1つであるRuboCop2×2に関する発表でした。RuboCop2×2はRuby 3×3で目指しているように、RuboCopの速度を1.0系と比較し、2倍を目指すというものです。

RuboCopはCaching、Multi-cores、Reduce unused requires、Daemonizeの4つのアプローチで高速化を図っています。

Cachingはかねてから提供されていて、1回検査したコードはデフォルトで ~/.cache/rubocop_cacheに保存しています。

Multi-coresは1.19からデフォルトで並列検査するようになり、1.32から並列でオートコレクションするようになりました。8 core CPUかつHyper-Threadingを使用して約1,300ファイルに対して直列実行と並列実行を比較した場合、直列実行は61秒で完了したのに対し、並列実行は10秒で完了したそうです。

Reduce unused requiresは --onlyオプションを付与したとしてもすべてのCopが読み込まれてしまう問題がありました。require 'rubocop' の改善により高速化を実現しており、セッションの本論で話されていたserverモードと一部関連があります。

Daemonize(serverモード)がセッションで一番厚くお話されていた内容になります。serverモードは#10706で対応されて1.31から導入されています。使用することでrubocopコマンドを実行する度にプロセスを起動するのではなく、プロセスを常駐することでモジュールの読み込みが初回のみになります。Client/Serverモデルを採用して高速化を実現していたサードパーティ製のgem、rubocop-deamonを統合することで、RuboCopのserverモードは高速化されています。Client/Serverモデルとは、Server側の初回プロセスであらかじめモジュールを読み込んでおいて、Client側がすでにモジュールを読み込んでいるサーバーに接続するアプローチです。またrubocop-daemonをどのように統合したか、serverモードの設計、具体的な使用方法についてはセッション内で詳しく解説されていますので気になる方は是非スライドをご覧ください。

成果としては、moduleの読み込みを必要なもののみにし、serverモードを実装したことで850倍高速化されています。驚くべき成果です。

RubyのDX向上に、すぐに繋がると実感した素敵な発表でした。まだ不安定な挙動が残っているとの補足はありましたが、RuboCopのバージョンを上げて積極的に使っていきたいです!


The Better RuboCop World to enjoy Ruby



三浦です。私からはOhba Yasukoさん(@nay3)によるセッション "The Better RuboCop World to enjoy Ruby" について紹介したいと思います。技術的なセッションからは少し視点を変えた、RuboCopとうまく付き合っていくにはどうしたら良いかを考える内容になります。

RuboCopはRubyの静的コード解析ツールの1つで、コーディング規約を守れていないコードを簡単に確認でき、自動で修正できる便利なツールです。CIで回して事前に修正しておくことでレビューの負担軽減にも繋がります。

ただこのRuboCopのルールは "状況に合わないこと" もあります。例えば "Naming/PredicateName" というルールは、has, is, have_ といった特定の接頭辞のメソッド名をチェックし、接頭辞を排除したメソッドを使うよう警告します。


# bad
def is_child?
end

def has_child?
end

# good
def child?
end


ただ child?にしてしまうと、どちらとも捉えられるような曖昧なメソッド名になってしまいます。

"is child" なことをチェックするメソッド
"has child" なことをチェックするメソッド
このようにRuboCopの中にはルールとして間違ってはいないけど状況によっては合わないルールがいくつか存在しています。

初心者〜初級者のエンジニアの場合、状況に合っていないルールなのかを判断するのは難しいです。そのためRuboCopの警告に忠実に従ってコーディングをしてしまい、その結果かえって読みにくいコードになってしまう場合があります。

レビューで指摘された軽微な修正でも直そうとするとRuboCopのルールに引っかかってしまい、複雑な実装になってしまったなんてこともよくあります。(私もありました、、、) このような状況に合わないルールで振り回されてしまうのは、開発速度を低下させる一因にもなります。

RuboCopの全てのルールは、無効にしたりデフォルトとは異なる方針に変更できます。また、特定のコードで特定のルールを無視できます。しかしこのルールの設定の判断はルールの妥当性を判断できる技術力と経験が必要です。初心者〜初級者のエンジニアにとってはこの判断はなかなか難しいものです。かといって経験者が1つ1つのルールを必要か毎回確認するのも大変です。

そこで提案されたのがルールを大きく2つのレベルに分けて考えることでした。

  • 強制レベル:ほぼ100%の状況で適用しても問題がないようなルール
  • 参考レベル:なるべく多くの改善ポイントに気付けるような理想的なルール

この2つのレベルに合わせてrubocop.ymlの設定ファイル自体を分けておきます。強制レベルのルールは現状通りCIなどで警告を出し、修正を強制します。参考レベルのルールはCIを回しますが、参考情報として表示するだけに留めておきcommitの禁止やマージの禁止といった強制力は持たせないようにします。

このように参考レベルのルールを作っておくと全てのルールに従おうとして不自然なコードを作ることを防ぐことができるので良いのではということでした。Ruby初心者にとってのつまづきポイントなどもたくさん紹介しており、うなずきたくなるような共感できる内容でした。スライドのイメージ図は全て画像生成AIのMidjourneyを使って生成したものだそうで笑いも起こる楽しいセッションでした。


Implementing Object Shapes in CRuby

@tsuwatchです。個人的におもしろそうだなと思っていたObject Shapesというオブジェクトのプロパティを表現する手法について書こうと思います。Object Shapesを導入することでインスタンス変数のを見つけるときのキャッシュヒット率の増加とランタイム時のチェックを減らすことができ、JITのパフォーマンスを向上させるというものです。また、この手法はTruffleRubyやV8で採用されているそうです。

詳細はチケットにあるのでご覧ください。


Feature #18776: Object Shapes - Ruby master - Ruby Issue Tracking System
Aaron Patterson, Eileen Uchitelle and I have been working on an implementation of Object Shapes for Ruby. We are filing a ticket to share what we've been doing, as well as get feedback on the project in its current state. We hope to eventually submit the
https://bugs.ruby-lang.org/issues/18776


Object Shapesとは


class Foo
  def initialize
    # Currently this instance is the root shape (ID 0)
    @a = 1 # Transitions to a new shape via edge @a (ID 1)
    @b = 2 # Transitions to a new shape via edge @b (ID 2)
  end
end

class Bar
  def initialize
    # Currently this instance is the root shape (ID 0)
    @a = 1 # Transitions to shape defined earlier via edge @a (ID 1)
    @c = 1 # Transitions to a new shape via edge @c (ID 3)
  end
end

foo = Foo.new # blue in the diagram
bar = Bar.new # red in the diagram


例えばこういうコードが存在したときにObject Shapesは以下のようなツリー構造を構築します。



インスタンス変数の遷移をツリー状に構築することで、同じ遷移をするクラスはキャッシュを利用できます。別のクラスをnewしたときにも元のShapesの差分のみ作れば良いというわけです。


class Hoge
  def initialize
    # Currently this instance is the root shape (ID 0)
    @a = 1 # Transitions to the next shape via edge named @a
    @b = 2 # Transitions to next shape via edge named @b
  end
end

class Fuga < Hoge; end

hoge = Hoge.new
fuga = Fuga.new


現在はクラスをキャッシュキーとして使用しており、その場合はこのコードではキャッシュヒットさせることはできません。Object Shapesを導入することでクラス依存しないキャッシュを実現し、キャッシュヒット率を上げることができます。

複雑そうですが、効果がありそうなパフォーマンスチューニングです。キャッシュの構造や実際にどれくらいパフォーマンスが改善するのか今後もウォッチしていこうと思います。

続きはこちら

株式会社ZOZO's job postings

Weekly ranking

Show other rankings
Invitation from 株式会社ZOZO
If this story triggered your interest, have a chat with the team?