== 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