2

I'm having SRAM overflow problems with my 8bit atmega328p in my Arduino Uno, which has 2KB of SRAM, so I'm defining all my for loops with bytes as counters instead of ints. As in:

for(byte i=0;i<16;i++) buf[i]=(char)ap1[i];

Would it make any difference if I declared the integer literal 16 as a constant?

const byte BUFSIZE=16;

Or using the preprocessor:

#define BUFSIZE 16

There's also the macro PROGMEM which stores variables in flash memory, which I've decided to use for all my constants, but is it not the point of const to do this? as well as aiding the compiler in warning you if you screwed up changing the variable.

const byte BUFSIZE PROGMEM=16;

AFAIK, using const would save BUFSIZE in the flash memory, but how about using the preprocessor? Using the preprocesor would be the exact the same as using the literal 16, as far as i'm concerned.

This fear arose from the fact that string literals are saved in Flash and then transferred to SRAM, wasting twice the space, which you can solve using the arduino macro F("thisisastring"), which stores them in flash memory, and I don't want to waste 2 bytes of SRAM in every for loop I use

2
  • The compiler should optimise out any constants into the most compact form possible. Take a look at the assembly code if you're curious.
    – tomnexus
    Jun 4, 2015 at 8:55
  • 3
    Do you have a lot of global variables? Try to move everything into smaller functions using local variables except things that absolutely must be persistent. The compiler does a very good job of optimising constants out of SRAM so they are unlikely to be your problem.
    – Jon
    Jun 4, 2015 at 9:57

3 Answers 3

2

Well, it's easy to just try and see... Let's try the following program:

#ifdef USE_CONST
const byte BUFSIZE = 16;
#else
#  define BUFSIZE 16
#endif

char buf[BUFSIZE], ap1[BUFSIZE];

int main(void)
{
    for (byte i = 0; i < BUFSIZE; i++)
        buf[i] = ap1[i];    
    return 0;
}

I compiled this for an Uno both with and without #define USE_CONST, and it doesn't make any difference! avr-size reports the same sizes for both versions, and the generated assemblies are identical: the optimizer transformed the loop into something like

byte *Z = ap1;
byte *X = buf;
do {
    *X++ = *Z++;
} while (Z != ap1 + 16);

where X and Z are two of the CPU's pointer registers, and ap1 + 16 is an immediate constant, i.e. part of the instruction itself, c.f. the AVR addressing modes.

It is worth noting that, in C++, const implies internal linkage. Thus a const global variable is very much like a preprocessor constant.

This is unlike C, where you would need to qualify the variable as static const in order to get the same behavior. Otherwise the compiler would need to allocate storage to BUFSIZE, just in case it is referenced as extern from another source file, even though it can replace occurrences of BUFSIZE in the same file with its immediate value.

On the other hand, the Arduino IDE compiles your code with -ffunction-sections -fdata-sections and links it with --gc-sections, which means the linker will eventually get rid of the unused reserved storage.

3

I'm not familiar with the Atmel megaAVR microcontrollers or Arduino. But generally speaking, your memory optimization ideas are misguided.

For example, it's likely that the compiler implements local loop variables within a register so that they don't consume any RAM at all.

And the integer literal constants, such as 16 from your example, also likely don't consume any RAM. The compiler likely incorporates these constants into the code.

Whenever you optimize something it's best to go after the big fish first. You're focusing on the tiniest fish. To find the big fish, look at the *.map file produced by the linker. The map file should show how much RAM and Flash memory is used by your program. The RAM sections might be labeled "bss" and "data" in the map file. Hopefully the map file will also detail how much RAM is used by each compilation unit. Find the compilation units that are using the most RAM and focus on them. Maybe there are large buffers or arrays defined in those compilation units that can be reduced in size or redesigned.

The map file is also where you could check to see if your optimizations have made any difference. For example, try some of the optimization you described and see whether the map file changes significantly.

2

Yes, defining "const byte bufsize" would make a difference - it'll use one additional byte of SRAM. All variables are stored in RAM unless they're annotated as being PROGMEM.

A #define gets translated into its value by the preprocessor, and so looks the same to the compiler.

Your constant '16' isn't taking up any SRAM in the first place; look elsewhere (at actual variables) if you need to optimise.

String literals are treated the same way as arrays of any numeric type: they're copied to SRAM on startup because the code has to be able to dereference the pointer (which is what myarray[x] does) and find the location in RAM. Single numeric constants do not have this issue - they're value types - and so don't get copied into RAM.

3
  • It's actually slightly worse. The const byte is first stored in flash by the compiler/linker, and then during the preamble it's copied to SRAM. So you're taking twice the hit needlessly. Jun 4, 2015 at 13:34
  • Did the test: you are actually taking zero times the hit, not twice. Jun 5, 2015 at 14:48
  • At higher optimization levels of course (for a given definition of "higher"). Jun 5, 2015 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.