17

eql?型変換なしの厳密な等値演算子で==あり、2 番目の引数を文字列に変換しようとする等値演算子である String クラスのドキュメントを読みました。このメソッドの C ソース コードは、次のことを確認しています。

eql?ソースコード:

static VALUE
rb_str_eql(VALUE str1, VALUE str2)
{
    if (str1 == str2) return Qtrue;
    if (TYPE(str2) != T_STRING) return Qfalse;
    return str_eql(str1, str2);
}

==ソースコード:

VALUE
rb_str_equal(VALUE str1, VALUE str2)
{
    if (str1 == str2) return Qtrue;
    if (TYPE(str2) != T_STRING) {
        if (!rb_respond_to(str2, rb_intern("to_str"))) {
            return Qfalse;
        }
        return rb_equal(str2, str1);
    }
    return str_eql(str1, str2);
}

しかし、これらの方法のベンチマークを試みたところ、最大 20%==も高速であることに驚きました。eql?私のベンチマークコードは次のとおりです。

require "benchmark"

RUN_COUNT = 100000000
first_string = "Woooooha"
second_string = "Woooooha"

time = Benchmark.measure do
  RUN_COUNT.times do |i|
    first_string.eql?(second_string)
  end
end
puts time

time = Benchmark.measure do
  RUN_COUNT.times do |i|
    first_string == second_string
  end
end
puts time

そして結果:

ルビー 1.9.3-p125:

26.420000   0.250000  26.670000 ( 26.820762)
21.520000   0.200000  21.720000 ( 21.843723)

ルビー 1.9.2-p290:

25.930000   0.280000  26.210000 ( 26.318998)
19.800000   0.130000  19.930000 ( 19.991929)

では、 2 つの同様の文字列に対して実行した場合に、より単純なeql?メソッドがメソッドよりも遅い理由を誰か説明できますか?==

4

3 に答える 3

2
equal? is reference equality
== is value equality
eql? is value and type equality

3 番目の方法eql?は、通常、2 つのオブジェクトが同じ値と同じ型を持っているかどうかをテストするために使用されます。例えば:

puts "integer == to float: #{25 == 25.0}"
puts "integer eql? to float: #{25.eql? 25.0}"

gives:

Does integer == to float: true
Does integer eql? to float: false

そのため、チェックを増やすと遅くなると思いeql?ました。文字列の場合、少なくともRuby 1.93ではそうです。したがって、型に依存する必要があると考え、いくつかのテストを行いました。整数と浮動小数点数を比較eql?すると、少し速くなります。整数を比較する場合は==、x2 までははるかに高速です。間違った理論、最初に戻ります。

次の理論: 同じ型の 2 つの値を比較すると、両方の値のいずれかが true であることが証明された場合に高速になります。同じ型の場合==は常に高速でeql?あり、型が異なる場合は x2 まで高速です。

すべてのタイプを比較する時間はありませんが、さまざまな結果が得られると確信していますが、同じ種類の比較では常に同様の結果が得られます. 誰かが私が間違っていることを証明できますか?

OPのテストからの私の結果は次のとおりです。

 16.863000   0.000000  16.863000 ( 16.903000) 2 strings with eql?
 14.212000   0.000000  14.212000 ( 14.334600) 2 strings with ==
 13.213000   0.000000  13.213000 ( 13.245600) integer and floating with eql?
 14.103000   0.000000  14.103000 ( 14.200400) integer and floating with ==
 13.229000   0.000000  13.229000 ( 13.410800) 2 same integers with eql?
  9.406000   0.000000   9.406000 (  9.410000) 2 same integers with ==
 19.625000   0.000000  19.625000 ( 19.720800) 2 different integers with eql?
  9.407000   0.000000   9.407000 (  9.405800) 2 different integers with ==
 21.825000   0.000000  21.825000 ( 21.910200) integer with string with eql?
 43.836000   0.031000  43.867000 ( 44.074200) integer with string with ==
于 2012-04-24T20:46:26.463 に答える
2

ベンチマークを行うときは、使用しないでください。timesこれにより、閉鎖RUN_COUNT時間が作成されます。結果としてかかる余分な時間は、絶対的にはすべてのベンチマークに等しく影響しますが、相対的な違いに気付くのが難しくなります:

require "benchmark"

RUN_COUNT = 10_000_000
FIRST_STRING = "Woooooha"
SECOND_STRING = "Woooooha"

def times_eq_question_mark
  RUN_COUNT.times do |i|
    FIRST_STRING.eql?(SECOND_STRING)
  end
end

def times_double_equal_sign
  RUN_COUNT.times do |i|
    FIRST_STRING == SECOND_STRING
  end
end

def loop_eq_question_mark
  i = 0
  while i < RUN_COUNT
    FIRST_STRING.eql?(SECOND_STRING)
    i += 1
  end
end

def loop_double_equal_sign
  i = 0
  while i < RUN_COUNT
    FIRST_STRING == SECOND_STRING
    i += 1
  end
end

1.upto(10) do |i|
  method_names = [:times_eq_question_mark, :times_double_equal_sign, :loop_eq_question_mark, :loop_double_equal_sign]
  method_times = method_names.map {|method_name| Benchmark.measure { send(method_name) } }
  puts "Run #{i}"
  method_names.zip(method_times).each do |method_name, method_time|
    puts [method_name, method_time].join("\t")
  end
  puts
end

与える

Run 1
times_eq_question_mark    3.500000   0.000000   3.500000 (  3.578011)
times_double_equal_sign   2.390000   0.000000   2.390000 (  2.453046)
loop_eq_question_mark     3.110000   0.000000   3.110000 (  3.140525)
loop_double_equal_sign    2.109000   0.000000   2.109000 (  2.124932)

Run 2
times_eq_question_mark    3.531000   0.000000   3.531000 (  3.562386)
times_double_equal_sign   2.469000   0.000000   2.469000 (  2.484295)
loop_eq_question_mark     3.063000   0.000000   3.063000 (  3.109276)
loop_double_equal_sign    2.109000   0.000000   2.109000 (  2.140556)

Run 3
times_eq_question_mark    3.547000   0.000000   3.547000 (  3.593635)
times_double_equal_sign   2.437000   0.000000   2.437000 (  2.453047)
loop_eq_question_mark     3.063000   0.000000   3.063000 (  3.109275)
loop_double_equal_sign    2.140000   0.000000   2.140000 (  2.140557)

Run 4
times_eq_question_mark    3.547000   0.000000   3.547000 (  3.578011)
times_double_equal_sign   2.422000   0.000000   2.422000 (  2.437422)
loop_eq_question_mark     3.094000   0.000000   3.094000 (  3.140524)
loop_double_equal_sign    2.140000   0.000000   2.140000 (  2.140557)

Run 5
times_eq_question_mark    3.578000   0.000000   3.578000 (  3.671758)
times_double_equal_sign   2.406000   0.000000   2.406000 (  2.468671)
loop_eq_question_mark     3.110000   0.000000   3.110000 (  3.156149)
loop_double_equal_sign    2.109000   0.000000   2.109000 (  2.156181)

Run 6
times_eq_question_mark    3.562000   0.000000   3.562000 (  3.562386)
times_double_equal_sign   2.407000   0.000000   2.407000 (  2.468671)
loop_eq_question_mark     3.109000   0.000000   3.109000 (  3.124900)
loop_double_equal_sign    2.125000   0.000000   2.125000 (  2.234303)

Run 7
times_eq_question_mark    3.500000   0.000000   3.500000 (  3.546762)
times_double_equal_sign   2.453000   0.000000   2.453000 (  2.468671)
loop_eq_question_mark     3.031000   0.000000   3.031000 (  3.171773)
loop_double_equal_sign    2.157000   0.000000   2.157000 (  2.156181)

Run 8
times_eq_question_mark    3.468000   0.000000   3.468000 (  3.656133)
times_double_equal_sign   2.454000   0.000000   2.454000 (  2.484296)
loop_eq_question_mark     3.093000   0.000000   3.093000 (  3.249896)
loop_double_equal_sign    2.125000   0.000000   2.125000 (  2.140556)

Run 9
times_eq_question_mark    3.563000   0.000000   3.563000 (  3.593635)
times_double_equal_sign   2.453000   0.000000   2.453000 (  2.453047)
loop_eq_question_mark     3.125000   0.000000   3.125000 (  3.124900)
loop_double_equal_sign    2.141000   0.000000   2.141000 (  2.156181)

Run 10
times_eq_question_mark    3.515000   0.000000   3.515000 (  3.562386)
times_double_equal_sign   2.453000   0.000000   2.453000 (  2.453046)
loop_eq_question_mark     3.094000   0.000000   3.094000 (  3.140525)
loop_double_equal_sign    2.109000   0.000000   2.109000 (  2.156181)
于 2012-04-26T00:02:08.000 に答える