日記帳

日記です。

String オブジェクトを破壊的に空文字列化(2)

http://d.hatena.ne.jp/sa-y/20051221#1135158425 の続き.

他に思い付かなかったのでとりあえず benchmark.rb してみる.
以下の4つの処理を試しました.

  1. str.replace('')
  2. str.slice!(0..-1)
  3. str[0..-1] = ''
  4. str.sub!(/.*/, '')

コードは以下の通りです.

require 'benchmark'

NUMBER_OF_STRING = 200000

def create_data_array(string_length)
	original_string = '0' * string_length
	data_array = []
	NUMBER_OF_STRING.times(){ |i|
		data_array << original_string.dup
	}
	data_array
end

Benchmark::bm(25){ |x|
	[10, 100, 1000, 2000].each(){ |length|
		puts "----- String#length : #{length}"
		GC.start()
		data_array = create_data_array(length)
		x.report("str.replace('')") {
			data_array.each(){ |str| str.replace('') }
		}

		GC.start()
		data_array = create_data_array(length)
		x.report("str.slice!(0..-1)") {
			data_array.each(){ |str| str.slice!(0..-1) }
		}

		GC.start()
		data_array = create_data_array(length)
		x.report("str[0..-1] = ''") {
			data_array.each(){ |str| str[0..-1] = '' }
		}

		GC.start()
		data_array = create_data_array(length)
		regexp = Regexp::compile('.*')
		x.report("str.sub!(/.*/, '')") {
			data_array.each(){ |str| str.sub!(regexp, '') }
		}
	}
}

実行結果は以下の通り.

                               user     system      total        real
----- String#length : 10
str.replace('')            0.410000   0.030000   0.440000 (  0.441957)
str.slice!(0..-1)          0.610000   0.020000   0.630000 (  0.624024)
str[0..-1] = ''            0.350000   0.020000   0.370000 (  0.364398)
str.sub!(/.*/, '')         0.570000   0.010000   0.580000 (  0.579559)
----- String#length : 100
str.replace('')            0.330000   0.050000   0.380000 (  0.381891)
str.slice!(0..-1)          0.680000   0.070000   0.750000 (  0.746998)
str[0..-1] = ''            0.360000   0.060000   0.420000 (  0.418298)
str.sub!(/.*/, '')         1.000000   0.040000   1.040000 (  1.043050)
----- String#length : 1000
str.replace('')            0.380000   0.020000   0.400000 (  0.395453)
str.slice!(0..-1)          1.390000   0.040000   1.430000 (  1.430818)
str[0..-1] = ''            0.420000   0.010000   0.430000 (  0.425488)
str.sub!(/.*/, '')        35.990000   0.100000  36.090000 ( 36.095320)
----- String#length : 2000
str.replace('')            0.380000   0.020000   0.400000 (  0.401155)
str.slice!(0..-1)          2.110000   0.070000   2.180000 (  2.179827)
str[0..-1] = ''            0.370000   0.060000   0.430000 (  0.432991)
str.sub!(/.*/, '')        75.550000   0.220000  75.770000 ( 75.787502)

ずいぶんと誤差がありそうな気がするけど大体の傾向はつかめます.
1. 3. の方法は文字列の長さに影響されない、または無視できる範囲の影響しかなさそう.2. 4. はレシーバの長さが長いほど処理時間が長くなる模様.

str.sub!(/.*/, '') が遅いのは予想通りですが str.slice!(0..-1) も遅くなるのですねぇ…. あ… String#slice!() は取り除いた部分文字列を返す,という仕様だからですか.毎回元の文字列と同じ内容の String オブジェクトを生成するから遅くなるのですね.

とりあえずの結論はこんな感じですか…

class String
	unless method_defined?(:clear) then
		def clear() replace('') end
	end
end