When the compiler matters (Part 1)

Part 1

Recently, I worked on a library to manage LED strips. It worked perfectly using the Window’s version of Arduino. It worked as well on the GNU/Linux’ version with a LED strip… but not with another strip ! An advanced issue for an advanced headache…

In the Arduino environment , when the “verify” or “upload” buttons buttons are pressed, a compiler is called: e.g. avr-g++ is the default compiler used by Arduino IDE. The compiler  reads the code from source file(s) (e.g. .ino, .h and .cpp) and generates an executable binary file (.hex). The resulting executable file content may vary a lot depending upon the numerous parameters involved in compilation. This post is about the testing of some of these parameters which may have an impact on the timings.

  • The compiler version which may depend on the Operating System in use
  • The use of functions or macros to encapsulate a sequence of instructions
  • The various ways of managing digital ports from the ATMEGA micro-controller:  using Atmel instructions or using Arduino’s wrapper

To understand the issue, some explanations are given about the LED strip. Each LED of the strip has a built-in controller that receives the data for itself and for the next LEDs, it treats its data and forwards the rest. This kind of controller (a WS2812) uses a protocol that requires a certain precision for the timings. Its principle is straightforward : a color is sent by a digital pin using a 24 bits stream of data. Each bit of data consists in a modulated width pulse as per the following picture :

protocol       timing_suggested

The tolerance for these timings are specified at 150 nS.

Next picture is a snap shot taken from an oscilloscope. It represents one byte of data (out of 3) issued by an Arduino board. This board is running the same source code compiled with avr-g++ on a Linux workstation and a Windows workstation :

result_OS

 Both byte send the  01010000B binary value (0x50 in hexadecimal format). The red bars show the time difference between two identical streams of bits. This shift might be a problem… or not depending upon the LED strip. In any case, one should be aware about the potential issue there.

The second way to modify the scheduling is to use a macro instead of a function. But what is the difference between a function and a macro ?

When a function is called, the context of the previous function is pushed from the processor’s registers to the memory stack :

mem_load

‘a’ is pushed from r2 then ‘b’ is pushed from r1. Some other stuff is also done to load the context of the current function.

At the opposite, defining a macro as HELLO_WOLRD here :

#define HELLO_WORLD() { Serial.print("hello world !"); }
void Setup()
{
   Serial.begin(115200);
   HELLO_WORLD();
}

Will replace the macro by its body, the code above is equivalent to :

void Setup()
{
   Serial.begin(115200);
   Serial.print("hello world !");
}

This way, no overhead is added. Another tricky C/C++ thing is the keyword “inline” before a function. Using it will ask the compiler to replace the function by its body when it is called but the compiler is able to refuse and silently disobey after analyzing the cost/benefit ratio. The two following snippets are equivalent if the compiler is in good mood :

inline void IncrementI(uint8_t i)
{
   i++;
}

void counterTo255()
{
   uint8_t i = 0;
   while(true){
      Serial.print(i);
      IncrementI();
}
void counterTo255()
{
   uint8_t i = 0;
   while(true){
      Serial.print(i);
      i++;
}

Here is the comparison between using inline functions or macro on Windows :

result_MF 

The time shifting is not a big deal, the only difference is the delay of the second byte, which may cumulatively become a problem but works with 30 LED at least.

The third parameter that was tested is the difference between the direct ports manipulation and the use of the Arduino’s wrapper :

PORTB  |= 1 << PINB4; /* direct manipulation */
digitalWrite(12, HIGH); /* using Arduino's wrapper */

Which outputs :

result_PORT2

 

The overhead introduced by the wrapper makes it not usable in this kind of application.

The compiler can also optimize the execution time or the size of the produced binary, on Arduino the default behavior is to optimize the size but it is another story. You may also want to read : disassembling code

2 Comments

  1. Alejandro Vargas says:

    But you didn’t explain why is different the compilation result under linux and windows.

    • Mickael says:

      Hi Alejandro,

      You are right, the aim of this post was only to point out that, sometimes, the compiler matters. Please keep in mind that it is not the operating system itself which makes any differences but the tools which come with. For instance, the “about” splash screen on Windows 7 64 bits prints “1.0.5 -r2” but prints “1:1.0.5+dfsg2-1” on GNU/Linux Kubuntu 13.10 x86_64 with the official and stable repositories.
      Digging deeper, the Arduino software uses the AVR toolchain.
      On windows, in the directory “C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\bin”, typing “c++ -version” returns :
      c++ (WinAVR 20081205) 4.3.2
      Copyright (C) 2008 Free Software Foundation, Inc.
      This is free software; see the source for copying conditions. There is NO
      warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

      On Linux, typing “avr-g++ –version” returns :
      avr-g++ (GCC) 4.7.2
      Copyright (C) 2012 Free Software Foundation, Inc.
      This is free software; see the source for copying conditions. There is NO
      warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

      You can note that the compilers are clearly not the same.

      If your question is “why are the outputs from two different compilers different ?”, the answer is that the process behind the compilation is not straightforward and features optimizations. Some choices are made to optimize the output in a special case but not in another. These choices (or bug fixes) may vary depending on the teams which maintain a compiler and the version of the compiler.

      Thanks for your interest.

Leave a Reply

You must be logged in to post a comment.