I was told that if we use local variables in Arduino, when the function ends, the space those variables were using should be freed, but, in practice, I can't find any used memory reduction at the end of the called function. Here is my sketch to test free memory:
#include <MemoryFree.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(4, INPUT_PULLUP);
fm();
}
void loop() {
// put your main code here, to run repeatedly:
to_do();
}
void to_do(){
int foo = digitalRead(4);
Serial.println("on the first day");
fm();
int some = 6*foo;
fm();
long somee = 30*foo;
fm();
char somechar [8] = {foo};
fm();
Serial.println(F("end of sketch"));
delay(8000);
}
void fm(){
Serial.print(F("freeMemory()="));
Serial.println(freeMemory());
delay(800);
}
And here's the result:
on the first day
freeMemory()=1795
freeMemory()=1795
freeMemory()=1795
freeMemory()=1795
end of sketch
I was expecting something like free memory changing with every variable declaration, but, as I see, the memory is allocated from the very beginning. Is there any way to force local variables to be freed? Sincerely, I don't wan't (and should not) use Malloc and Free to manage dynamic memory.
If you switched all warnings on in preferences, or verbose or whatever it is, you would likely see a compiler warning about unused variables. As you never do anything with some, somee or somechar, they'll get optimized out. So will foo by the way. The only code that will get compiled are your two print statements and your calls to fm(). Not a very exciting program.
You have to do something with those new variable, else the linker sees that they are not used and will throw them away. Add e.g. serial print statements to print them.
You're adding variables. The compiler is seeing that they aren't used and is optimizing them away. Try doing something useful with those variables like printing their value. Then you'll see that they start to take up some memory and you'll see that they go out of scope once the function ends.
Thanks, I'm sorry about the unused variables thing. I changed them to this:
#include <MemoryFree.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(4, INPUT_PULLUP);
fm();
}
void loop() {
// put your main code here, to run repeatedly:
to_do();
}
void to_do(){
long foo = digitalRead(4);
Serial.println("on the first day");
fm();
long some = 6*foo;
some = some + 6;
fm();
long somee = 30*foo;
somee = somee + some;
fm();
long someint [8];
someint [1]=foo + somee;
fm();
Serial.println(someint[1]);
Serial.println(F("end of sketch"));
delay(8000);
}
void fm(){
Serial.print(F("freeMemory()="));
Serial.println(freeMemory());
delay(800);
}
But still got this result:
freeMemory()=1828
on the first day
freeMemory()=1828
freeMemory()=1828
freeMemory()=1828
freeMemory()=1828
6
end of sketch
Also, there is no compiler warning about unused variables (al warnings displayed)
If I do the same printing Strings I can actually see the memory being used, but, also, at the end of the function they're not send to oblivion.
[Edit] I'm interested about using a GPRS modem + SD card and some serial comm. Al of them need to use buffers (in special the sd card) and can fill up the DRAM. So I was thinking about keeping everything clean inside each function so the available DRAM will be much bigger (and stable), so that's why I'm printing stuff to test the memory management
Thanks, but I was also checking at Setup().
Here the result of measure at loop:
Setup()
freeMemory()=1828
Start of loop
freeMemory()=1828
start of to_do()
freeMemory()=1828
freeMemory()=1828
freeMemory()=1828
freeMemory()=1828
6
end of to_do()
freeMemory()=1828
End of loop
Start of loop
freeMemory()=1828
...
...
anxietyscroll:
[Edit] I'm interested about using a GPRS modem + SD card and some serial comm. Al of them need to use buffers (in special the sd card) and can fill up the DRAM. So I was thinking about keeping everything clean inside each function so the available DRAM will be much bigger (and stable), so that's why I'm printing stuff to test the memory management
It's likely that your buffer will need to be global or static due to the speed of the GPRS modem, SD (SPI) and Serial. It will take the serial port running at 115200 86.8 microseconds to receive another character after the first, for example. You don't want to be waiting in the loop for the buffer to fill, just for the memory conservation. Depending on how you are using the three, you might be able to use the same buffer for all of them.
That behavior is several layers down from the language of Arduino. My point was that you can be sure that AVR microcontrollers operate as you have read. For additional information see here. For help in optimizing your use of RAM, see here.
Variables local to functions are stored on the stack. Upon exiting the function, those memory locations become available for use in other function calls. That’s simply a fact, despite clumsy attempts to prove otherwise.
gfvalvo:
Variables local to functions are stored on the stack. Upon exiting the function, those memory locations become available for use in other function calls. That’s simply a fact, despite clumsy attempts to prove otherwise.
Also, the compiler can allocate ALL the space on the stack for all the variables as the first thing it does in the function. It does not need to allocate space as it progresses through the function. In this way the allocation for all local variables is just a single stack pointer subtraction for ALL variables and not one for each variable allocated.
long foo = digitalRead(4);
Serial.println("on the first day");
fm();
long some = 6 * foo;
some = some + 6;
fm();
long somee = 30 * foo;
somee = somee + some;
fm();
long someint [8];
someint [1] = foo + somee;
fm();
Serial.println(someint[1]);
Serial.println(F("end of sketch"));
delay(8000);
}
You seem to have two major misconceptions, and one less serious...
While local storage is allocated for the duration of the function, it is NOT allocated on a line-by-line basis; only at the boundaries of scopes; in this case some, somee, someint[] would all be allocated at the beginning of the to_do() function, if they were allocated at all.
You seriously underestimate the compiler's ability to detect unused variables and optimize them into non-existence. "someint[1] = something" ? "Hah! Clearly not using the other elements allocated to someint[], so we don't need them!" In fact, your program dosn't show any difference in free memory, because it never actually allocates any memory! "local variables are stored on the stack" is a nice simplification, but in fact stack memory will only be used when the compiler can't figure out how to leave all of the variables in a function in registers. (I disassembled your code - no memory allocated!)
(the minor issue) The optimization issue is further clouded by the fact that Arduino programs are compiled with "link time optimization." This means that it notices that to_do() is pretty simple, and it's only called once, so to_do disappears as a separate function and becomes in-line code. I'm actually not sure where memory allocation would happen in that case, if it was needed...
Try something like this:
#include <MemoryFree.h>
void setup() {
Serial.begin(9600);
pinMode(4, INPUT_PULLUP);
fm();
}
void loop() {
Serial.println("\n*******\nloop start");
fm();
to_do();
fm();
to_do();
Serial.println("loop end");
fm();
}
void to_do() {
Serial.println("to_do start");
fm();
long data[50];
for (int i = 0; i < 50; i++)
data[i] = digitalRead(3);
for (int i = 0; i < 50; i++)
digitalWrite(13, data[i]);
Serial.println("to_do end");
fm();
delay(4000);
}
void fm() {
Serial.print(F("freeMemory()="));
Serial.println(freeMemory());
delay(800);
}
*******
loop start
freeMemory()=1757
to_do start
freeMemory()=1539
to_do end
freeMemory()=1539
freeMemory()=1757
to_do start
freeMemory()=1539
to_do end
freeMemory()=1539
loop end
freeMemory()=1757
westfw:
but in fact stack memory will only be used when the compiler can't figure out how to leave all of the variables in a function in registers
I would have guessed that before the compiler was able to store local variables in processor registers, it would need to ensure there were no recursive function calls (direct or indirect) to the function in question. How does it do that? Trace though all possible paths in the program? What about calls between different compilation units?
it would need to ensure there were no recursive function calls
There's a defined API that says which registers have to be saved when you call another function, and that's enough. Essentially, that's the simplest form of "local variable on the stack" - "I used a bunch of registers, but I pushed their old values on the stack before I used them." (There are actually two possible forms of this: "I have values in registers that the next called function is not required to save, so I need to save them before I call the function" and "I need to used registers that I'm not allowed to modify, so my function will save them before it uses them.")