I've got a question concerning the Arduino library "RTClib" by Adafruit and the use of the word "static".
Here you can see an excerpt from the example provided for the pcf8523 real-time clock:
#include "RTClib.h"
RTC_PCF8523 rtc;
void setup () {
while (!Serial) {
delay(1); // for Leonardo/Micro/Zero
}
Serial.begin(57600);
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (! rtc.initialized()) {
Serial.println("RTC is NOT running!");
}
}
void loop () {
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
/*
do some more stuff
*/
}
In the beginning of loop() they wrote:
DateTime now = rtc.now();
Thus, with every iteration of loop() "new" gets defined anew (on the heap?!), right? This appears inefficient to me.
I thought about rewriting it this way:
static DateTime now; // static declaration, executed only once
now = rtc.now(); // assignment to "now" every time loop() starts over
In this case "new" should be allocated on the stack like a global variable, right?
I want to use "static" to increase overall performance by reducing work for the processor and the heap (thereby avoiding heap fragmentation). Does this make sense or will it cause more problems than it solves? Keep in mind that I want to learn both proper programming for Arduino and proper coding in general.
Link to the library: https://github.com/adafruit/RTClib
Thank you! :-)
EDIT: For some reason I CAN NOT declare DateTime now;
in global scope or else the program won't run. Don't know why though. This is why I want to use static in the first place.
2 Answers 2
You wrote:
DateTime now = rtc.now();
Thus, with every iteration of loop() "now" gets defined anew (on the heap?!), right?
No. Local variables like this are usually allocated on the stack, although the compiler may keep them in CPU registers if that helps optimization.
static DateTime now; // static declaration, executed only once
In this case "new" should be allocated on the stack like a global variable, right?
No. It is allocated statically, in the .bss section, like uninitialized global variables.
I want to use "static" to increase overall performance by reducing work for the processor
Not sure it will make much difference. Static allocation may save you a few CPU instructions compared to stack allocation (the instructions that move the stack pointer). On the other hand, it will cost you a few instructions compared to register allocation (the instructions to access the RAM).
avoiding heap fragmentation
Stack allocation does not use the heap, and the stack never fragments.
Does this make sense or will it cause more problems than it solves?
If you really care about sub-microsecond differences in the execution
time, you should either look and the disassembly of the program, or do
some benchmarking. Otherwise just write whatever makes more sense from
the point of view of the program's logic: does the variable need to keep
its value across calls to loop()
? If the answer is "no", do not use
static
.
You don't need to use the static
keyword.
The cleanest solution is to define DateTime
outside of the loop
function:
DateTime now;
This will allocate the memory, and call the constructor of the class DateTime
initializing it.
In case you would need a basic variable (like an int
), you should initialize it, like:
int now = 0;
When you use it in setup
or loop
you use the created instance (without the class name to declare it):
void loop() {
...
now = rtc.now();
}
Also, find a better name for now
, because the variable is the same as the rtc
's function now
.
Update
after your comment, it cannot be used as a global variable:
I didn't try it myself, but I suspect, the constructor calls a function that uses variables or a function that is not initialized at that point. Actually, it's quite bad practice.
Anyway, one way to circumvent and stick as much to what I wrote above, is to use a pointer instead:
RTC_PCF8523* rtc = NULL;
Than you create an empty pointer, without calling the constructor. You will do this at the end of setup
:
void setup() {
...
rtc = new RTC_PCF8523();
}
This will cause the variable to be put on the stack.
Than when using it, you call:
now = rtc->now();
Note you have to use the -> notation, as rtc is a pointer.
-
1Forgot to mention: Defining it outside of loop() in the global scope DOES NOT WORK! Don't ask me why, I spent hours just to find this problem. This is the reason why I wanted to use static in the first place. Will edit my post. And concerning the name "now": It's just what they used in the example, I won't use it, but good point!blackdaw– blackdaw2020年04月22日 19:54:18 +00:00Commented Apr 22, 2020 at 19:54
-
I updated my answer, just try if it helps.Michel Keijzers– Michel Keijzers2020年04月22日 20:10:42 +00:00Commented Apr 22, 2020 at 20:10
-
1Re "I suspect, the constructor calls a function that uses variables or a function that is not initialized at that point": I can't see anything wrong in this constructor.Edgar Bonet– Edgar Bonet2020年04月22日 20:34:22 +00:00Commented Apr 22, 2020 at 20:34
-
@EdgarBonet ... me neither, I'm wondering why creating it as global variable wouldn't work.Michel Keijzers– Michel Keijzers2020年04月22日 20:36:46 +00:00Commented Apr 22, 2020 at 20:36
-
See the comments above beneath @EdgarBonet 's solution. It compiles and uploads but doesn't execute.blackdaw– blackdaw2020年04月23日 17:16:07 +00:00Commented Apr 23, 2020 at 17:16
DateTime now;
in global scope": what happens if you do so? A compilation error? Incorrect run-time behavior? I tried and it compiles just fine (but I have no RTC for testing).DateTime now = rtc.now()
might have utilize "copy elision", so it gets constructed directly intonow
variable and therefore it's faster than your approach (as you effectively blocked this possibility - so it has to create temporary object and copy it into now variable).Setup()
.Setup()
starts withSerial.begin(9600); Serial.println("test");
Blinking the builtin-led doesn't work either.