STORES Product Blog

こだわりを持ったお商売を支える「STORES」のテクノロジー部門のメンバーによるブログです。

Ruby で一番呼ばれたり定義されたりするメソッドはなんでしょう、調べてみました!

テクノロジー部門で Ruby インタプリタ開発をしている笹田です。

Ruby ではメソッドを駆使してプログラミングをします。そんな Ruby を使っていると、一番使われているメソッド一番定義されているメソッド を知りたいと思ったことはありませんか? 私はありませんでした。

が、ものは試しと調べてみました! 調査は、あるタイミングの Ruby の RubyGems で取得できるすべての Gem (の各 Gem の最新版)を集めてきて、その中の .rb ファイルをすべて読み込み、字面上で呼び出されているメソッドと、定義されているメソッドを集計したものです。実際に動かしたときに呼ばれたり定義されたりするメソッドの数の集計ではないことに注意してください(それは、実行しないとわからないので、網羅的な調査は難しいのです)。

ちなみに、この記事は、Ruby のメソッド定義時に仮引数があるとき、それをカッコでくくらないのは私だけなの? - STORES Product Blog の二番煎じです。

調査方法

次のツールを利用しました。

調査結果

あるタイミング(多分先月集めた全 gem のソースコード)のうち、.rb が拡張子である 2,945,839 ファイルから調査しました。すぐに結果を出しますので、自分で考えたい方は、下記を読まずに考えてみてください。

ヒント:メソッドを呼ばれた回数でソートしたもの(上位100件)

ヒント:メソッドを定義された回数でソートしたもの(上位44件)

呼び出されているメソッド

上位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

呼び出される回数の多い上位100件のメソッド

#[] がダントツで多いですね。ほか、テストコードを 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

定義される回数の多い上位44件のメソッド

圧倒的に 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 株式会社