🐢

Ruby の OpenStruct は本当にパフォーマンスが悪いのか?

2022/10/02に公開

はじめに

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

計測方法

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