10

Is it worth it to avoid polymorphism in order to gain performance?

I read in Stroustrup's book that polymorphic function call is within 25% more expensive than an ordinary function call. Does this mean I should avoid polymorphism whenever possible? I mean should I always be thinking something like: OK polymorphism would solve this problem but it might mess up performance bla bla... Or given the power of modern CPUs, I should not even dare to think that?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
user3111311
  • 7,583
  • 7
  • 33
  • 48
  • as long as you don't need ultra low latency the cost of a function call is negligible – 4pie0 Jan 16 '14 at 01:58
  • 2
    @D.Shawley : This question is about virtual functions, not virtual inheritance -- entirely different things. – ildjarn Jan 16 '14 at 02:03
  • 2
    @user3111311 - Do not get into the habit of micro optimisation. You end up spending a lot of effort for little gain. Use profiling tools to find out where things are slow. Remember the 80-20 rule. Away 25% of a small amount is not a lot to worry about. – Ed Heal Jan 16 '14 at 02:06
  • 1
    Why are there 3 votes to close this as a question about virtual **inheritance**? If you don't know the difference between virtual calls and virtual inheritance, you shouldn't be voting in C++ questions. – MSalters Jan 16 '14 at 10:56
  • Did you know that a call of a virtual function is not necessarily a virtual function call? Modern compilers with sufficient information to know the exact object type will resolve the call at compile time. This even means it can subsequently be inlined. Also, compiler writes can optimize the virtual call mechanism. they know it's a commonly used mechanism. Your own alternative code won't benefit from such specific optimizations. – MSalters Jan 16 '14 at 11:02
  • _Did anyone actually read the answers to the virtual inheritance question?_ The answers include links to various analysis of virtual method call performance that happen to include the cost of method and attribute references induced by virtual inheritance. – D.Shawley Jan 18 '14 at 14:07

6 Answers6

13

I read in Stroustrup's book that polymorphic function call is within 25% more expensive than an ordinary function call. Does this mean I should avoid polymorphism whenever possible?

No, absolutely not! A non-virtual call is extremely fast, because the call itself is usually a single instruction. A virtual call requires an additional level of indirection; that's where the alleged 25% are coming from, but that is "pure overhead", i.e. one that you measure when you compare calls of two parameterless functions. Once you start adding parameters, especially ones that require copying, the gap between the two overheads is going to shrink: for example, passing a string by value to a virtual and a non-virtual function would make this gap very hard to measure.

Moreover, it is only the call instructions themselves that are more expensive, not the function. The overhead of calling a function represents a small percentage of the overall timing of the function. The longer the function, the smaller percentage the overhead of the call is representing.

Overall, you should strive for understandability of your code. If adding a subclass with virtual functions makes it easier to understand your design, then by all means you should use subclassing. The hardware on which your code runs becomes faster every year, so in the long run, readability wins.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
11

I read in Stroustrup's book that polymorphic function call is within 25% more expensive than an ordinary function call.

This might be the case (probably a ball-park figure), but it's not that important. Even if a virtual function call is 25% more expensive than an ordinary function call, this is still just the cost of the function call, not the cost of the function execution. I just wanted to make that precision. The cost of the function call will often be much smaller than the cost of the function execution. But watch out, it's not always insignificant either, if you abuse polymorphism and have it for all functions, even the most trivial ones, then that extra overhead can add up very quickly.

Most importantly though, virtual function calls have to be dispatched through a function pointer, and function pointers mean no inlining, or any form of cross-contextual optimization. That is usually where most of the slow down will come from, i.e., you should not be comparing a "virtual function call" to a "function call", but rather a "virtual function call" to a "statically analyzable, potentially inline-able, lto-optimizable function call".

Is it worth it to avoid polymorphism in order to gain performance?

In C++, and in programming in general, whenever there's a price, there's a gain, and whenever there's a gain, there's a price. It's that simple. Dynamic polymorphism comes with a price in the form of (1) overhead on function calls, (2) blocking static analysis and optimizations, (3) often requiring heap-allocated objects and extra indirection (pointers, references, etc.), and so on, just to name a few. However, you also make substantial gains in the form of (1) run-time flexibility, (2) scalability (disjoint unions of problem domains), (3) reduced compilation times, (4) less code repetition, and so on.

The point is, if you want the things that polymorphism buys you, then you should use it. But if you don't need those things (and you'd be surprised how often you don't need those things), then you shouldn't pay an unnecessary price for it. It's that simple.

For more details on the alternatives and trade-offs, I suggest this article.

Mikael Persson
  • 18,174
  • 6
  • 36
  • 52
1

You Aren't Gonna Need It is the rule of thumb to apply, here.

Any one instance of polymorphism is unlikely to slow your program down enough to be worth worrying about, so you should not hesitate to use polymorphism when it is the right tool for the immediate situation. However, if you make everything polymorphic because it might be useful later, then your program will slow down enough to worry about, because now that 25% (or whatever it actually is) penalty applies to every function call, and Amdahl's law is no longer your friend.

Also, it is much harder to remove polymorphism from a program that has too much of it, than it is to add polymorphism to a program that doesn't have enough. Adding polymorphism is a local change. Removing it involves global searches to make sure you got all the implementations.

zwol
  • 135,547
  • 38
  • 252
  • 361
1

It is more expensive than an ordinary function call, but it has its own advantage. Polymorphism is one of the most powerful thing for all OO language. With polymorphism, you can implement several design pattern fairly easy and save you a lot of time. And it keeps the code clean, elegant, reusable, easy to extend and maintain. This is the big advantage. So it is really a tradeoff.

Hunter
  • 151
  • 1
  • 14
1

The virtual table lookup overhead is not a problem in "most" cases. That being said, if efficiency is a requirement:

  • For something like a visitor pattern, you can perhaps use a templated solution instead of a virtual function-based one.
  • One should also consider static polymorphism, with the Curiously Recurring Template Pattern.

Keep in mind that this is important only if you work on a "sensitive" system like a logging or monitoring interface, or algorithms which need to call different callbacks repeatedly, such as an A* or a behavior tree.

armand
  • 11
  • 1
0

As with many questions on performance/efficiency, you often have to evaluate it on a case-by-case basis. It's can be a trade-off between run-time performance and general programming issues, such as versatility and maintainability.

With that said, avoiding such a major language feature based on hypothetical performance concerns is probably a bad idea. The impact of any individual function call (even an indirect one) is very small. You'd have to be calling it extremely frequently for it to have a significant impact compared to other typical bottlenecks.

If there is a significant performance issue relating to a specific function call then (in my experience) other adjustments to the algorithm tend to be more useful. For example, pre-fetching or caching the data, or finding other ways to reduce how often it's called.

Peter Bloomfield
  • 5,578
  • 26
  • 37