Inline Code in Headers Justification

srlm edited this page Nov 15, 2013 · 2 revisions

If you're an experienced C++ programmer you may have noticed that all of the libpropeller objects have the function definitions inline. Since this is non-standard, even frowned upon, you may be asking "why?"

First, let's start with why not and address each point directly. Implicit inline code:

  1. increases the compile time.
  2. increases final binary size.
  3. makes the header harder to read (as a documentation source).
  4. we can just use precompiled static libraries

It is true that inline code increases the compile time. While this may be a problem for the Linux kernel, it's not a problem for any PropellerGCC code. All code that can ever be written for the Propeller is bounded in size to 32KB (or maybe slightly larger for XMM or Propeller 2 code). Even if everything is in a single file the compilation time is much less than the loading time, and will always be less than a few seconds. Therefore, for Propeller GCC code, compilation time is negligible.

Inline code increases the final binary size iff the compiler actually inlines it. PropellerGCC has some advanced internal metrics that examine the cost and value of inilining, and decides whether or not to inline the code. This applies even if the programmer uses the inline keyword or implicitly inlines the code. With functions defined in the headers, the compiler has the option to do more advanced optimization. In practice, this means that the binaries are smaller, not larger.

Let's show some numbers to back up that statement. Here are some compiled binary sizes (actual size loaded). Most of these are from the unit tests of those classes. The last one (Beta2) is an actual application built using all of the above classes.

Class            Before	After	Bytes Saved
Stopwatch        6132	6084	48
Serial           18040	17956	84
SD               21752	21532	220
Scheduler        7040	7036	4
PCF8523          10004	9876	128
Numbers          21428	21448	-20
MS5611           10220	10240	-20
MAX17048         7456	7420	36
LSM303DLHC       7820	7820	0
L3GD20           7128	7128	0
I2C              8280	8192	88
GPSParser        9492	9420	72
Elum             6356	6244	112
EEPROM           15600	15580	20
ConcurrentBuffer 12676	12586	90
Beta2            22224	21720	504

From the data it's clear that inline code does not increase the final binary size, except in a few minor and rare cases.

But what about in an actual project. Surely it can't work there, right? Well, it does. The following numbers are based on a project with 4 high level classes that make use of the classes above. Each class was inlined in turn, and the code size recorded.

Class           Code Size
(start)         20148 bytes
Class 1         19788
Class 2         19756
Class 3         19460
Class 4         19368
(savings)       780

In this actual project we were able to save almost 800 bytes just by inlining 4 classes. This, plus whatever we have already saved by inlining all the lower level classes.

The third reason for inlining is personal: inline code makes the header harder to read, which reduces it's value as documentation. Java programmers get by just fine with inline code in their headers, don't they? And the classes shouldn't be that large anyway, otherwise they should be refactored. But if you're looking for documentation, look no further than the Doxygen generated API webpages.

Finally, opponents present the precompiled libraries. The problem with those is that they hide the source code, and they have to be compiled. This is an issue for the Propeller where you might have several incompatible compile time options that you want to use. The library provided would have to provide a library for each combination of options such as -cmm, -lmm, etc. This is a hassle, so we don't do it.

Let's look at why you would want to inline code, distinct from the above:

  1. It makes it easier to include it in a project
  2. It reduces the number of dependent files floating around
  3. Did I mention that code size is smaller?

The standard method of including a C++ file in another file is via the #include directive. This is handy because the compiler will automatically find the .h file by searching various paths and include it. It's not so good for .cpp files: you have to define those explicitly in your makefile. This makes it a hassle to create new projects. It's much easier just to provide a link to the libpropeller path and let the compiler find the header files.

Secondly, the separate .cpp and .h files means that you have to keep both in sync. Do you want to change the function name? Gotta change it in both. Want to make the function return something? Change it in both. This is a hassle that inline functions don't have to deal with.

Finally, code size is smaller. Yeah, defining your functions in the header file reduces code size, not increases it.

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.