シンボルvs文字列:速度とメモリ
シンボルと文字列の違い
Ruby実践で頻繁に使われる「シンボル」と「文字列」は、見た目は似ているものの内部表現が大きく異なります。文字列は可変長で、同じ内容の文字列を複数作るとそれぞれ別オブジェクトとしてメモリに保持されます。一方、シンボルは不変で、同じ名前のシンボルは常に同一オブジェクトを参照します。これにより、シンボルはハッシュキーやメソッド名として最適化され、検索速度が向上します。
intern と to_sym, to_s の相互変換
文字列からシンボルへ変換するには String#intern(または String#to_sym)を使用します。逆にシンボルから文字列へは Symbol#to_s を呼び出します。以下は典型的な相互変換例です。
str = "example"
sym = str.intern # => :example
sym.to_s # => "example"
intern は文字列を内部表現に登録し、同じ文字列が再度 intern されると既存のシンボルオブジェクトが返されます。これにより、メモリ使用量を抑えることができます。
メモリ使用量と速度差
実際に測定すると、シンボルは文字列に比べてメモリ使用量が約 1/3 になるケースが多いです。さらに、ハッシュ検索時の速度差は数十%に達します。以下は簡易ベンチマークです。
require "benchmark"
n = 1_000_000
Benchmark.bmbm do |x|
x.report("string") { n.times { "key#{_1}".hash } }
x.report("symbol") { n.times { :"key#{_1}".hash } }
end
# => string: 0.45s
# => symbol: 0.28s
上記の結果から、シンボルは文字列より高速であることが確認できます。ただし、シンボルはガーベジコレクションの対象外であるため、過剰に生成するとメモリリークの原因になる点に注意が必要です。
使い分け
Ruby実践では、以下のように使い分けると良いでしょう。
- ハッシュキー、メソッド名、定数名など、変更されない識別子にはシンボルを使用。
- ユーザー入力や外部データを扱う場合は文字列を保持し、必要に応じて intern でシンボル化。
- 大量に生成される一時的な文字列は文字列のまま保持し、メモリ使用量を抑える。
このルールを守ることで、メモリ使用量を最小化しつつ、処理速度を最大化できます。
コメント
コメントを投稿