Different data sizes in program usage

Hi,

I was testing which is better to use #define in my libraries or const uint8_t, that which one takes more space in program memory.

So I tested these data types in Arduino IDE and found different results:

Note: I've put the result of each compile of the const value in the front comment

// type                    // size
// empty println           // 1458
// println(0)              // 1694
// println(1000000)        // 1632
#define        b0       9  // 1696
const char     b1     = 9; // 1468
const byte     b2     = 9; // 1516
const int      b3     = 9; // 1696
const uint8_t  b4     = 9; // 1516
const long     b5     = 9; // 1516
const double   b6     = 9; // 1728
enum          {b7       }; // 1694

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println();        // <-- here I put the name of any const above
}

I don't know this difference in size results is because the use of the serial print function or what exactly.

My questions:

  1. Why this fluctuations in results ?
  2. For const values, which is best #define or const uint8_t ?

One thing that will make a difference is that there is not a single Serial.print() function but a number of them. Which one is used depends on the data type being printed so I would expect different results from some different data types

For const values, which is best #define or const uint8_t ?

What is your definition of "best" ?

char can be put directly into output buffer.
number types are converted to text for printing.

UKHeliBob:
One thing that will make a difference is that there is not a single Serial.print() function but a number of them. Which one is used depends on the data type being printed so I would expect different results from some different data types

OK, I thought Serial.print() isn't the optimal example here, so that explains a lot.
I shouldn't use Serial.print() as a test tool in the first place.
Then the original rules of data size are the same if we use one function and not overloaded function.

What is your definition of "best" ?

Program size and performance.

Juraj:
char can be put directly into output buffer.
number types are converted to text for printing.

ArduinoCore-avr/cores/arduino/Print.cpp at 9f8d27f09f3bbd1da1374b5549a82bda55d45d44 · arduino/ArduinoCore-avr · GitHub

Thanks that's also an important point.
But since Serial.print() isn't the right tool to test data sizes, then I would just take with the fundamental rules of data sizes.
So as sizes issue is cleared now, then my last concern here is what to use in my libraries ? #define or const uint8_t ?

You should use const.

Program size and performance.

If a larger program runs faster, then what ?

#define is used by the preprocessor to do what amounts to a text substitution, before invoking the compiler.

The const is processed by the compiler, so there will be type checking and possible generation of warnings or errors if applicable.

If you look at the amount of dynamic memory (RAM) used for each of your tests, that does not change because of the compiler optimizations hard coding the const values into the code, so there is no disadvantage to using const in terms of RAM usage.

You do need to be careful when choosing types, one of your test const values will not produce the expected output.

TheMemberFormerlyKnownAsAWOL:
You should use const.

I thought so, because with const I can control the size of the value, defines are 2 bytes.
But also I found something also interesting, when I define a value that is larger to fit in 2 bytes, then compiler adjust the size of define to be 4 bytes but also it's signed.
So if I even needed something that's bigger than 2 billion, then I should use long long which provides 8 bytes.

So eventually, this is not a one route that's the only route. Using defines or const either one of course works and right.
But for more optimized program space and with conclusions of this thread, and some search, it's ok to use const.
Still couple concerns:

  1. I read in stackoverflow that const works differently in C vs C++, but what I understood is that it's more optimized in C++ and also should work fine too in C. Is that right ?
  2. Does it affect the program speed or performance to use either define or const ?

UKHeliBob:
If a larger program runs faster, then what ?

I'm not sure but I think define are better in this regard because they are compiled before anything. Not sure even of my answer :slight_smile:

david_2018:
#define is used by the preprocessor to do what amounts to a text substitution, before invoking the compiler.

The const is processed by the compiler, so there will be type checking and possible generation of warnings or errors if applicable.

If you look at the amount of dynamic memory (RAM) used for each of your tests, that does not change because of the compiler optimizations hard coding the const values into the code, so there is no disadvantage to using const in terms of RAM usage.

You do need to be careful when choosing types, one of your test const values will not produce the expected output.

For a start; if I want to change things in my libraries.
For example; I want to change the GLCD defines,
from:

#define CS_PIN						10
#define MOSI_PIN					11
#define CLK_PIN						13
#define CMD_MSK						0xF8
#define DATA_MSK					0xFA
#define DISPLAY_CLEAR				0x01
#define RETURN_HOME					0x02
#define ENTERY_MODE					0x06
#define DISPLAY_CONTROL				0x0C
#define DIS_CURSOR_CTL				0x14

to:

const uint8_t CS_PIN =          10;
const uint8_t MOSI_PIN =        11;
const uint8_t CLK_PIN =         13;
const uint8_t CMD_MSK =         0xF8;
const uint8_t DATA_MSK =        0xFA;
const uint8_t DISPLAY_CLEAR =   0x01;
const uint8_t RETURN_HOME =     0x02;
const uint8_t ENTERY_MODE =     0x06;
const uint8_t DISPLAY_CONTROL = 0x0C;
const uint8_t DIS_CURSOR_CTL =  0x14;

I guess this won't affect the code in any regard, is that right ?

wolfrose:
I thought so, because with const I can control the size of the value, defines are 2 bytes.

No. #define has NO defined size, and uses NO RAM at all. What you DO with the #define value MAY cause some RAM to be used, but not by the #define itself. #define is NOTHING but a text substitution that occurs before the compiler is even run. It does nothing but a cut-and-paste of one sequence of characters in the source file for another.

RayLivingston:
No. #define has NO defined size, and uses NO RAM at all. What you DO with the #define value MAY cause some RAM to be used, but not by the #define itself. #define is NOTHING but a text substitution that occurs before the compiler is even run. It does nothing but a cut-and-paste of one sequence of characters in the source file for another.

OK, thank you very much for clearing this point. So it does not take any space in program but when used.
So it's same like declaring a variable, so declaring a variable doesn't take any space but using the variable use program/RAM space, am I right in this one ?
So, if there is no difference in declaring/using "define" vs "const var" ? is that right ? and which is recommended for const values like the ones in #10 ? and not macros, because macros vs functions is another story.

A #define consumes NO RAM. const variables consume NO RAM. The difference between the two is the const variable has a type, so the compiler can do type checking whenever the const variable is referenced. A #define has NO type. It is nothing more than a literal string, and that is used to replace another literal string in the source code. The effect is PRECISELY the same as if you manually did a global search and replace on the source code with your text editor, before compiling the code. That is exactly what the pre-processor does with all #defines.

Thanks a lot, your answer explains a lot.

So it doesn't matter actually, whether using a const or define.

But other things I measured some space saving, I saved several bytes changing const values from enum to const uint8_t.

This:

typedef enum{NOT_FINISHED,FINISHED,DONE}TASK_STATE, THREAD_STATE;

With this:

typedef bool TASK_STATE, THREAD_STATE;
const uint8_t NOT_FINISHED = 0,FINISHED = 1,DONE = 2;

It compiled the same, and worked the same too.

This one I noticed saving some bytes, but using enum is more easier to write than the second method.

const bool ...DONE = 2; Now you're being ridiculous.

You can specify the datatype of an enum:

typedef enum : uint8_t {NOT_FINISHED,FINISHED,DONE}TASK_STATE, THREAD_STATE;

I read somewhere that to guarantee a const to be a compile time constant like a #define that you should use:
constexpr uint8_t CS_PIN = 10;
or better yet:
static constexpr uint8_t CS_PIN = 10;
Apparently const can be subverted so it may not always be a compile time constant.

EDIT: I remember now. If you take the address of a constant then it will be placed in RAM you cannot take the address of a constexpr.

Oh !! I'm sorry :grin:

I edited it. Thanks,

Hutkikz:
You can specify an enum as a uint8_t

typedef enum : uint8_t {NOT_FINISHED,FINISHED,DONE}TASK_STATE, THREAD_STATE;

wow that's interesting ! Thank you so much, I'm really enjoying this thread :slight_smile:

I read somewhere that to guarantee a const to be a compile time constant like a #define that you should use:

constexpr uint8_t CS_PIN = 10;

or better yet:

static constexpr uint8_t CS_PIN = 10;

Apparently const can be subverted so it may not always be a compile time constant.

Well, if that would provide the same effect as #define, then I would keep the defines, it's easier to write I guess.

wolfrose:
wow that's interesting ! Thank you so much, I'm really enjoying this thread :)Well, if that would provide the same effect as #define, then I would keep the defines, it's easier to write I guess.

But it has a type. so it's better than a define.