Semantics of toString

Steven Schveighoffer schveiguy at yahoo.com
Thu Nov 12 09:20:41 PST 2009


On Thu, 12 Nov 2009 11:14:56 -0500, Steven Schveighoffer  
<schveiguy at yahoo.com> wrote:

> On Thu, 12 Nov 2009 10:29:17 -0500, Andrei Alexandrescu  
> <SeeWebsiteForEmail at erdani.org> wrote:
>
>> I think the cost of calling through the delegate is roughly the same as  
>> a virtual call.
>
> Not exactly.  I think you are right that struct member calls are faster  
> than delegates, but only slightly.  The difference being that a struct  
> member call does not need to load the function address from the stack,  
> it can hard-code the address directly.
>
> However, virtual calls have to be lower performing because you are doing  
> two indirections, one to the class vtable, then one to the function  
> address itself.  Plus those two locations are most likely located on the  
> heap, not the stack, and so may not be in the cache.

Some rudamentary attempts at benchmarking:

testme.d:

struct S
{
     void foo(int x){}
}

interface I
{
     void foo(int x);
}

class C : I
{
     void foo(int x){}
}

const loopcount = 10_000_000_000L;

void doVirtual()
{
     C c = new C;
     for(auto x = loopcount; x > 0; x--)
         c.foo(x);
}

void doInterface()
{
     I i = new C;
     for(auto x = loopcount; x > 0; x--)
         i.foo(x);
}

void doDelegate()
{
     auto d = new C;
     auto dg = &d.foo;
     for(auto x = loopcount; x > 0; x--)
         dg(x);
}

void doStruct()
{
     S s;
     for(auto x = loopcount; x > 0; x--)
         s.foo(x);
}

void main(char[][] args)
{
     switch(args[1])
     {
         case "virtual":
             doVirtual();
             break;
         case "interface":
             doInterface();
             break;
         case "struct":
             doStruct();
             break;
         case "delegate":
             doDelegate();
             break;
     }
}


[steves at steveslaptop testd]$ time ./testme interface

real	1m18.152s
user	1m16.638s
sys	0m0.015s
[steves at steveslaptop testd]$ time ./testme virtual

real	1m11.146s
user	1m10.497s
sys	0m0.014s
[steves at steveslaptop testd]$ time ./testme struct

real	1m5.828s
user	1m5.249s
sys	0m0.011s
[steves at steveslaptop testd]$ time ./testme delegate

real	1m10.464s
user	1m9.856s
sys	0m0.010s


According to this, delegates are slightly faster than virtual calls, but  
not by much.  By far a direct call is faster, but I was surprised at how  
little overhead virtual calls add in relation to the loop counter.  I had  
to use 10 billion loops or else the difference was undetectable.

I used dmd 1.046 -release -O (the -release is needed to get rid of the  
class method checking the invariant every call).

The relative assembly for calling a virtual method is:

mov	ECX,[EBX]
mov	EAX,EBX
push	dword ptr -8[EBP]
call	dword ptr 014h[ECX]

and the assembly for calling a delegate is:

push	dword ptr -8[EBP]
mov	EAX,-010h[EBP]
call	EBX

-Steve



More information about the Digitalmars-d mailing list