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?
When doing benchmarks, don't use
times
, because that creates a closureRUN_COUNT
times. The extra time taken as a result affects all benchmarks equally in absolute terms, but that makes it harder to notice a relative difference:gives
The third method,
eql?
is normally used to test if two objects have the same value as well as the same type. For example:So I thought since
eql?
does more checking it would be slower, and for strings it is, at least on my Ruby 1.93. So I figured it must be type dependent and did some tests. When integer and floats are comparedeql?
is a bit faster. When integers are compared==
is much faster, until x2. Wrong theory, back to start.The next theory: comparing two values of the same type will be faster with one of both proved to be true, in the case they are of the same type
==
is always faster,eql?
is faster when types are different, again until x2.Don't have the time to compare all types but I'm sure you'll get varying results, although the same kind of comparison always gives similar results. Can somebody prove me wrong?
Here are my results from the test of the OP:
The reason you are seeing a difference is not related to the implementation of
==
vseql?
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 toeql?
and thus slower results than==
.Compare using
send :==
andsend :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 usingsend
or__send__
.Here's code that shows both:
Results:
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.