OpenStructがRubyのパフォーマンスを大幅に低下させる理由
Ruby開発者の多くはハッシュ(Hash)を愛用しています。しかし、ハッシュにはいくつかよく知られた弱点があります。Richard氏が「Hashie Considered Harmful」で指摘したように、ハッシュは柔軟すぎることがあるのです。ちょっとしたタイプミスで、意図しないキーへの代入や参照を平気で行ってしまいます。
a = { type: "F150" }
a[:typo] # nil
ハッシュの代替手段として一般的なもの
構造化されたデータを格納するためにハッシュを使っているなら、「実は柔軟性は必要ない。むしろトラブルの元になるだけだ」と判断することもあるでしょう。
代替手段はいくつかあります。ここでは、XY座標のペアを格納する必要がある場面を想定してみましょう。まず考えられるのは、XY座標のペアを保持することだけを目的としたクラスを定義する方法です。
class PointClass # クラス名の末尾にClassをつけるのはおすすめしません :)
attr_accessor :x, :y
def initialize(args)
@x = args.fetch(:x)
@y = args.fetch(:y)
end
end
point_class = PointClass.new(x: 1, y: 2)
point_class.x # => 1
このケースではデータのカプセル化だけが必要なので、より簡潔な選択肢としてStructを使う方法があります。
PointStruct = Struct.new(:x, :y)
point_struct = PointStruct.new(1, 2)
point_struct.x # => 1
3つ目の選択肢はOpenStructです。OpenStructはStructに似た見た目をしていますが、ハッシュのように任意の値を自由に設定できます。
point_os = OpenStruct.new(x: 1, y: 2)
point_os.x # => 1
パフォーマンスへの影響
【2015年7月10日更新】ベンチマークスクリプトがハッシュに対して不公平でした。Patrick Helm氏が指摘してくれたように、初期化に非効率なメソッドを使用していたためです。ハッシュに関する結果は無視してください。ただし、OpenStructが非常に遅いという主な結論自体は依然として有効です。修正版のスクリプトは彼のリポジトリで確認できます。
これら4つの選択肢を並べて見ると、それぞれのパフォーマンスへの影響が気になってきます。少量のデータしか扱わないのであれば、どれを選んでも十分に高速です。しかし、数千〜数百万件のアイテムを処理する場合、ハッシュ・OpenStruct・Struct・クラスのどれを選ぶかが、パフォーマンスに大きな影響を与え始めます。
Honeybadgerでは毎秒数千件の例外がAPIに報告されるため、こうしたパフォーマンス特性の理解は常に重要な関心事です。
そこで、シンプルなベンチマークスクリプトを作成しました。この種の実験にはbenchmark-ips gemが便利です。適切なサンプル数を自動的に算出し、標準偏差もレポートしてくれるからです。
初期化の速度比較
PointClass・PointStruct・Hash・OpenStructの初期化時間を計測したところ、PointClassとPointStructが圧倒的な勝者であることがわかりました。OpenStructの約10倍高速で、ハッシュの約2倍の速さです。

PointClassとPointStructはOpenStructの約10倍高速
この結果は理にかなっています。Structが最もシンプルなので最速であり、OpenStructは最も複雑(ハッシュのラッパー)なので最も遅くなります。とはいえ、その速度差の大きさには驚かされます。
この実験の後、速度が重要になるコードでOpenStructを使うことにはかなり慎重になりました。また、パフォーマンスが重要なコードでハッシュを見かけたら、注意深く目を向けるようになりました。
読み書きの速度比較
初期化とは異なり、値の設定やアクセスに関しては、4つの選択肢すべてがほぼ同等のパフォーマンスを示しました。

読み書きのベンチマークでは、Struct・クラス・ハッシュ・OpenStruct間に大きな差は見られません
ベンチマークスクリプト全文
自分の環境でベンチマークを実行してみたい方は、以下のスクリプトをご利用ください。筆者はOSX上のMRI 2.1で実行しました。他のRuby処理系でのパフォーマンスが気になる方は、Michael Cohen氏がMRI 2.2やJRubyなどの結果をまとめた素晴らしいgistを公開しているので、そちらも参考になります。
require 'benchmark/ips'
require 'ostruct'
data = { x: 100, y: 200 }
PointStruct = Struct.new(:x, :y)
class PointClass
attr_accessor :x, :y
def initialize(args)
@x = args.fetch(:x)
@y = args.fetch(:y)
end
end
puts "\n\nINITIALIZATION =========="
Benchmark.ips do |x|
x.report("PointStruct") { PointStruct.new(100, 200) }
x.report("PointClass") { PointClass.new(data) }
x.report("Hash") { Hash.new.merge(data) }
x.report("OpenStruct") { OpenStruct.new(data) }
end
puts "\n\nREAD =========="
point_struct = PointStruct.new(100, 200)
point_class = PointClass.new(data)
point_hash = Hash.new.merge(data)
point_open_struct = OpenStruct.new(data)
Benchmark.ips do |x|
x.report("PointStruct") { point_struct.x }
x.report("PointClass") { point_class.x }
x.report("Hash") { point_hash.fetch(:x) }
x.report("OpenStruct") { point_open_struct.x }
end
puts "\n\nWRITE =========="
Benchmark.ips do |x|
x.report("PointStruct") { point_struct.x = 1 }
x.report("PointClass") { point_class.x = 1 }
x.report("Hash") { point_hash[:x] = 1 }
x.report("OpenStruct") { point_open_struct.x = 1 }
end
-
ウイルス対策ソフトでPCを高速化する方法|セキュリティと快適さを両立させる5つのポイント
「アンチウイルスソフトを入れるとPCが遅くなる」という誤解を持っている方は少なくありません。確かに、まれに一時的に動作が重く感じられることはありますが、それはPC全体を徹底的にスキャンし、脅威を早期に排除するために必要な処理です。むしろ総合的に見れば、アンチウイルスソフトはPCのパフォーマンス向上に大きく貢献してくれます。 現在、優れた有料・無料のアンチウイルスソフトが数多く登場しています。本記事では、その中でも特に評判の高い「Systweak Antivirus」を例に挙げて解説します。詳細なレビュー記事も公開しているので、実際の動作を確認したい方はぜひ参考にしてください。 アンチウイルスソ
-
Androidアプリを休止状態にしてパフォーマンスとバッテリー寿命を向上させる方法
時間が経つにつれて、ほとんどのAndroidデバイスは、バックグラウンドで動作し続けるアプリのせいで動作が遅くなります。技術がここまで進歩した現代において、これは本来あるべき姿ではありません。Androidアプリは賢く自ら終了し、メモリを解放できるべきものです。 しかし現実にはそううまくいきません。そこで、RAMを解放しバッテリー駆動時間を延ばすためには、アプリを休止状態(ハイバネート)にする、RAMをブーストする、ジャンクファイルを削除するなど、さまざまな対策が必要になります。簡単に言えば、パフォーマンスを総合的に向上させるオールラウンダーなアプリが必要なのです。その名も「Smart Pho