Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ: Wasm Workers Server 1.0、mruby 3.2.0リリース、irbtoolsほか(20230315後編)

こんにちは、hachi8833です。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

🔗Ruby

🔗 Wasm Workers Server 1.0でPythonとRubyをサポート


つっつきボイス:「Wasm Workers ServerというWebAssemblyサーバレス基盤でruby.wasmやpython.wasmも動かせるようになったのね: これは面白そう」「これまではRustのWasmしか動かせなかったみたいですね」「Wasm Workers Serverは、よく見るとVMwareが運営してる」「ホントだ」

参考: オープンソースのWebAssemblyサーバレス基盤「Wasm Workers Server 1.0」正式リリース。RubyとPythonのWASMランタイムに対応し、Ruby/Pythonでの記述が可能に - Publickey
参考: VMware Japan:クラウド、モビリティ、ネットワークとセキュリティ | JP

「コマンド名はwws↓」

# 同記事より: お試しコマンド
curl -fsSL https://workers.wasmlabs.dev/install | bash && \
    wws runtimes list

wwsコマンドでこうやってローカルサーバーを起動できるんですね↓」

# 同記事より
wws

⚙️  Loading routes from: .
🗺  Detected routes:
    - http://127.0.0.1:8080/
      => ./index.py (name: default)
    - http://127.0.0.1:8080/ruby
      => ./ruby.rb (name: default)
🚀 Start serving requests at http://127.0.0.1:8080

🔗 Ractorビギナーズガイド(Ruby Weeklyより)

# 同記事より
def tarai(x, y, z) =
  x <= y ? y : tarai(tarai(x-1, y, z),
                     tarai(y-1, z, x),
                     tarai(z-1, x, y))
require 'benchmark'
Benchmark.bm do |x|
  # sequential version
  x.report('seq'){ 4.times{ tarai(14, 7, 0) } }

  # parallel version
  x.report('par'){
    4.times.map do
      Ractor.new { tarai(14, 7, 0) }
    end.each(&:take)
  }
end

つっつきボイス:「Honeybadgerの記事です」「基礎的な内容に徹して短くまとめられた記事: Ractorを初めてやる人向けのチュートリアルによさそう👍」

参考: ruby/ractor.md at master · ruby/ruby · GitHub

Ruby 3: FiberやRactorでHTTPサーバーを手作りする(翻訳)

🔗 !value.nil?value != nilよりずっと高速(Ruby Weeklyより)


つっつきボイス:「コードとベンチマークのシンプルなGistです」「value != nilvalueの内容がメソッドなら中身も見に行くのでその分遅くなるのかなと思ったけど、Gistのvalueはシンプルな1だし、考えてみたら!value.nil?もその点は同じですね」「それもそうですね」「nil?メソッドは利用頻度が高い分最適化が進んでいるんだろうか?」

!value.nil?の方がRubyらしい書き方ですし、そもそも!= nilという書き方はあまり癖にしない方がいいでしょうね」「たしかに」「nil == nilが成立するかどうかは言語によって違う可能性があるかもしれないので(例: SQLではNULL = NULLがfalseになる)、nilをチェックする専用の書き方があるならそれを使うことを習慣づけておく方が、言語や処理系が変わった場合にハマりにくいかなと個人的に思います」

「Gistのレスに、これをRuboCopに入れるべきと書かれてますね: 速くてRubyらしく書けるならRuboCopに入れてよさそう👍」

参考: Object#nil? (Ruby 3.2 リファレンスマニュアル)


後で自分でもbenchmark_driverで動かしてみました。Ruby 3.2.1, 3.1.3, 3.0.5, 2.7.7を使いました。

▶コード(クリックすると開きます)
#!/usr/bin/env ruby

require 'benchmark_driver'

Benchmark.driver do |x|
  x.rbenv "3.2.1", "3.1.3", "3.0.5", "2.7.7"
  x.prelude %{
    def not_eq_nil
      n = 1_000_000

      value1 = 1
      value2 = nil

      n.times do
        value1 != nil
        value2 != nil
      end
    end

    def not_value_nilcheck
      n = 1_000_000

      value1 = 1
      value2 = nil

      n.times do
        !value1.nil?
        !value2.nil?
      end
    end
  }

  x.report('not_eq_nil')
  x.report('not_value_nilcheck')
  x.compare!
end
# 実行結果
$ ruby bench.rb
Warming up --------------------------------------
          not_eq_nil      7.443 i/s -       8.000 times in 1.074826s (134.35ms/i)
  not_value_nilcheck     46.295 i/s -      50.000 times in 1.080038s (21.60ms/i)
Calculating -------------------------------------
                          3.2.1       3.1.3       3.0.5       2.7.7
          not_eq_nil      7.500       6.702       7.796       8.672 i/s -      22.000 times in 2.933199s 3.282593s 2.822123s 2.536985s
  not_value_nilcheck     46.167      29.985      30.882      33.898 i/s -     138.000 times in 2.989180s 4.602239s 4.468657s 4.070990s

Comparison:
                       not_eq_nil
               2.7.7:         8.7 i/s
               3.0.5:         7.8 i/s - 1.11x  slower
               3.2.1:         7.5 i/s - 1.16x  slower
               3.1.3:         6.7 i/s - 1.29x  slower

               not_value_nilcheck
               3.2.1:        46.2 i/s
               2.7.7:        33.9 i/s - 1.36x  slower
               3.0.5:        30.9 i/s - 1.49x  slower
               3.1.3:        30.0 i/s - 1.54x  slower

benchmark-driver/benchmark-driver - GitHub

🔗 pid_cache: Process.pidの速度低下を改善(Ruby Weeklyより)

Shopify/pid_cache - GitHub


つっつきボイス:「Shopifyのgemです」「glibc 2.25からgetpid(2)のキャッシュがなくなったために、新しいLinux環境だとRubyのProcess.pidが遅くなるのか」「Ruby側でキャッシュを持たせることで同等のことをRubyのレイヤで行うgemなんですね」

参考: Process.#pid (Ruby 3.2 リファレンスマニュアル)
参考: GNU Cライブラリ - Wikipedia(glibc)

Process.pid呼び出しをキャッシュすることで、現代のLinuxでは無駄なものとなったシステムコールの多くを回避する。
RubyのProcess.pidは標準のlibc getpid(2)を呼び出す。libcのほぼすべての実装は歴史的にシステムコールを毎回呼ぶのを避けるためにこの関数をキャッシュしていた。

しかし2017年にリリースされたglibc 2.25ではこのキャッシュが廃止され、PIDを頻繁に監視するアプリケーションでパフォーマンスのリグレッションが発生した。

これが削除された理由は、一部の低レベルライブラリがこのシステムコール自身を呼び出すことでforkし、キャッシュのクリアを担当するコードをバイパスしていたためである。

しかしRubyでこれを行うのは不可能なので、PIDを安全にキャッシュする形でパフォーマンスをある程度改善できる。

このgemはRuby 3.3で提案されている機能(#19443#note-7)のバックポートであり、おそらくRuby 3.3ではこのgemを使えなくなるだろう。
同リポジトリREADMEより

「このgemは、Ruby 3.3で提案されている変更のバックポートなんですね」「byroot (Jean Boussier) さんのベンチマーク結果(#19443#note-3#19443#note-4)を見て計算してみると、glibcのバージョンによって0.05usくらいの速度で動いていたシステムコール呼び出しが0.28usくらいかかるようになってしまっていて、確かにオーバーヘッドが大きい」「こんなことが起きていたとは」「これは対応する価値があるでしょうね👍」「Ruby 3.3にも入って欲しいですね」

参考: Feature #19443: Cache Process.pid - Ruby master - Ruby Issue Tracking System -- 現在オープン

🔗 mruby 3.2.0リリース(Ruby Weeklyより)


つっつきボイス:「mrubyも着々とバージョンが上がってますね」「mruby-bigintというgemで多倍長整数を使えるようになったんですって」

後で探すと、mruby-bigintはmrubyのリポジトリの中にありました。

参考: mruby/mrbgems/mruby-bigint at master · mruby/mruby · GitHub

🔗 irbtools: IRBの機能を強化(Ruby Weeklyより)

janlelis/irbtools - GitHub


つっつきボイス:「IRBを拡張するツールだそうです」

lookでメソッドをancestorごとにグループ化して表示する機能↓、これはすぐにでも欲しい❤️」

# 同リポジトリより
>> look "str"
.
.
.
Comparable
  <  <=  ==  >  >=  between?  clamp
String
  %            crypt                  inspect      squeeze!
  *            dedup                  intern       start_with?
  +            delete                 length       strip
  +@           delete!                lines        strip!
  -@           delete_prefix          ljust        sub
  <<           delete_prefix!         lstrip       sub!
  <=>          delete_suffix          lstrip!      succ
.
.
.

「このツールの機能が本家IRBに取り入れられるかもしれませんね」「IRBもどんどん強くなってるので、この勢いで入るといいですね」


後でIRBメンテナーの1人である@st0012さんに尋ねたところ、irbtoolsは既にIRBメンテナーにも知られているそうです。

また、以下のRailsへのプルリクも興味深いと思うよとst0012さんから教えてもらいました↓。

参考: Make irb a railties dependency by st0012 · Pull Request #47442 · rails/rails -- マージ済み

上のプルリクメッセージで、Ruby 3.3でbundled gemsやdefault gemsの編成変更が進められていることを知りました↓

参考: Feature #19351: Promote bundled gems at Ruby 3.3 - Ruby master - Ruby Issue Tracking System

🔗 その他Ruby

つっつきボイス:「気づくのが遅れましたが、『研鑽Rubyプログラミング』の翻訳がひととおり終わって、全ページの翻訳+Jeremy Evansさんの日本語版向けあとがきをβ3版で読めるようになったそうです」「お、正式版のリリースが近づいていそうですね👍」「既にβ版を購入した人は無料で読めるそうです」


「こちらはruby-jp Slackで知りました: これまで英語版にしかなかった過去のRubyリリースの詳しい一覧が日本語版にも全部反映されたそうです」「Ruby 1.6.7から3.2.0までのリストって壮大」「自分が触り始めたのは1.8とか1.9あたりだったかな」

参考: Ruby Releases(英語版)

🔗CSS/HTML/フロントエンド/テスト/デザイン

🔗 スライド『全ブラウザ対応したcontainer queryは何がスゴイのか?』


つっつきボイス:「少し前に公開された、マネーフォワードの鹿野壮さんによるスライドです」「最近話題のコンテナクエリのわかりやすい解説👍」

「従来のメディアクエリではビューポートを基準にするので、ネストしたコンテナのレスポンシブ対応やブレークポイントの変更などがつらかったけど、コンテナクエリは任意の要素を基準にするので明らかに書きやすいですね」

メディアクエリーの使用 - CSS: カスケーディングスタイルシート | MDN

「これもそのとおりだと思います↓: コンテナクエリはコンポーネントのためにあるといっても過言ではないでしょうね」

待望のCSSコンテナクエリがやってきた(翻訳)


後編は以上です。

バックナンバー(2023年度第1四半期)

週刊Railsウォッチ: Devise 4.9のHotwire/Turbo統合に対応する、英国政府のViewComponentほか(20230314前編)

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Ruby Weekly


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。