我在string类的文档中看到,eql?是一个严格的相等运算符,没有类型转换,==是一个相等运算符,它尝试将第二个参数转换为字符串,并且,此方法的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);
}

但当我尝试对这些方法进行基准测试时,我惊讶地发现==eql?快20%!
我的基准代码是:
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)

那么,有人能解释为什么在我为两个类似的字符串运行eql?方法时,更简单的==方法比方法慢吗?

最佳答案

您看到差异的原因与==vseql?的实现无关,而是因为ruby优化了操作符(如==)以尽可能避免进行常规的方法查找。
我们可以通过两种方式进行验证:
==创建别名并调用它。您将得到与eql?相似的结果,因此结果比==慢。
与使用send :==send :eql?进行比较,您将得到类似的计时;速度差异消失了,因为ruby只使用优化直接调用操作符,而不使用send__send__
下面的代码显示了这两种情况:

require 'fruity'
first = "Woooooha"
second = "Woooooha"
class String
  alias same_value? ==
end

compare do
  with_operator   { first == second }
  with_same_value { first.same_value? second }
  with_eql        { first.eql? second }
end

compare do
  with_send_op    { first.send :==, second }
  with_send_eql   { first.send :eql?, second }
end

结果:
with_operator is faster than with_same_value by 2x ± 0.1
with_same_value is similar to with_eql
with_send_eql is similar to with_send_op

如果你好奇的话,操作符的优化在insns.def中。
注意:这个答案只适用于ruby mri,例如,如果jruby/rubinius中存在速度差异,我会感到惊讶。

关于ruby - 为什么==比eql快?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10257096/

10-11 23:04