Arduino Inline Keyword. Faster Code With Forced Inline Functions.

Sometimes every microsecond counts, and you can't even afford time on a function call. One such example is my OV7670 camera module project:
https://circuitjournal.com/arduino-ov7670-10fps

You could write everything time-critical into one function to avoid calls. But Having separate methods for sub-routines is still necessary for code readability and re-usability.

C++ has an "inline" keyword, but unfortunately, it doesn't work when compiling code in Arduino IDE. It's just a hint to the compiler, and depending on the compiler optimization parameters, it may ignore it. This is the case for Arduino. By default, the compiler is optimized for code size and not speed.

But it is possible to force a function to be inlined by adding a function declaration with "always_inline" attribute before the actual function body.

inline void myInlinedFunction() __attribute__((always_inline));

void myInlinedFunction() {
}

Video Overview

In this video, I demonstrate that the "inline" keyword by itself doesn't work. And then, I show the effects of inlining after forcing it with the "always_inline" attribute. Arduino generates pulses, and you can see the time difference on the oscilloscope screen.

"inline" Keyword vs "always_inline" Attribute

I ran the following code on an Arduino and measured the output of pin 13 on the oscilloscope. I am using direct port manipulation by changing the PORTB register. "PORTB = 0b00100000;" will set the digital output 13 to HIGH. "digitalWrite(13, HIGH);" is very slow, and the effects of inlining would be hard to see.

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  while(1) {
    twoPulses();
    twoPulses();
    twoPulses();
    twoPulses();
  }
}

inline void twoPulses() {
    PORTB = 0b00100000;
    PORTB = 0b00000000;
    PORTB = 0b00100000;
    PORTB = 0b00000000;
}

Despite having "inline" in front of the "twoPulses" function, the oscilloscope image shows gaps between the pulses

'inline' function still has gaps between pulses

Then I added the function declaration with the "always_inline" attribute (you can remove the "inline" keyword from the function body).

inline void twoPulses() __attribute__((always_inline));

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  while(1) {
    twoPulses();
    twoPulses();
    twoPulses();
    twoPulses();
  }
}

void twoPulses() {
    PORTB = 0b00100000;
    PORTB = 0b00000000;
    PORTB = 0b00100000;
    PORTB = 0b00000000;
}

And now the gaps between the pulse pairs disappear. The space after the eighth pulse is the "while" loop going around.

'always_inline' function with no gaps between pulses

Inlining Functions from a Classes

To inline a member function of a class, you have to define its body inside the header file of that class. The compiler has to be able to see the inlined function from the source file where you are calling it.

Function declaration inside the class can have the "inline" keyword and the "always_inline" attribute.

PulseGenerator.h

class PulseGenerator {
public:
  inline void twoPulses() __attribute__((always_inline));
};

void PulseGenerator::twoPulses() {
  PORTB = 0b00100000;
  PORTB = 0b00000000;
  PORTB = 0b00100000;
  PORTB = 0b00000000;
}

Now you can call it like a regular method, and it will be automatically inlined.

#include "PulseGenerator.h"

PulseGenerator pulseGenerator;

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  while(1) {
    pulseGenerator.twoPulses();
    pulseGenerator.twoPulses();
    pulseGenerator.twoPulses();
    pulseGenerator.twoPulses();
  }
}

The oscilloscope image for this code will also be

'always_inline' function with no gaps between pulses