Do I need to pack or typecast the sizes of variables, structs and defines ?

Hi,

I have a concern regarding the program size and what things that optimize the over all code size.

The questions I have:

  1. The default size of #define in 8-bit microcontroller compiler is 2 bytes, if I only need a small value, does the following affect the code size:
#define NOT_FINISHED    (uint8_t)0

vs

#define NOT_FINISHED             0
  1. The same relates to any variable I need; for example, if the variable won't exceed 256, does it matter in program size the following:
byte counter;

vs

long counter;
  1. My 3rd question, should I pack the struct ?
typedef struct __attribute__((__packed__)){
    f_ptr *tsk_fptr;
	uint8_t tsk_cnts;
	uint8_t tsk_cntr;
	bool thread_state;
}THREAD;

The size in desktop compiler is 11.

typedef struct {
    f_ptr *tsk_fptr;
	uint8_t tsk_cnts;
	uint8_t tsk_cntr;
	bool thread_state;
}THREAD;

Size is 16.

The default size of a #define is ZERO bytes. A #define is nothing but a string of characters that gets copied and pasted into the source code at compile time everywhere the #define identifier appears. How much FLASH and/or RAM will be consumed by use of the #define is entirely a function of where and how the #define is actually used. It could be none, or it could be a lot. The #define itself NEVER has any defined size.

#define X 1234 // This uses NO FLASH and NO RAM

uint8_t thing1 = X;    // This will consume one byte of FLASH and one byte of RAM
const uint8_t thing2 = X;    // This will consume one byte of FLASH and zero bytes of RAM
uint32_t thing3 = X;  // This will consume four bytes of FLASH and four bytes of RAM
const uint32_t thing4 = X;  // This will consume four bytes of FLASH and zero bytes of RAM
uint8_t array[X];  // This will consume 1234 bytes of RAM
1 Like

And the answer to #2: Yes, a 'byte' variable is 1 byte, a 'long' variable consumes 4 bytes. It is always a good idea to make your variables the correct size. It is equally important to know if your variables will be signed or unsigned

For Q3 - it should not matter unless you are transmitting this structure to some other controller/PC/something and then you have to care.

As a general rule, all of these concerns should not be a concern. Do you really care if you consume a couple of extra bytes of memory? Your first concern should be to get a working sketch that does what you intend. Then, and only if needed, worry about how fast it is working or how efficient it is in terms of size, etc. A small, fast program that doesn't do the right thing is useless :slight_smile:

1 Like

RayLivingston:
The default size of a #define is ZERO bytes. A #define is nothing but a string of characters that gets copied and pasted into the source code at compile time everywhere the #define identifier appears. How much FLASH and/or RAM will be consumed by use of the #define is entirely a function of where and how the #define is actually used. It could be none, or it could be a lot. The #define itself NEVER has any defined size.

#define X 1234 // This uses NO FLASH and NO RAM

uint8_t thing1 = X;    // This will consume one byte of FLASH and one byte of RAM
const uint8_t thing2 = X;    // This will consume one byte of FLASH and zero bytes of RAM
uint32_t thing3 = X;  // This will consume four bytes of FLASH and four bytes of RAM
const uint32_t thing4 = X;  // This will consume four bytes of FLASH and zero bytes of RAM
uint8_t array[X];  // This will consume 1234 bytes of RAM

Thank you so much for the explanation, this cleared fundamental aspect for me about #define, it's just a text substitution tool.

RayLivingston:
The default size of a #define is ZERO bytes. ...

I tried this code and didn't notice any change in compiler result of used RAM and FLASH sizes.

#define VALUE 10

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  byte i = VALUE;  i++;
}

blh64:
For Q3 - it should not matter unless you are transmitting this structure to some other controller/PC/something and then you have to care.

Thanks for this info.

As a general rule, all of these concerns should not be a concern. Do you really care if you consume a couple of extra bytes of memory?

Yep, that's really not so important at the end of the day.

Your first concern should be to get a working sketch that does what you intend. Then, and only if needed, worry about how fast it is working or how efficient it is in terms of size, etc. A small, fast program that doesn't do the right thing is useless :slight_smile:

Yes, that's mostly my main sequence of concerns, to get it working, faster and reduced size.

Got good info of this link:
http://www.catb.org/esr/structure-packing/

Learned something about padding and packing.

So basically I have to order the variables and struct members by the order of memory alignment basic size.

wolfrose:
I tried this code and didn't notice any change in compiler result of used RAM and FLASH sizes.

#define VALUE 10

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  byte i = VALUE;  i++;
}

Because the "byte i = VALUE; i++;" will be completely optimized out by the compiler, since it does nothing of any value.

OK, this brings the question: what things that affect the program size ?

wolfrose:
OK, this brings the question: what things that affect the program size ?

Things that require storage in FLASH and/or RAM. But, without understanding how the compiler interprets the code, that is often hard to discern. It is NOT always entirely straight-forward, particularly once the optimizer has done its thing. Modern optimizers are remarkably good at eliminating "dead" code and variables. But, any time you add lines of code, or variables, and the FLASH and/or RAM size remain fixed, you can be fairly certain the new code or data has been optimized out by the compiler.

RayLivingston:
Because the "byte i = VALUE; i++;" will be completely optimized out by the compiler, since it does nothing of any value.

i is a variable local to loop() so it is allocated on the stack. The compiler reports on global allocation, not local, stack allocation. It very well may also optimize it away as well as noted in reply#7

RayLivingston:
Things that require storage in FLASH and/or RAM. But, without understanding how the compiler interprets the code, that is often hard to discern. It is NOT always entirely straight-forward, particularly once the optimizer has done its thing. Modern optimizers are remarkably good at eliminating "dead" code and variables. But, any time you add lines of code, or variables, and the FLASH and/or RAM size remain fixed, you can be fairly certain the new code or data has been optimized out by the compiler.

Thank you for this concise explanation.
Well, at this point, I should start reading about compilers and optimization which should be a nice level of understanding that I know what is going on.
But the most important now is that I learned that optimization is taking action inn almost any compiler.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.