gives the programming community -- especially those used to higher-level languages which require less attention to bookkeeping--lots to jeer about. However, there is one myth about C++ that is rather easily debunked -- yet keeps rearing its ugly head.
That is the MythOfCppBloat
-- the claim that C++ offers worse performance than other OO languages, especially those which are typically compiled to native code. Here, performance can refer to execution time, memory usage, or both.
C++ is also compared to CeeLanguage
for performance issues. C++ provides several features that C lacks which incur (or can incur) a performance penalty, most
only incur a penalty if you use them. Among these are:
- Templates. Actually, these are very efficient at runtime; and with modern linkers don't cause code bloat.
- Virtual functions. DynamicDispatch is more expensive than StaticDispatch; however this imposes no additional cost unless used.
- Constructors/Destructors. If you do something silly like explicitly declare a trivial destructor (but don't make it inline), and then allocate an array of thousands of your class, that will hurt performance. If you don't use non-default ctors/dtors, there is no penalty. If you use them properly, the penalty is negligible.
- RunTimeTypeInformation. For each class with a VeeTables, this causes a small increase in code size (a fixed amount per class, not per object). No runtime penalty unless RTTI is used however.
Two C++ features do impose some additional overhead, even if they aren't used.
- MultipleInheritance. Makes DynamicDispatch more complicated--classes that use SingleInheritance but still use DynamicDispatch have to pay for an extra level of indirection, to support MI, on every virtual function call.
- ExceptionHandling. This you pay for as soon as you call your program foo.cc rather than foo.c. All ActivationRecords need additional data structures included, and all function prologues/epilogues need additional code generated by the compiler, so the language can successfully UnwindTheStack? when an exception is thrown. The overhead is minimal (unless an exception is actually throw--throwing exceptions is expensive), but it is present.
Possible causes for this myth:
- Poor toolchain support (see also SufficientlySmartCompiler):
- Many C++ compilers didn't properly support inline (and some, such as GnuCpp, won't inline unless you compile with optimization enabled); inlining is necessary for efficient C++ code
- Early linkers, designed to work well with C code, didn't mesh well with certain C++ features (mainly templates and inlining).
- Many early linkers didn't support inlining very well either, and executables would contain multiple non-inlined copies of the same inline function. Likewise, templates weren't well-supported either, for much the same reason. Modern C++ toolchains no longer have any problem with either issue.
- Early C++ compilers would often produce humongous HelloWorld programs (200k or larger for HelloWorld), some may still do. This is an issue with your compiler and not with C++; the executable probably contains numerous libraries/functions which it simply doesn't need. (In addition, many early C++ implementations implemented iostreams as a thin wrapper on top of stdio; which isn't a good way to do it).
- Many compilers still insist on including lots of extra stuff--system libraries and the like.
- Other new features weren't well supported by many early compilers--with GCC 2.7.2, you could have optimization or exception handling, but not both.
- When C++ first appears, DLLs/shared libraries were still an immature technology; often times you might encounter a small app statically-linked to some large library (one which was poorly designed so that every last .o in the archive got linked to the executable no matter what you did).
- Inappropriate/incorrect coding practices:
- Failure to inline certain small, often called functions--especially constructors, and the assignment and equality operators
- Constructors cannot be virtual, operator = and operator == generally shouldn't be (unless you're doing full-blown DoubleDispatch).
Correct me if I'm wrong (not a C++ expert) but don't you have to include methods in the header to make them inlined? And if so, doesn't that bloat your headers, which slows compilation?
It does bloat headers, but it typically doesn't have a significant impact on compilation speed. Back in the 1980s, everyone and their brother were coming up with schemes for precompiling C headers to speed up compilation. This made sense with 8Mhz processors compiling on floppy disk. Sometimes it helped even on hard disks of the era. It's rare to see such schemes used these days with C or C++ because disks and processors are vastly faster, so the cost of reading, lexing, and parsing headers tends not to be the big issue it used to be.
There are exceptions, of course, with complex systems with millions of lines of header files included in every source file (usually unnecessarily), where the majority of compilation time might involve the header files, but in those bloated environments, it's not clear that the cost of inlined code in the headers is necessarily a large percentage. If it were, I would suspect that far too much were being inlined.
In any case, given the general nature of C++, there's no clear place to put inlined methods other than in headers, despite various non-bloat-related criticms of that aspect of C++ (the major complaint is that it publicizes that code to the world).
The PimplIdiom can often address all of those criticisms.
Some tools are able to inline function calls across module boundaries. Microsoft Visual C++ has had this option for a while now. I first remember seeing it in the 2002 release, but it may have been there in 6.0. They just use a slightly smarter linker. -- MichaelSparks
The phrase "sufficiently smart compiler" usually refers to compilers or compiler features/optimizations which a) don't exist; b) are only found on research compilers and not in production tools, or c) are expensive/dangerous to implement. In other words, bleeding edge stuff. The toolchain improvements described on this
page, enabling efficient C++ code (proper inlining, support of WeakSymbol
s for the linker, properly structured libraries) are all straightforward things to implement, and definitely are not on the bleeding edge of compiler technology.
I'm not sure if that's what was intended... I just wanted to clarify.
Myth: Virtual functions are less efficient
Actually virtual functions are more efficient than runtime evaluation and call of functions. For a simple two-way branching veritual function the comparison should be:
a.Method(); //indirect virtual function call
if(X == Y)
AFAIK, indirect function calls which have to a) dereference a bunch of pointers (object -> vtable pointer -> vtable -> function pointer) to get at the function pointer and b) perform an indirect call are much slower than a simple test and branch and call.
But I guess the processor manufacturers might've done something about the cost of indirect calls since the introduction of C++.
Typically there are only two memory accesses to call a virtual function. First, you retrieve the vtable pointer by looking at the base address of the object plus a fixed offset - that is one memory hit. Then you retrieve the function pointer by looking at the vtable plus a fixed offset - that is the second hit. However, in all implementations I'm aware of, virtual function calls are still slower than the example given above. Simpler code to read and write, but not faster. -- MichaelSparks
True, at least under gcc on the PII 400mhz notebook I happen to be using at the moment. I couldn't resist generating some empirical data, and conditional direct function calls are indeed faster than virtual method calls. To wit, approximately 2.6 seconds vs. 3.4 seconds for 100,000,000 iterations. It would be interesting to know if this is different under other architectures. But for lack of a SCSI adapter to install a new hard drive, I'd try it on my AlphaStation?.