JUN
7
2005

C vs. C++ for embedded development

There is the myth that using C++ per se makes applications and libraries slow and bloated. Avoiding bloat is of special importance for embedded devices, which usually have limited amounts of CPU power and memory.
On www.linuxdevices.com I found a short article on this topic: C vs. C++ on Embedded Devices.
Let's get down to the statements being made there:

  • C++'s virtues are expensive. Advanced OOP features, such as templates and the practice of using classes in the place of primitives, to name two examples, cause unacceptable code bloat.

    Basically, templates can be seen as glorified macros. So if you are using templates, you should know that each new template instance means generation of code for this type. This is in no way different from using macros in C, except that it is type safe and can be implemented using facilities of the language instead of text processing. So, yes, templates produce code. If you are concerned about the code size, of course you can do your best to avoid code bloat.
    In many situations it is common practice to implement template classes completely inline in the header. For embedded development, if code size is important enough for you, this is not neccessary. Implement the functions in a foo.inl file, include this in your project and instanciate the templates explicitely:
    #include
    template class MyList;



    I don't understand in which way using a class creates overhead. Consider the following example, showing a C++ class Foo and a plain C struct Bar:
    class Foo {
    int m_a;
    int m_b;
    void doSomething();
    }

    struct bar {
    int a;
    int b;
    }

    void do_something_else();


    Both versions basically come down to the same: (usually) 8 bytes for the two variables, and a function. More or less the only difference is that void Foo:doSomething() will be name-mangled to a plain C function to something like void Foo_doSomething(Foo* this). So where's the overhead ?
  • A C++ compiler may generate many routines for one function (templates) or create routines where no function explicitly appeared (constructors, casts, etc.). There is generally a one-to-one relationship between a function in C code and the resulting machine-code routine. It's easier to optimize what you can see than what you must infer.

    If you are using C++, you should know the basics of the language. You should know that constructing a variable means that it's ctor will be called. It's the same overhead as calling a init() function in C, except that the ctor can't be forgotten. You should also know that type casts can be functions and operators can be functions. If you are aware of this, there's basically no difference left.
  • Virtual methods and polymorphism complicate runtime linking and require many relocations. This slows C++ application launch time considerably. C applications are both simple to link and amenable to lazy linking, so they load quickly. (For details, see Waldo Bastian's paper ``Making C++ Ready for the Desktop", http://www.suse.de/~bastian/Export/linking.txt.)

    Yes, C++ virtual functions increase startup time due to more symbol lookups during dynamic linking. This is a problem for general-purpose desktop computers. In embedded devices static linking can be a solution. This way dynamic symbol lookup can be avoided.

  • Each class with virtual methods has an associated vtable array, which adds memory overhead.

    Yes, the virtual function tables require memory. But what would you do if you are using C ? Either you would use function pointers (require memory) or you would have to differentiate the function to be called via e.g. type fields and switch statements:
    switch (foo->type) {
    case CIRCLE:
    draw_circle(foo);
    break;
    case RECT:
    draw_rect(foo);
    break;
    case POINT:
    draw_point(foo);
    break;

    The other choice for C is the following:
    foo->fct_pointer=draw_circle;
    ...
    foo->fct_pointer(foo);

    How does this compare to C++ ?

    Option one requires code memory, which could reside in ROM. OTOH it adds runtime overhead since the type of foo has to be checked during runtime and it is vulnerable to coding mistakes.
    Option two using a function pointer is basically the same as virtual functions in C++ implemented using C. Except that it can't be expressed using built-in facilities of the language it is equivalent to C++.
  • C++'s tighter type-checking makes it difficult to write the space-conscious code reuse common to C applications (think: void *).

    I don't understand this one. You can use void* in C++ too. Usually I only use it for pointers to buffers. What else can you do with void-pointers ?
  • The small, simple code demanded of embedded projects provides maintainability. There is no reason to assume OOP will further simplify such systems.
    GUIs may not have a simple solution in a rigorous OOP model.


    Well, some people say so, some say different.
  • It's easy to get carried away and start doing OOP for OOP's sake. The One True Object Model may describe a problem perfectly, but it comes at the cost of excessive code.
    Carefully written C code can be much faster than C++ code, especially on embedded hardware. GTK+'s hand-crafted object system offers much better spatial locality than C++'s more numerous and distributed constructors. Our device has a tiny cache, so locality is an especially important performance consideration.


    Well, and carefully written C++ code can be much faster than (sloppy) C code.


Some things the C++ developer must be aware of:

  • run time type information and dynamic_cast<> add memory and performance overhead, so consider avoiding it
  • creating a variable means that its ctor, i.e. a function will be called
  • AFAIK exceptions still add some overhead using gcc, so it should be considered to avoid them
  • a class without virtual functions is the same as a struct and some functions, memory wise, no additional overhead here, except the implicit this pointer for each member method.
  • be aware how template code generation works. You can reduce this to a minimum if you want to.

And there are also advantages of using C++ for embedded projects:

  • global/static objects will be initialized automatically, when the application starts since then their ctors will be called automatically
  • init-functions can't be forgotten, since ctors are called automatically
  • you have stricter type checking
  • you can write more expressive and concise code, since the language gives you more power
  • use const extensively to make semantics clearer

So, that's about it.

IOW I don't see any inherent disadvantages of C++ compared to C. It boils down to "Know your tools". The best tools are the ones you know best. If you know C++, you know where code is called and where memory is required and can avoid it if neccessary. Additionally you get all the advantages of C++



Alex

Comments

OOP, even used sparingly, promotes encapsulation, which is a big plus over straight C. You mentioned being able to work faster because the language is more expressive, this also means that you as a programmer are cheaper in the long run. And the reduction in cost of development might well outway the extra cost of more memory...


By Ruurd Pels at Tue, 06/07/2005 - 17:45

If you want to write fast, efficient and native software but that software is too complex in structure (i.e. inherently object-oriented) and at too high a level then the only language you can consider is C++. You only need to look at KDE to see that wisdom. C++ isn't perfect, but it's the best fit there is for that problem scenario. As people are trying to do more and more in that kind of area, especially on embedded devices, C++ inevitably becomes a better fit for that kind of problem. Sounds like someone just doesn't want to go anywhere near C++ at all costs.

Why do people think there's such a debate going on around Gnome about higher level languages, technology and software??!! Arrrghhhh.


By segedunum at Tue, 06/07/2005 - 18:55

For some reason people still see OO as the thing that C++ tries to achieve. This puzzles me because the best code that I have ever seen comes from Boost; whose code is OO only if you squint your eyes -- or even better, gouge them out. It's not OO, not really functional (too many side-effects), but hugely pragmatic and beautifully designed. Also fast -- a decent compiler like gcc can optimize away the template instances, and later gcc (3.4 onwards) allows control over symbol visibility leading to code that's just as small as hand-coded C.


By genneth at Wed, 06/08/2005 - 03:20