Close

What's the most efficient way to print things

A project log for (AT) Mega Enigma, a 2560 Pro Mini Enigma Simulator

A minimalist 139x171x50mm Enigma Machine Simulator with working plugboard using an ATmega 2560 Pro Mini and a custom PCB.

arduino-enigmaArduino Enigma 10/10/2019 at 04:060 Comments

We are at a stage of the project where things are getting standardized and optimized. The program needs to print a space every once in a while to separate groups of characters, as well as send the CR LF sequence to start printing things in a new line. We are curious what is the best way to do that.

Let's start by saying that the most efficient way to print strings is to wrap them in the F("") macro. This keeps the string in PROGMEM, otherwise the string is stored in PROGMEM, then at runtime it is copied to precious RAM before printing and it is possible to run out of RAM if the sketch uses a lots of strings.

Example:

Serial.print(F("HELLO WORLD"));  // DO THIS, 18 bytes code, 0 bytes ram

don't do this:

Serial.print("HELLO WORLD"); // DON'T DO THIS, 26 bytes code, 12 bytes ram

Now, what what is the most efficient way to print a single space to separate two characters printed elsewhere? How about a newline / CR LF sequence?

The sketch below was created to test different ways to print a space and a new line in between the A and B letters. 

First a very basic sketch was created and compiled. The program size and ram usage were recorded.

void testSerialSpace()     /*xls*/
{
  Serial.begin(9600);
  Serial.print(F("A"));
  Serial.print(F("B"));
}

void setup()
{
  testSerialSpace();
}

void loop()
{
}

 Then different ways to print a space and a new line were added, one at a time and the memory usage was obtained by subtracting it from the baseline program.

The moral of the story is that the most efficient way to print a space is simply:

Serial.print(F(" ")); // adds 8 bytes code, 0 ram

Things got a little more surprising in the newline department.

The obvious and wrong choice would be to print an empty string using the F("") macro.

Serial.println(F("")); // adds 26 bytes code and 4 bytes ram

 But this added a whopping 26 bytes of code and 4 bytes of ram. Not sure why.

After trying different combinations the most efficient way to send a newline is to do a regular print, not a println and send a hex escaped string with the CR LF sequence

Serial.print(F("\x0d\x0a"));    // adds 10 bytes code and 0 bytes ram

This result was a little unexpected. but now we know.

The full results are listed below, ranking from most efficient to least efficient. The strings printed without F("") are at the bottom of the list, with the absolute worst  ways to send a single new line clocking at 40 bytes of code and 4 bytes of RAM.

//
// moral of the story:
// to print a space, use Serial.print(F(" "));
// to print a newline, use Serial.print(F("\x0d\x0a"));
//
//  Only prints AB
//  Sketch uses 1,794 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 182 bytes (2%) of dynamic memory, leaving 8,010 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.print(F("HELLO WORLD"));
//  Sketch uses 1,812 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 182 bytes (2%) of dynamic memory, leaving 8,010 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.print("HELLO WORLD");
//  Sketch uses 1,820 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 194 bytes (2%) of dynamic memory, leaving 7,998 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.print(F(" "));
//  Sketch uses 1,802 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 182 bytes (2%) of dynamic memory, leaving 8,010 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.print(F("\x0d\x0a"));
//  Sketch uses 1,804 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 182 bytes (2%) of dynamic memory, leaving 8,010 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.print(" ");
//  Sketch uses 1,810 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 184 bytes (2%) of dynamic memory, leaving 8,008 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.print(' ');
//  Sketch uses 1,816 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 182 bytes (2%) of dynamic memory, leaving 8,010 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.println(F(""));
//  Sketch uses 1,820 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 186 bytes (2%) of dynamic memory, leaving 8,006 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.println("");
//  Sketch uses 1,826 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 186 bytes (2%) of dynamic memory, leaving 8,006 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.println((char)0);
//  Sketch uses 1,834 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 186 bytes (2%) of dynamic memory, leaving 8,006 bytes for local variables. Maximum is 8,192 bytes.
//
//  Serial.println(' ');
//  Sketch uses 1,834 bytes (0%) of program storage space. Maximum is 253,952 bytes.
//  Global variables use 186 bytes (2%) of dynamic memory, leaving 8,006 bytes for local variables. Maximum is 8,192 bytes.
//
void testSerialSpace()     /*xls*/
{
  Serial.begin(9600);
  Serial.print(F("A"));

  //Serial.print(F("HELLO WORLD")); // adds 18 bytes code,  0 bytes ram
  //Serial.print("HELLO WORLD");    // adds 26 bytes code, 12 bytes ram

  //Serial.print(F(" "));           // adds  8 bytes code,  0 bytes ram
  //Serial.print(F("\x0d\x0a"));    // adds 10 bytes code,  0 bytes ram
  //Serial.print(" ");              // adds 16 bytes code,  2 bytes ram (string gets copied to ram)
  //Serial.print(' ');              // adds 22 bytes code,  0 bytes ram

  //Serial.println(F(""));          // adds 26 bytes code,  4 bytes ram
  //Serial.println("");             // adds 32 bytes code,  4 bytes ram
  //Serial.println((char)0);        // adds 40 bytes code,  4 bytes ram
  //Serial.println(' ');            // adds 40 bytes code,  4 bytes ram

  Serial.print(F("B"));
}

void setup()
{
  testSerialSpace();
}

void loop()
{
}

Discussions