== operator

Jonathan Marler via Digitalmars-d digitalmars-d at puremagic.com
Sat Jan 3 20:43:15 PST 2015


On Sunday, 4 January 2015 at 03:14:31 UTC, Andrei Alexandrescu 
wrote:
> On 1/3/15 5:30 PM, Jonathan Marler wrote:
>> I've recently looked at how the '==' operator works with 
>> classes.  I was
>> disappointed to find that 'a == b' always gets rewritten to:
>>
>> .object.opEquals(a, b);
>>
>> The reason for my disappointment is that this results in 
>> unnecessary
>> overhead. I would think that the compiler would first try to 
>> rewrite the
>> '==' operator using a type-specific opEquals method, then fall 
>> back on
>> the generic version if one did not exist.  Is there a reason 
>> for this?
>
> TDPL has a detailed explanation of that, including a reference 
> to Java's approach. There's less overhead in calling the free 
> function in object (it's inlinable and if e.g. the references 
> are equal there's no virtual call overhead).
>
> Andrei

Can you point me to that detailed explanation?

The problem I see is that in almost all cases the 
opEquals(Object) method will have to perform a cast back to the 
original type at runtime.  The problem is this isn't doing any 
useful work.  The current '==' operator passes the class as an 
Object to a generic opEquals method which eventually gets passed 
to a method that must cast it back to the original type.  Why not 
just have the == operator rewrite the code to call a "typed" 
opEquals method?  Then no casting is necessary.

I wrote a quick performance test to demonstrate the issue.

import std.stdio;
import std.datetime;
class IntWrapper
{
   int x;
   this(int x)
   {
     this.x = x;
   }
   override bool opEquals(Object o)
   {
     IntWrapper other = cast(IntWrapper)o;
     return other && this.x == other.x;
   }
   bool opEquals()(auto ref const IntWrapper other) const
   {
     return this.x == other.x;
   }
}
void main(string[] args)
{
   size_t runCount = 2;
   size_t loopCount = 10000000;
   StopWatch sw;

   IntWrapper x = new IntWrapper(1);
   IntWrapper y = new IntWrapper(1);

   bool result;

   for(auto runIndex = 0; runIndex < runCount; runIndex++) {

     writefln("run %s (loopcount %s)", runIndex + 1, loopCount);

     sw.reset();
     sw.start();
     for(auto i = 0; i < loopCount; i++) {
       result = x.x == y.x;
     }
     sw.stop();
     writefln("  x.x == y.x               : %s microseconds", 
sw.peek.usecs);

     sw.reset();
     sw.start();
     for(auto i = 0; i < loopCount; i++) {
       result = x.opEquals(y);
     }
     sw.stop();
     writefln("  x.opEquals(y)            : %s microseconds", 
sw.peek.usecs);

     sw.reset();
     sw.start();
     for(auto i = 0; i < loopCount; i++) {
       result = x.opEquals(cast(Object)y);
     }
     sw.stop();
     writefln("  x.opEquals(cast(Object)y): %s microseconds", 
sw.peek.usecs);

     sw.reset();
     sw.start();
     for(auto i = 0; i < loopCount; i++) {
       result = x == y;
     }
     sw.stop();
     writefln("  x == y                   : %s microseconds", 
sw.peek.usecs);

   }
}

Compiled with dmd on Windows(x64):
dmd test.d -O -boundscheck=off -inline -release


run 1 (loopcount 10000000)
   x.x == y.x               : 6629 microseconds
   x.opEquals(y)            : 6680 microseconds
   x.opEquals(cast(Object)y): 89290 microseconds
   x == y                   : 138572 microseconds
run 2 (loopcount 10000000)
   x.x == y.x               : 6124 microseconds
   x.opEquals(y)            : 6263 microseconds
   x.opEquals(cast(Object)y): 90918 microseconds
   x == y                   : 132807 microseconds




More information about the Digitalmars-d mailing list