C++ Overheads

Recently IAR have finally released full support for C++ (adding exceptions and RTTI) to their family of cross compilers. Initially the kickstart (free) version had not had exceptions and RTTI enabled, however with the release of version 6.10.2 this has now been rectified.

We currently use the IAR compilers on our training courses, targeting an NXP LPC2129 (ARM7TDMI) based systems. As part of verifying that the previous version’s (v5.41) projects still work with v6.10, I decided to investigate the potential overheads of full C++ in this environment (I’m pleased to say all projects worked under v6.10 without modification – phew).

Here are my preliminary findings and I’ll add to them as I investigate further:

First off, I created a project based on the C++ main project, giving the following code:

int main()
{
return 0;
}

[Yes I know, return 0 isn’t necessary and it’s perfectly legal to have “void main()” in C++]

First off, the default language selection is still “Extended Embedded C++”, so I needed to change this to full C++. All build numbers are based on a Debug setting and I/O set to semi-hosting (I/O via debugger terminal window).

image

The build then gave (in bytes):

  • code – 296
  • const – 1
  • data – 8704

The data size surprised me, but by checking the default settings in the Linker configuration file, the default stack size was set to 0x2000 (8192). By changing this to 0x400 (1024) the data requirement dropped to 1536 bytes. I haven’t, as yet, dug down to where the other 500+ bytes are coming from (for another day).

Next step was try the “mandatory” hello world program:

#include <iostream>

int main()
{
std::cout << “hello world\n”;
return 0;
}

Building this (Stack back at 0x2000), gave the following:

  • code – 17357
  • const – 2571
  • data – 42196

Whoa!!! What the… almost 17k of code and 42k of data for hello world; what’s going on here?

Again, the default Linker settings are skewing the values. This time the culprit was the default for the Heap (free store) set at 0x8000(32768). Stack and heap combine account for 40960 bytes. Again I checked this by setting the Heap to 0x2000 and sure enough the data requirement dropped to 17620 bytes (Stack+Heap = 16384).

From these values two things can be deduced:

  1. The heap memory is only allocated if fully linked program requires it
  2. The run-time library (RTL) I/O stream <iostream> makes use of the heap

To confirm point 1 above, I proceeded to build the following program:

int main()
{
volatile int* p = new int;
delete p;
return 0;
}

Sure enough I got the results I expected.

Stack Heap code const data
0x2000 0x8000 1 083 17 41 512
0x2000 0x2000 1 083 17 16 936
0x400 0x400 1 083 17 2 600

back to the hello world program. With Full C++ enabled and both stack and heap set at 0x2000, the baseline numbers are:

  • code – 17357
  • const – 2571
  • data – 17620

Disabling exceptions, RTTI and static object destruction (pretty much extended embedded C++) gave:

  • code – 7600
  • const – 432
  • data – 17589

So, the basic overhead for Full C++ (due to <iostream>) is around 10k of code space. Breaking this down further:

Exceptions RTTI ~static code const data
y y y 17 357 2 571 17 620
n n n 7 600 432 17 589
y n n 16 604 2 471 17 604
n y n 7 600 432 17 589
n n y 7 600 432 17 589

As expected, the bulk of the overhead comes from exception support (in a future post I plan to explain the C++ exception model on the ARM). You wouldn’t expect any overhead from just RTTI (as we have no classes) or static object destruction (as we have no static objects). Again, the overhead for these will be looked at in a future post.

Notice however, exceptions without RTTI & ~static differs from exceptions with RTTI and ~static. This is expected as, of course, the C++ exception model is class based (e.g. ios_base::failure) and cin, cout, cerr and clog are static objects.

Finally, I compared using cout against good old printf (stack & heap at 0x2000).

Stream Exceptions RTTI ~static code const data
cout y y y 17 357 2 571 17 620
printf y y y 8 846 70 8 712

So using printf in preference to cout halves the code and data requirements. In addition IAR allow you to adjust the size of RTL by selecting different Printf formatters:

image

The options are; Full, Large, Small and Tiny. When using cout changing the formatter had no effect on code or data size. However, when only using printf the following values were observed:

Formatter Exceptions RTTI ~static code const data
Full y y y 8 846 70 8 712
Large y y y 7 942 54 8 712
Small y y y 2 949 47 8 712
Tiny y y y 1 902 46 8 712

So, as a first pass, there we have it. There are many unanswered questions regarding why the memory is being allocated, and further investigation of RTTI and ~static.

Niall Cooling

Director at Feabhas Limited
Co-Founder and Director of Feabhas since 1995.
Niall has been designing and programming embedded systems for over 30 years. He has worked in different sectors, including aerospace, telecomms, government and banking.
His current interest lie in IoT Security and Agile for Embedded Systems.
Dislike (0)

About Niall Cooling

Co-Founder and Director of Feabhas since 1995. Niall has been designing and programming embedded systems for over 30 years. He has worked in different sectors, including aerospace, telecomms, government and banking. His current interest lie in IoT Security and Agile for Embedded Systems.
This entry was posted in C/C++ Programming. Bookmark the permalink.

10 Responses to C++ Overheads

  1. Dan says:

    Niall,

    Good post. I chuckled a little when I read the (innocent, accidental) typo which described v5.41 as "pervious" - I had a customer not too long ago struggle with something in that version (or one very close to it). The customer used much fouler language than "pervious"!

    Also, one slight correction: though I'm not much of language pedant, the whole issue about the return type of main() in C++ is something that seems to flare up every now & then on newsgroups, forums, etc. In C++, a conforming program must have main() returning "int", not void. As you correctly stated, you can omit the "return 0", however.

    Don't just take my word for it... this comes right from "the man" himself:

    http://www2.research.att.com/~bs/bs_faq2.html#void-main

    (and I'm sure it's in the standard, too 😉 )

    Like (0)
    Dislike (0)
  2. Chris Hills says:

    The problem with int main is there is nothing to return to in a self hosted system. It is a fallacy in that you must have int main in C... this is only required if there is an OS to return to. Actually in self hosted systems you don't actually have to call it main! So The Man is wrong on that for C90 and C99.

    However I don't know C++ well enough but as he designed the initial language 30 (?) years ago he should know for C++ unless they have changed it.

    The interesting point that Niall highlights is the well known "C++ bloat" in fact a lot of it seems to it come down to how you set up the compiler and linker. You have to know your tools.

    Like (0)
    Dislike (0)
  3. Interesting article, Niall. I knew that inclusion of exception handling created overhead (whether it's used or not) but I'm staggered by the amount of overhead. However, I'm relieved that the data (RAM) overhead is relatively small.

    From the tables in your article, I conclude that the code-space overhead for exceptions is roughly proportional to the amount of code (in these cases, library code), making use of exceptions.

    I look forward to your explanation of the ARM exception model.

    Like (0)
    Dislike (0)
  4. Pingback: Tweets that mention Sticky Bits » Blog Archive » C++ Overheads -- Topsy.com

  5. Hi Dan,
    Thanks for feedback. Yes the "pervious" was innocent 😉
    When the debate about void main() vs. int main() comes up I always ask people to go and read the spec (whether C or C++) and understand the definition for a "freestanding" implementation. For a "freestanding" implementation, void main() is perfectly legal; actually you don't need main at all (even "the man" is ignoring this, his comments only apply to "hosted" applications). In the link you supplied, the pertinent comment is "The int returned by main() is a way for a program to return a value to "the system" that invokes it.". As Chris points out in his comment, in an embedded application there is no "system" to return to.
    Thanks again,
    Niall.

    Like (0)
    Dislike (0)
  6. Gary Lynch says:

    I read a review like this for C some time back and recall
    that any function that calls printf invites overhead, as
    that is one of the few system functions that support
    variable-length parameter lists, and much of the code size
    goes to support this property.

    There is rarely a need for an embedded app to exploit said
    feature so it is not a valid test of compiler efficiency.

    Don't know if cout inherits this property, but it's worth
    investigating.
    ============================================================
    Gary Lynch printf("gary.lynch%cieee%corg" 55+9, 55-9);
    ============================================================

    Like (0)
    Dislike (0)
  7. Hi Gary,

    Yes you're quite right printf, etc. typically won't appear in embedded code, so no not a good test. The initial post was intended to be certain observations. I intend looking into those areas that will cause overheads (exceptions, RTTI, STL, etc.) and trying to gauge their overheads.
    Thanks,
    Niall.

    Like (0)
    Dislike (0)
  8. Dave Banham says:

    Niall,
    it will be interesting to see how IAR's implementation compares against both ARM's and GNU in both terms of size and the impact on the execution speed of normally operating code. I seem to recall that ARM's solution through data at the problem to ensure that exception handling code did not impact on the normally execution speed of the code.

    Dave B.

    Like (0)
    Dislike (0)
  9. Anders says:

    Hi Niall,
    I get the impression that your number are without any speed/size optimizations?

    Like (0)
    Dislike (0)
  10. Niall says:

    Hi Anders,

    Yes, there are no optimizations, that is something I'd like to look at further unless you have numbers you can share with us?

    Niall.

    Like (0)
    Dislike (0)

Leave a Reply