How much overhead does C++ bring compared to straight C?

The other day, I had a conversation about putting C code onto an embedded chip. I wondered if it was possible to put C++ code on there. The gist of the conversation was that the C++ libraries had a lot of overhead and the executable size would be too large for the space available on the chip. I'm not an expert on exactly how much space is available on such devices (although I plan on becoming one), but I thought I'd play around with a few things and see just what overhead a c++ program would have when compared with it's C counterparts.

I should note that there is already comparisons of the actual language constructs of C++ vs C for embedded devices. A simple google search yielded me a few answers. I'm more interested here in library overhead. For example, how does printf compare to cout, etc. All my experiments are done on my iMac with gcc/g++.

  1. The apples to apples test

    Just for a quick check, to make sure the other information is about the same, I compiled a simple do nothing program:


    2. int main(int argc, char* argv[]) {
    3. int x=3;
    4. x+=5;
    5. --x;
    6. return 0;
    7. }
    2. gcc -o test test.c

    I then copied this file to test.cpp, and used g++ instead.

    2. >g++ -o test2 test.cpp
    3. >ls -l test test2
    4. -rwxr-xr-x 1 dennis dennis 12564 Aug 9 08:12 test
    5. -rwxr-xr-x 1 dennis dennis 12564 Aug 9 08:12 test2

    Wait, slow down there. 12564 bytes exactly for each program? I thought for a second that perhaps gcc and g++ are creating the same output for the assembler. Here is a quick tip for checking the assembly generated by gcc or g++: Use the -S feature, which tells the compiler to stop after the assembly generation.

    2. gcc -o test.asm -S test.c

    I verified that the two asm files are indeed different. One looks much like a c program and the other looks like a c++ program:


    2. .text
    3. .globl _main
    4. _main:
    5. pushl %ebp
    6. movl %esp, %ebp
    7. subl $24, %esp
    8. movl $3, -12(%ebp)
    9. leal -12(%ebp), %eax
    10. addl $5, (%eax)
    11. leal -12(%ebp), %eax
    12. decl (%eax)
    13. movl $0, %eax
    14. leave
    15. ret
    16. .subsections_via_symbols


    2. .text
    3. .align 1,0x90
    4. .globl _main
    5. _main:
    6. LFB2:
    7. pushl %ebp
    8. LCFI0:
    9. movl %esp, %ebp
    10. LCFI1:
    11. subl $24, %esp
    12. LCFI2:
    13. movl $3, -12(%ebp)
    14. leal -12(%ebp), %eax
    15. addl $5, (%eax)
    16. leal -12(%ebp), %eax
    17. decl (%eax)
    18. movl $0, %eax
    19. leave
    20. ret
    21. LFE2:
    22. .globl
    23. = 0
    24. .no_dead_strip
    25. .constructor
    26. .destructor
    27. .align 1
    28. .subsections_via_symbols

    Ok then, the assembler takes these two different files and just happens to produce programs that are the exact same number of bytes. Good enough for me. On to step 2.

  2. IO Functions

    The 1st thing I'd like to check is how much overhead is produced by a simple include of the IO libraries.

    2. #include <iostream>
    2. > ls -l test test2
    3. -rwxr-xr-x 1 dennis dennis 12564 Aug 9 14:04 test
    4. -rwxr-xr-x 1 dennis dennis 12856 Aug 9 14:05 test2

    You get a few objects, cin, cout, et. al., with the C++ version so that doesn't surprise me that the C++ version is slightly larger. Of course, the C version isn't doing anything yet so I'll compare again after printing something. Also of note here, if I #include <cstdio>, the c++ version is still the same code size as the c version.

    2. printf ( "The result stored in x is %d.\n" , x );
    2. std::cout "The result stored in x is " << x << '.' << std::endl;
    2. >ls -l test test2
    3. -rwxr-xr-x 1 dennis dennis 12588 Aug 9 14:11 test
    4. -rwxr-xr-x 1 dennis dennis 13164 Aug 9 14:11 test2

    The gap widens. I'm assuming if I used cstdio and printf in the C++ program, that I'd still have the same sized executable with C++.

  3. How about some string operations

    Lets say I'd like to do some string operations.

    2. #include <string.h>
    2. #include <string> // note there is cstring also just like there is cstdio

    A quick compilation check produced the same sized programs as before. Including string doesn't increase code size in the C++ program the same as iostream does.

    Now, since string is a template class, I'm guessing that the size will further increase here.

    2. char* test_string="I'd like to test this string.";
    3. printf ( "%s\n", test_string );
    2. std::string test_string("I'd like to test this string.");
    3. std::cout << test_string << std::endl;
    2. >ls -l test test2
    3. -rwxr-xr-x 1 dennis dennis 12612 Aug 9 14:19 test
    4. -rwxr-xr-x 1 dennis dennis 13416 Aug 9 14:19 test2

    The C program increased 24 bytes. The C++ program increased 252 bytes. The gap widens further.

    I'm going to add another string and compare the two:

    2. char* string2 = "This is another string.";
    3. printf ( "comparison: %d\n" , strcmp ( test_string, string2 ) );
    2. std::string string2("This is another string.");
    3. std::cout << "comparison: " << (test_string==string2) << std::endl;
    2. >ls -l test test2
    3. -rwxr-xr-x 1 dennis dennis 12636 Aug 9 14:23 test
    4. -rwxr-xr-x 1 dennis dennis 13548 Aug 9 14:26 test2

    Ok, another 24 bytes for the C program and another 132 bytes for the C++ program. I actually thought that since the string template was already included, there might be some savings on calling the operator ==, but after thinking it through, that operator wouldn't be compiled into the program unless it was used. I also thought their might be more overhead for strcmp. I haven't looked into where that went.

  4. Classes

    One of the main things people talk about when working with C++ is the ability to group data into objects and take an object oriented approach to your program. Of course, you can do this with C too by just creating methods that take and operate on structs.

    2. typedef struct {
    3. int x, y;
    4. } some_class;
    6. int add(some_class* sc) {
    7. return sc->x + sc->y;
    8. }
    9. // ..snip..
    10. some_class sc;
    11. sc.x=3;
    12. sc.y=5;
    13. printf ( "class output: %d.\n" , add(&sc));
    2. class some_class {
    3. public:
    4. int x,y;
    5. int add() { return x+y;}
    6. };
    7. // .. snip ..
    8. some_class sc;
    9. sc.x=3;
    10. sc.y=5;
    11. std::cout << "class output: " << sc.add() << '.' << std::endl;
    2. >ls -l test test2
    3. -rwxr-xr-x 1 dennis dennis 12652 Aug 9 14:57 test
    4. -rwxr-xr-x 1 dennis dennis 13588 Aug 9 14:56 test2

    Well, there you have it. 16 more bytes for the C program and 44 more bytes for the C++ program. Not terribly a lot, but I guess it is more. I think you'd have to do a lot of test with larger classes and larger programs to get a better picture though.

  5. Compiler Optimization

    A last thought I had was to compare code size if the compiler optimizes a few things for size. I found that on Darwin, you can use -Oz to optimize only for size, even at the expense of speed sometimes. Here goes:

    2. >gcc -o test -Oz test.c
    3. >g++ -o test2 -Oz test.cpp
    4. >ls -l test test2
    5. -rwxr-xr-x 1 dennis dennis 12628 Aug 9 14:33 test
    6. -rwxr-xr-x 1 dennis dennis 13448 Aug 9 14:33 test2

    Looks like we get -24 bytes for the C program and the C++ program lost 144 bytes.

I think my verdict is that it depends. The C++ program benefitted more from the optimization than the C program. (Noting that the C program was practically at it's minimal size already though.) The final difference for these little simple programs is only 820 bytes. I think it depends on the amount of space available and what exact functions you'd need I guess. I'm not convinced you couldn't go ahead and use C++ to make a nice little app on an embedded chip though.

I'll post more about this as I gain experience with it.

2 Responses to “How much overhead does C++ bring compared to straight C?”

  1. Levi

    Lots of people use C++ in embedded chips. You just have to be a little careful about what C++ features you use. As your first program attests, C++ is almost 100% backwards compatible with C. Although there are some differences in the labels of the original program as compiled in C and C++, you’ll notice that the actual instructions generated are exactly the same.

  2. agtrier

    IMH experience, the main difference is that C++ makes it *easier* to use existing class libraries rather than re-implementing all the features yourself, and thus skip a lot of implicit optimisations that you would otherwise have done.

    This is where most of your binary file size comes from. The other differences are neglectable (well, most of the time..)