テクノロジー部門で Ruby インタプリタ開発をしている笹田です。
Ruby ではメソッドを駆使してプログラミングをします。そんな Ruby を使っていると、一番使われているメソッド や 一番定義されているメソッド を知りたいと思ったことはありませんか? 私はありませんでした。
が、ものは試しと調べてみました! 調査は、あるタイミングの Ruby の RubyGems で取得できるすべての Gem (の各 Gem の最新版)を集めてきて、その中の .rb ファイルをすべて読み込み、字面上で呼び出されているメソッドと、定義されているメソッドを集計したものです。実際に動かしたときに呼ばれたり定義されたりするメソッドの数の集計ではないことに注意してください(それは、実行しないとわからないので、網羅的な調査は難しいのです)。
ちなみに、この記事は、Ruby のメソッド定義時に仮引数があるとき、それをカッコでくくらないのは私だけなの? - STORES Product Blog の二番煎じです。
調査方法
次のツールを利用しました。
- Gem のミラーの取得: GitHub - akr/gem-codesearch: Set up a full text code search engine for rubygems mirror
- 構文木の解析: GitHub - ruby/prism: Prism Ruby parser
- 先の記事よりもまともなマシン(前回は AWS の比較的弱いマシンで、今回は CI 用に使っている実機)
- 探索スクリプト all_method_name_finder.rb · GitHub
調査結果
あるタイミング(多分先月集めた全 gem のソースコード)のうち、.rb が拡張子である 2,945,839 ファイルから調査しました。すぐに結果を出しますので、自分で考えたい方は、下記を読まずに考えてみてください。
呼び出されているメソッド
上位100件の結果です。
method cnt [] 12,248,403 new 5,434,367 []= 4,419,090 require 3,344,624 == 3,085,552 it 2,552,628 expect 1,993,453 nil? 1,925,815 should 1,872,058 to 1,659,815 assert_equal 1,645,956 + 1,575,932 attr_accessor 1,544,671 << 1,373,461 to_s 1,298,123 each 1,295,545 describe 1,185,300 ! 1,160,119 raise 1,155,197 class 1,149,921 include 1,067,694 transition 962,827 is_a? 846,817 config 836,690 join 822,080 eq 808,482 map 804,601 u 751,416 puts 743,323 key? 706,726 name 687,384 subject 610,757 let 605,961 context 604,143 send 592,035 attr_reader 575,221 fail 553,966 include? 526,432 first 516,666 parse 509,371 attribute_map 492,039 private 481,482 require_relative 473,849 empty? 467,741 assert 464,456 size 448,880 - 439,236 =~ 414,491 to_i 402,500 before 401,029 respond_to? 399,363 freeze 388,847 length 384,808 get 382,300 normalize 381,642 * 378,101 to_sym 376,852 body 376,446 id 353,241 call 352,512 dirname 338,348 property 329,925 options 325,961 with 317,076 inspect 314,557 _deserialize 307,407 > 304,523 != 298,859 merge 296,176 split 286,234 delete 285,364 logger 283,662 lambda 283,637 expand_path 280,794 request 270,390 params 263,565 to_hash 254,548 has_key? 247,519 push 244,762 load 244,700 message 237,362 autoload 236,489 read 233,511 gsub 232,460 create 232,311 and_return 231,187 index 228,225 extend 227,192 match 226,571 find 223,153 debug 221,535 receive 221,207 tap 220,659 < 219,864 value 219,347 _to_hash 218,584 last_match 211,126 last 205,419 raise_error 201,267 send_flowing_data 200,133
#[]
がダントツで多いですね。ほか、テストコードを gem に含めるものも多いため、rspec 関連のメソッドも多いですね。
上位1万件の結果: Top 10,000 called methods in AST on all Ruby gems · GitHub
ちなみに、map or collect の論争も答え出ちゃってるね...。
1295545 each 804601 map 98884 collect
定義されているメソッド
こちらは上位44件です(2万回以上定義されたもの)。
Defined method Cnt initialize 989,783 to_s 139,528 build_from_hash 106,177 == 98,310 to_hash 88,232 setup 84,030 hash 83,334 valid? 83,076 eql? 79,350 attribute_map 75,758 _to_hash 72,858 update! 64,524 to_body 61,613 _deserialize 61,371 list_invalid_properties 60,366 call 56,467 run 52,458 mapper 49,288 get 47,213 openapi_types 39,120 create 38,838 included 38,527 all 37,374 add 35,785 parse 35,769 swagger_types 35,452 method_missing 35,183 [] 34,202 inspect 34,129 delete 33,568 openapi_nullable 33,423 build 32,860 deserialize 32,446 update 28,587 change 27,771 acceptable_attributes 26,159 name 24,360 validate 24,147 execute 23,502 each 23,350 configure 22,422 reset 21,052 start 20,407 description 20,085
圧倒的に initialize
が多いですね。私にとってよく知らないメソッドも結構ありました。
上位1万件の結果: Top 10,000 defined methods in AST on all Ruby gems · GitHub
そもそもなんでこんなことしているの?
この記事の背景です。
この調査自体は、なんとなくネタになるかな、という気持ちでやりましたが、その大元の動機は、ソースコードを調査するというフレームワークを作ったので試してみた、というものです(といっても簡単なものですが)。
我々 Ruby 言語開発者は、「実際に Ruby をどんなふうに使っているのかな?」ということに興味があります。例えば、メソッド名を決めるとき「この名前で被っているものはないかな?」という調査をしたりします。他にも、it
というメソッドはどれくらい、どのように使われているのかな、とかです(Ruby 3.4 で導入予定の機能ですね。See プロと読み解くRuby 3.3 NEWS - STORES Product Blog「無引数 it が警告されるようになった」)。
これまで、gem-codesearch
というコマンドで Ruby gems の全文検索をしていたのですが、だいたい grep 的な使い方なので、それがメソッド名であるか、コメントや文字列の中に入っている内容であるかわかりませんでした。検索結果から、人間が手でフィルタリングしていたりしました。そこで、Prism を用いて構文木を取得すれば、例えば「呼び出されているメソッド名」や「定義されたメソッド名」を明確に取得することが可能です。
さらに、もっと意味に踏み込んで、「どんなふうに利用されているか」という調査をすることも可能になります。例えば、
Bug #20043: `defined?` checks for method existence but only sometimes - Ruby master - Ruby Issue Tracking System では、defined?(expr)
という構文の expr
にはどんな式が入っているかを調べる、ということを行いました。
これらの調査をやってみると、AWS の遅いマシンで2、3時間くらいかかっていたので、イマイチ使い勝手が悪かったという問題点がありました。そこで、手元の速い実マシンでやってみると、全ファイルをパースするのに15分程度でできることがわかりました。結構カジュアルにいろんな探索ができそうです。さらに、全文検索コマンドである gem-codesearch
と組み合わせてフィルタリングすることで、例えば matz
というメソッドは何個定義され、何回呼ばれているか、みたいなことを3分程度で調べることができました(ちなみに、4 calls, 3 defs)。
より高速化するために fork
でプロセス並列に実行するモードも作りました。その結果、今回の集計が 53 分から 10 分まで短縮することができることもわかりました(いや、2度集計する必要はなかったのですが。ベンチマーク的に利用しました)。
というようなものを作ったよという自慢をするためにこの記事を執筆した次第です。
おわりに
本稿では、構文木を用いて全 gem から集計するフレームワークをご紹介し、そのサンプルとして、字面上で「どのメソッドが一番呼ばれているか」、「どのメソッドが一番定義されているか」を調査した結果をご紹介しました。一番呼ばれているのは #[]
で、一番定義されているのは #initialize
でした。聞いてみれば、せやな、って答えだったのではないでしょうか。
調査スクリプトをご紹介していますが、比較的簡単に調査できるもんだな、と思ってもらえると良いと思います。こういうわかりやすい話は現実逃避ちょっとした気分転換に最適なので、皆様もいかがでしょうか。
STORES では、このような役に立たない雑談もしていたりもするので、よかったら働く先の候補としてご検討ください:採用サイト | STORES 株式会社