Live data from Hacker News

The cost of dynamic vs. static dispatch in C++

eli.thegreenplace.net

1–10 of 72 posts

Re: The cost of dynamic vs. static dispatch in C++

#2
When you think you can use CRTP instead of virtual dispatch in your program, you didn't need virtual dispatch to begin with... you needed a generic algorithm to operate over your object classes. That's exactly what run_crtp() is, the CRTPInterface class is completely redundant except that it provides some degree of compile-time concept checking (which we'll hopefully get in C++17)

Virtual dispatch is useful for type erasure, when using abstract types from plugins, DLLs or generally "somebody elses code". IMHO, the valid use cases within a standalone program are actually fairly small.

Re: The cost of dynamic vs. static dispatch in C++

#3
This is a nice article and props for including and dissecting generated assembler!

A key thing here is that inlining is what enables zero-cost abstractions in C++. A virtual call is slower than a regular call, but the main problem is that it builds a barrier that effectively stops inlining.

It'll be interesting to see how devirtualization in GCC will do for real world programs.

Re: The cost of dynamic vs. static dispatch in C++

#5

    for (unsigned i = 0; i tick(j);
      }
    }
I wouldn't go quite so far as to say that benchmarks with tight inner loops like this are completely useless, but they are nearly so.

The author is clearly aware that the real world of performance is much bigger & more complex than his simple Petri dish. Credit to him for mentioning that. It's also really refreshing to see him analysing the optimised assembly.

The trouble with this approach is that it's tempting to draw simple conclusions. In this case, you might be tempted to conclude "CRTP always faster than virtual dispatch", when the truth is likely to be much more situation dependent.

I have seen a biggish project go though a lot of effort to switch to CRTP, only to see a negligible performance impact.

Re: The cost of dynamic vs. static dispatch in C++

#6
Instead of devirtualization, a simpler optimization, which would additionally also help in the dynamic case, is simple loop hoisting of the method pointer fetch. Instead of doing

    while(...) {
      (obj->vtable[0])(...)
    }
we could have

    void(*fn)(...) = obj->vtable[0]
    while(...) {
      fn(...)
    }
which would avoid two redirections per inner loop! Actually, I'm almost sure that is what LuaJIT would do, and many other high-level programming languages could perform this optimization as well. However, maybe C is too low-level to be able to do that, and I don't know about C++.

Re: The cost of dynamic vs. static dispatch in C++

#7
I'd like to see a comparison of calling a dynamically linked function call vs a non-dynamically linked virtual call.

Dynamic linking has more indirection than you might expect because the function addresses can't always just be put at the call site during the library load (the places where you would want to write the address can be in code that is read-only mmapped to aid in sharing memory between processes and to avoid loading unused stuff from disk).

Re: The cost of dynamic vs. static dispatch in C++

#8
I've done benchmarks on this fairly recently, and with the functions actually doing a lot of work (ray intersection for a raytracer), I saw practically no difference between CRTP and Virtual Functions:

http://imagine-rt.blogspot.co.uk/2013/08/c-virtual-function-...

And this was with billions of calls to the functions...

Re: The cost of dynamic vs. static dispatch in C++

#9
post #6

Instead of devirtualization, a simpler optimization, which would additionally also help in the dynamic case, is simple loop hoisting of the method pointer fetch. Instead of doing while(...) { (obj->vtable[0])(...) } we could have void(*fn)(...) = obj->vtable[0] while(...) { fn(...) } which would avoid two redirections per inner loop! Actually, I'm almost sure that is what LuaJIT would do, and many other high-level pr…

[deleted]

Re: The cost of dynamic vs. static dispatch in C++

#10

for (unsigned i = 0; i tick(j); } } I wouldn't go quite so far as to say that benchmarks with tight inner loops like this are completely useless, but they are nearly so. The author is clearly aware that the real world of performance is much bigger & more complex than his simple Petri dish. Credit to him for mentioning that. It's also really refreshing to see him analysing the optimised assembly. The trouble with this…

And I have seen projects whose performance was crippled by layers upon layers of endless virtual calls. YMMV ;-)
Post reply on HN