On Thu, 12 Nov 2009 11:14:56 -0500, Steven Schveighoffer
<schvei...@yahoo.com> wrote:
On Thu, 12 Nov 2009 10:29:17 -0500, Andrei Alexandrescu
<seewebsiteforem...@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;
}
}
[ste...@steveslaptop testd]$ time ./testme interface
real 1m18.152s
user 1m16.638s
sys 0m0.015s
[ste...@steveslaptop testd]$ time ./testme virtual
real 1m11.146s
user 1m10.497s
sys 0m0.014s
[ste...@steveslaptop testd]$ time ./testme struct
real 1m5.828s
user 1m5.249s
sys 0m0.011s
[ste...@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