🐢
Ruby の OpenStruct は本当にパフォーマンスが悪いのか?
はじめに
Ruby3.0から非推奨になった理由の一つにパフォーマンスの悪さがある(Hashの約200倍遅いとか。。)。そこで、本当にパフォーマンスが悪いのかを確認するために計測してみた。
比較対象
Hash、Struct、シンプルなクラスでどのくらいオブジェスト生成の時間が異なるのかをベンチマークで測ってみた。
# Hash
user = {
uuid: SecureRandom.uuid,
name: 'user1',
email: 'user1@email.com',
role: 'admin',
active: true
}
# Struct
StructUser = Struct.new('User', *user.keys)
StructUser.new(*user.values)
# Class
class User
attr_accessor :uuid, :name, :email, :role, :active
def initialize(uuid:, name:, email:, role:, active:)
@uuid = uuid
@name = name
@email = email
@role = role
@active = active
end
end
計測方法
- 複雑なことはやらずにただ計測するだけなので、標準ライブラリのBenchmarkを使用する。
https://docs.ruby-lang.org/ja/latest/library/benchmark.html - Rubyバージョン
- 2.7.6
- 3.0.0
- 3.1.2
require 'benchmark'
Benchmark.bm 10000 do |r|
r.report "OpenStruct" do
OpenStruct.new(**user)
end
r.report "Struct" do
User.new(**user)
end
r.report "Simple User Class" do
User.new(**user)
end
r.report "Hash" do
{**user}
end
end
結果
公式コメントにあったほど差が出なかったが、面白いことにRuby3.0以降から如実に遅くなっている。
また、Ruby3.1.2でわずかであるが改善が見られる。
# 2.7.6
user system total real
OpenStruct 0.000008 0.000003 0.000011 (0.000007)
Struct 0.000006 0.000003 0.000009 (0.000006)
Simple User Class 0.000006 0.000002 0.000008 (0.000006)
Hash 0.000003 0.000001 0.000004 (0.000002)
# 3.0.0
user system total real
OpenStruct 0.000038 0.000019 0.000057 (0.000047)
Struct 0.000000 0.000011 0.000011 (0.000008)
Simple User Class 0.000000 0.000005 0.000005 (0.000004)
Hash 0.000000 0.000004 0.000004 (0.000003)
# 3.1.2
user system total real
OpenStruct 0.000020 0.000010 0.000030 (0.000029)
Struct 0.000005 0.000002 0.000007 (0.000005)
Simple User Class 0.000004 0.000002 0.000006 (0.000004)
Hash 0.000003 0.000002 0.000005 (0.000003)
user, system, totalはCPUが実際に動いている時間
- 単位は ms
- user + system = total
- おそらく、realはCPU時間ではなく、現実の時間
最後に
公式コメントにあったほどの差は出なかったが、それなりにパフォーマンスが悪いことはわかった。一方で、今回はオブジェスト生成の時間だけを計測したが、メソッド呼び出しなどの処理を呼び出すとさらにパフォーマンスが悪化するのかもしれない。また、Ruby3.0以降から如実に悪くなっているため、少なくとも、Ruby3.0以降からOpenStructを使うのはやめよう。。
Discussion