I Didn't Order That, So Why Is It On My Bill, Episode 1
June 22nd, 2009

C++ was designed to ensure you "only pay for what you use," and at that it is mostly successful. But it's not entirely successful, and I thought it might be interesting to explore some counterexamples: C++ features you don't use but still negatively impact performance, and ways in which other languages avoid those issues.

So here's the first episode of I Didn't Order That, So Why Is It On My Bill, to be continued until I can't think of any more.

Inline Functions

The C++ standard says this:

A static local variable in an extern inline function always refers to the same object.

In other words, static variables in inline functions work just like static variables in any function. That's reasonable, because that's probably what you want. That's what statics are for, after all.

But wait, it also says this:

An inline function with external linkage shall have the same address in all translation units.

That's borderline silly. Who cares what the address of the function is? When's the last time you used the function pointer address for anything except calling it? Dollars to donuts says never. You aren't using a function pointer as a hash table key. You aren't comparing the same function via pointers from different files. And if you did, you'd probably have the good sense to ensure the functions are defined exactly once (i.e. not inline). You can easily live without this feature.

So hopefully I've established that you don't use this feature. Now I have to show how you're paying for it anyways. Well, this feature complicates linking. Ordinarily, there should be only one copy of a function, and if the linker finds more than one it gives you a multiple definition error. But with inline functions, the compiler is expected to generates multiple copies of the code and it's up to the linker to sort it out. See vague linkage.

So the linker has more work to do, and link time is increased. Again, who cares? Linking occurs after compilation, and I promised you a runtime cost. But ah - link time is runtime when we're using dynamic libraries!

Every time you start up a program, the linker has to make sure that your implementation of vector<int>::capacity() has the same address as the vector<int>::capacity() defined in libWhoGivesAHoot.dylib just in case you decide to go and compare their function pointers.

And it gets a little worse. You know how class templates have to live in header files? The function definition goes right there in the class declaration, and that makes them inline automatically. So nearly every function in a template gets this inline uniquing treatment. Every template function in a class that cannot or should not be inlined - because its address is taken, because it is recursive, because you're optimizing for size, or simply because it's too darn big and inlining is counterproductive - will incur a launch time cost.

The situation is dire enough that gcc added a "-fvisibility-inlines-hidden" flag. This is a minor violation of the C++ standard, but will improve launch time in cases with a lot of dynamic libraries. In other words, this flag says: I'm not using this feature, please take it off my bill.

C does not have this problem. Why not? C (in the C99 standard) has very different semantics for inline functions. To oversimplify a bit, a C99 inline function acts as sort of an alias for a real function, which must be defined once and only once. Furthermore, inline functions can't have static variables, or have their addresses taken. This approach is more limiting, but doesn't require the linker to bend over backwards, and so avoids the performance penalty of the C++ approach.