I read in the documentation for the String class that eql?
is a strict equality operator, without type conversion, and ==
is a equality operator which tries to convert second its argument to a String, and, the C source code for this methods confirms that:
The eql?
source code:
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);
}
The ==
source code:
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);
}
But when I tried to benchmark these methods, I was suprised that ==
is faster than eql?
by up to 20%!
My benchmark code is:
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
And results:
Ruby 1.9.3-p125:
26.420000 0.250000 26.670000 ( 26.820762)
21.520000 0.200000 21.720000 ( 21.843723)
Ruby 1.9.2-p290:
25.930000 0.280000 26.210000 ( 26.318998)
19.800000 0.130000 19.930000 ( 19.991929)
So, can anyone explain why the more simple eql?
method is slower than ==
method in the case when I run it for two similar strings?
Specifically with regard to strings, yes, == is slightly faster than equals , because the first thing the String. equals method does is...a == comparison to see if the string is being compared to itself. If it is, equals() is slower by the cost of a method call.
In many higher level programming languages, however, things are different. Many languages define inequality in terms of equality - to check inequality, you do the equality check, then a second check to see if it's false. This causes equality to be (microscopically) faster in these languages.
So === faster than == in Javascript === compares if the values and the types are the same. == compares if the values are the same, but it also does type conversions in the comparison. Those type conversions make == slower than ===.
!= is much faster. Anything with an exclamation point is given top priority in the Oracle engine.
The reason you are seeing a difference is not related to the implementation of ==
vs eql?
but is due to the fact that Ruby optimizes operators (like ==
) to avoid going through the normal method lookup when possible.
We can verify this in two ways:
Create an alias for ==
and call that instead. You'll get similar results to eql?
and thus slower results than ==
.
Compare using send :==
and send :eql?
instead and you'll get similar timings; the speed difference disappears because Ruby will only use the optimization for direct calls to the operators, not with using send
or __send__
.
Here's code that shows both:
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
Results:
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
If you're the curious, the optimizations for operators are in insns.def
.
Note: this answer applies only to Ruby MRI, I would be surprised if there was a speed difference in JRuby / rubinius, for instance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With