Precious SRam

Anyone who has written a sketch beyond blinking a LED, has likely encountered the problem of running out of SRam.

There is alot written about storing strings into FLASH, but not a lot about other variables and the best way to optimize their use of SRam.

Consider the following statement:
if (i == 5){ ....
I was really confused about how the 5 was stored... in SRam or Flash

Also, consider
const int i = 5;

SRam or Flash?

Below is an experiment that I used to help answer some of these questions.

[font=Verdana]
  #include <SPI.h>

  #define DZ 5
  bool first = true;
                                                // Step 1 : Execute the sketch as-is with everything commented
//  int a = 5;                                  // Step 2 : Uncomment here and Step2 below

//  int z = 5;                                  // Step 3 : Uncomment (Also below)
//  const int z = 5;                            // Step 4 : Comment Step3 and Uncomment this


 void setup(){
   
  Serial.begin(9600); 
  delay(1500);   
   
  Serial.println (F(""));
  Serial.println (F("SRam test..."));
  Serial.print   (F("Beginning SRAM = "));
  Serial.println (freeRam());
//  const int z = 5;                            // Step 5: comment Step4 and uncomment this
//  const int z = 4;                            // step 8: Comment Step5 and uncomment this
//  int z = 5;                                  // Step 9: Comment Step8 and uncomment this

//  if (a == z)    Serial.println (F("A = Z")); // Step 3: Uncomment and leave it uncommented until Step6
//  if (a == DZ)   Serial.println (F("A = Z")); // Step 6: Comment Step3 above and uncomment this
//  if (a == z){                                // Step 7: Comment Step6, Step2 below and Uncomment the block below
//    Serial.println (F("A = Z"));
//    int i = 5;                                 
//    a = i;
//    } 

//  a = 0;                                      // Step 2: Uncomment here.. This is here to ensure int a is not optimized out of the compile  

  Serial.print   (F("Ending SRAM = "));
  Serial.println (freeRam());
 }

 void loop(){
  if (first){ 
  Serial.print   (F("Loop SRAM = "));
  Serial.println (freeRam());
  first = false;
  } 
 }
  
int freeRam () {
 extern int __heap_start, *__brkval; 
 int v; 
 return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);  
 }
[/font]

Here are my results:

Step -- Flash -- SRam -- Desc --
1 2858 1901 No Variables Declared
2 2868 1899 int a Declared Globally
3 2910 1897 int z Declared Globally
4 2898 1899 const int z Declared Globally
5 2898 1899 const int z Declared Locally
6 2898 1899 #define is used in if statement
7 2908 1897 int i Declared Local and Conditionally
8 2838 1899 int i not Declared Conditionally
9 2928 1897 int z Declared Locally and int i Declared Conditionally

Many of the steps are obvious and the results are expected. However, there are a couple that have me a bit puzzled.
Not so obvious (at least to me) results:

  • using "#define" and "const int" are equivalent (Step 4 and Step 6)
  • declarations inside a conditional block are only declared if the condition is true (Step 7 and Step 8)
  • declaring a "const int" requires 12 fewer bytes in Flash than declaring "int" (Step 3 and Step 4)

Here are my questions:

  1. Look at Step 9. Variable z is declared locally as a regular int and assigned a value that will satisfy the conditional to declare variable i as an int. However, there are only 4 bytes consumed from SRam. I was expecting that 6 bytes be consumed (2 bytes each for variables a, i, z). Why only 4?

  2. Consider Step 7. Both calls to the FreeRam routine in Setup() returned 1897 bytes free. I would have expected the FreeRam call at the beginning of Setup() to return 1899 and the last FreeRam call to return 1897. How did the first FreeRam routine know it was going to need the Ram space before the conditional was evaluated? Perhaps, is z going out of scope (and then freed) since it isn't used any more in the routine?

Thanks.

Without looking at the generated code its guesswork - compilers optimize and
optimizations aren't always easy to second-guess.

MarkT is right.
Arduino uses very moderate optimizations. The avr-gcc compiler can do a lot more. In the past there was a global optimization for variables. The compiler could remove every function and keep every variable in registers throughout the whole program.
But even with moderate optimizations, the compiler can decide to keep variables in registers or use constants in ram or in flash. That decision can change with every code line.