Slide 1

Slide 1 text

extendはこわい

Slide 2

Slide 2 text

自己紹介 名前:藤田 将志 所属:DIGGLE株式会社 自己紹介: ● Ruby on Rails や React などを使用した、管理会計のためのtoBのSaasを開発し ています。 ○ 弊社は2023、2024Ruby会議のスポンサーで、とても Rubyにお世話になっています 😊 ● 兵庫県神戸市出身で、とても関西が恋しいです

Slide 3

Slide 3 text

事の始まり ● Userの一覧を、誕生日(birthday)順に取得・処理する際のメモリ使用量を改善す るために、バッチ処理にしたい ● find_eachはID順のバッチ処理のため、使えない 再現用コード https://github.com/fujitamasashi255/ruby-extend-memory-test バージョン ● ruby 3.3.4 ● Rails 7.1.3.4

Slide 4

Slide 4 text

事の始まり あまり深く考えずに、以下のように実装してみた →メモリ使用量が改善されない😭 (本発表とは関係ありませんが、 Userモデルのクラスメソッドとして定義することで、問題は解決しました。) ● FindEachInOrder モジュールを作成 ○ find_each_in_order メソッド ■ order を含む場合に、 find_each と同じように処 理を実行できる ● FindEachInOrder モジュールを User::ActiveRecord_Relation オブジェクトにextendする

Slide 5

Slide 5 text

事の始まり 調査の末、以下のコードでも同じ感じになることがわかった。 extendすると、find_each のメモリ使用量が増加する、、、

Slide 6

Slide 6 text

メモリ使用量を計測 extendあり ● 10万件のUserレコード ● 10000件ごとにrssを取得 extendなし

Slide 7

Slide 7 text

profilerを実行してみる heap-profilerを実行してみる extendあり extendなし

Slide 8

Slide 8 text

heap-profiler 実行結果を細かく見ると extendあり Total allocated: 172.37 MB (1342040 objects) Total retained: 9.11 MB (34481 objects) allocated objects by file ----------------------------------- 6637 activerecord-7.1.3.4/lib/active_record/relation/query_methods.rb 4148 activerecord-7.1.3.4/lib/active_record/relation.rb 2001 activerecord-7.1.3.4/lib/active_record/relation/batches.rb 815 578 activerecord-7.1.3.4/lib/active_record/relation/delegation.rb extendなし Total allocated: 171.48 MB (1334457 objects) Total retained: 9.10 MB (34108 objects) allocated objects by file ----------------------------------- 2988 activerecord-7.1.3.4/lib/active_record/relation/query_methods.rb 1230 activerecord-7.1.3.4/lib/active_record/relation.rb 1701 activerecord-7.1.3.4/lib/active_record/relation/batches.rb なし 476 activerecord-7.1.3.4/lib/active_record/relation/delegation.rb

Slide 9

Slide 9 text

なんとなくわかったこと1 allocated memory by location ----------------------------------- 645.08 kB :48 extendありのケースでは、:48 の実行によっ て、オブジェクトが生成されている ruby/kernel.rb ActiveRecord::QueryMethods#where ActiveRecord::Batches#find_each 内では、relation.whereによって、 User::ActiveRecord_Relation オブジェクトのcloneが発生する → clone 生成物ごとに異なる singleton_class が生成される → 多くのオブジェクトが作成される ActiveRecord::SpawnMethods#spawn

Slide 10

Slide 10 text

なんとなくわかったこと1 rb_obj_clone_setup rb_singleton_class_clone_and_attach singleton_class が attach されていな い場合、clone はスキップされる cloneの際、singleton_classもcloneが作成される

Slide 11

Slide 11 text

なんとなくわかったこと2 extendありのケースでは、 callcache がより多く生成される。 callcache: インラインメソッドキャッシュ のためのオブジェクト allocated objects by class ----------------------------------- 11652 (IMEMO) ・・・extendあり 4275 (IMEMO) ・・・extendなし インラインメソッドキャッシュ : 笹田 耕一『プログラム言語 Ruby におけるメソッド キャッシング手法の検討』 より引用 メソッドディスパッチ命令 send のオペランドにメ ソッ ドキャッシュ用のオペランドを用意し,その領域に レ シーバのクラスとメソッド定義情報を格納する.ディス パッチ時,レシーバのクラスをキャッシュしたクラス と 比較し,等しければヒットとする. rb_callcache

Slide 12

Slide 12 text

なんとなくわかったこと2 WEB+DB PRESS Vol.122 笹田 耕一「Rubyのウラガワ」 P.143より引用 注16 クラスが異なるとメソッド探索が失敗します。つまり、同 じクラスのオブジェクト でも、それぞれが特異クラスを 持っているとクラスが異なると判定され、キャッ シュが効 きません。性能が必要な場面では特異クラスを作らないよ うにする とよいかもしれません。

Slide 13

Slide 13 text

なんとなくわかったこと2(補足) 【参考】その他、extendによるメモリ使用量への悪影響 ● Call Cache for singleton methods can lead to "memory leaks" ○ 引用:Eregon (Benoit Daloze) さんのコメント As a general note, creating a singleton class is not cheap, this should only be used for class objects (which always have one) and for a few rare global objects where it's convenient. Using Object#extend objects often/on the fast path is just "making programs slow and uncached". (訳)一般的な注意点として、シングルトンクラスの作成はコストが高いので、常にシングルトンクラスを持つクラ スオブジェクトや、便宜上必要な少数のグローバルオブジェクトにのみ使用すべきです。 `Object#extend` を頻 繁に使用したり、高速パスで使用したりすることは、単に「プログラムを遅くし、キャッシュを効かなくする」ことに なります。 ● PoC: Cache Extended Collection Proxies

Slide 14

Slide 14 text

わからないこと allocated objects by location ----------------------------------- 606 activerecord-7.1.3.4/lib/active_record/relation/query_methods.rb:943 extendありの場合 なぜ、extendがあると、build_where_clause で より多くのオブジェクトが生成されるのか? heap-profiler の見方の問題?? ActiveRecodd::QueryMethods#build_where_clause

Slide 15

Slide 15 text

これから知りたいこと まとめ extendを使用すると、メモリ使用量でこわい思いをした → extendの使用ってこんなに気をつかう必要があるのか・・・ heap-profiler を見るコツみたいなのがあれば知りたい

Slide 16

Slide 16 text

ありがとうございました!