Go Down

Topic: String literals - I see no point to specify F() (Read 3653 times) previous topic - next topic

arduino_314

Nov 22, 2016, 04:12 pm Last Edit: Nov 22, 2016, 04:39 pm by arduino_314
Is there any reason to specify F() all the time with string literals, especially inside method calls for some classes? I personally do not see any. There is some minor speed difference, which are in this case marginal.

Currently, specifying F("...") free some SRAM which is allocated otherwise in temp variable. I would suggest simply to force C/C++ preprocessor to do that automatically for us (to assume/put F() around literals), or perhaps better even to have some directive for that.

I can see quite a lot waste of SRAM in novice codes, especially for mega8/16/168/328p that may be crucial feature, as well code would be much readable. That may sound minor for few lines of text, however if there is more than 20 (device for some equipment testing, etc), that may be quite demanding, especially if someone else wrote the code.

For instance: LCD, Serial and other libraries allow string literals.

Code: [Select]

void setup() {
  Serial.begin(115200);
  Serial.println("Testing for ATmegaMath2560");

  for (int i = 0; i < 128; i++) {
    Serial.print("Test line ");
    Serial.println(i);
  }
  
}

void loop() {}

Sketch uses 1,982 bytes (0%) of program storage space. Maximum is 253,952 bytes.
Global variables use 224 bytes (2%) of dynamic memory, leaving 7,968 bytes for local variables. Maximum is 8,192 bytes.


Code: [Select]

void setup() {
  Serial.begin(115200);
  Serial.println(F("Testing for ATmegaMath2560"));

  for (int i = 0; i < 128; i++) {
    Serial.print(F("Test line "));
    Serial.println(i);
  }
  
}

void loop() {}

Sketch uses 2,048 bytes (0%) of program storage space. Maximum is 253,952 bytes.
Global variables use 186 bytes (2%) of dynamic memory, leaving 8,006 bytes for local variables. Maximum is 8,192 bytes.

AWOL

Quote
I would suggest simply to force C/C++ preprocessor to do that automatically for us (to assume/put F() around literals), or perhaps better even to have some directive for that.
What about the strings that you want to pass that you don't want to be constants?

Add a few more strings to your examples, and watch how the RAM/ROM situation changes.

arduino_314

What about the strings that you want to pass that you don't want to be constants?
That is not a literal I refer on. I refer only on classes allow literals stored in ROM only.

Add a few more strings to your examples, and watch how the RAM/ROM situation changes.
It behaves as explained and what was reason for the suggestion. I do not see what you point on.

AWOL

That is not a literal I refer on. I refer only on classes allow literals stored in ROM only.
How do you distinguish them?
Ah! I know - use the F() macro.

Quote
It behaves as explained and what was reason for the suggestion. I do not see what you point on.
The reason for the suggestion was that as you add strings, you'll see that the slightly larger ROM usage flattens out using the F() macro, but not using it, RAM usage just grows, as does ROM usage.

arduino_314

#4
Nov 22, 2016, 08:29 pm Last Edit: Nov 22, 2016, 08:41 pm by arduino_314
How do you distinguish them?
Ah! I know - use the F() macro.
If it support it, RAM will be saved. The whole point.

The reason for the suggestion was that as you add strings, you'll see that the slightly larger ROM usage flattens out using the F() macro, but not using it, RAM usage just grows, as does ROM usage.
Exactly.

The whole point is to save valuable RAM. I had great headache to preserve minimum RAM for all with the complex project use SD card and Nokia 5110 display and many necessary text messages with 328p.

If use supported F() macro with the method calls, ROM will be enlarged by number of literals + 8 (call if have one parameter), but reserved RAM remains still.

If not used F() macro, ROM and reserved RAM will rise every time, regardless number of calls and individual literals length.

Otherwise, if compile at all with near to or override RAM limit, MCU will regularly reset or behaves unexpected. Simply I do not see the point to specify F() if conditions are as upper explained - that should be done automatically by compiler.

Hope it is all clear.

AWOL

Quote
that should be done automatically by compiler.
But the compiler can't know your intentions.

Delta_G

Because sometimes I might want to be able to modify the contents of the string, and if the compiler forced it into PROGMEM that wouldn't be possible.  I'd rather the compiler let me choose whether or not I want the string literal in RAM.  
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

arduino_314

But the compiler can't know your intentions.
Compiler should not waste the RAM with no reason.

Some compiler directive would be nice.

arduino_314

These are literals
Because sometimes I might want to be able to modify the contents of the string, and if the compiler forced it into PROGMEM that wouldn't be possible.  I'd rather the compiler let me choose whether or not I want the string literal in RAM. 
These are literals, i.e. constants in ROM, not variables!

If you intend to change something, variables are used, not constants.
How you will access to the copy into RAM compiler created automatically?

Without F(), compiler automatically reserve memory for literals and automatically copy the constant into RAM and use it as it. That have a sense in time critical operations (it is slower just a bit to convert it from memory to RAM dynamically), but in that content I see no point to waste RAM for any literal (constant).

westfw

Quote
These are literals, i.e. constants in ROM, not variables!
What you seem to be missing is that because of the AVR's harvard architecture, string constants in ROM are NOT the same as string constants in RAM, nor is the difference supported by C/C++.   That means that a function that needs to handle both RAM strings and ROM/Constant strings (say "strcmp_general()", or "Serial.print()" needs to be able to tell whether the argument is a ROM pointer or a RAM pointer, so that it can take the appropriate DIFFERENT actions.

The F() macro forces a string constant to have a different type (C++ wise) as well as storing the string in ROM, so that C++ polymorphism can execute different code for the different types of arguments. (which is actually pretty tricky, since a lot of types that might be appropriate aren't different c++-wise ("const" doesn't help, for example, nor the "__attribute__((__progmem__))".  The newest C compiler supports named address spaces and "__flash" keyword, but it's a feature that C++ has not accepted, and it's not very useful in general without polymorphism...)

On an ARM or PIC32 or other CPU with a unified address space, F() adds complexity that isn't really needed, EXCEPT for backward compatibility with AVR sketchs.


arduino_314

What you seem to be missing is that because of the AVR's harvard architecture, string constants in ROM are NOT the same as string constants in RAM, nor is the difference supported by C/C++.   That means that a function that needs to handle both RAM strings and ROM/Constant strings (say "strcmp_general()", or "Serial.print()" needs to be able to tell whether the argument is a ROM pointer or a RAM pointer, so that it can take the appropriate DIFFERENT actions.
...
AVR use modified Harvard architecture, which means (relevant for this case) it can read code space as data. Reading data directly from code space allow to spare RAM from copying string literals.

The point is that compiler have to know from which space to read data (ROM or RAM) and in this case default is RAM.

I admit I missing why is so difficult to force reading string literals directly from ROM (code space), avoiding RAM reservation in case class support reading from code space. Thus Arduino preprocessor (if exists) can silently insert F().

Reserving RAM for string literals need to be printed is in general nonsense, unless printing is so critical that reading time from code space affect moderately performance.

If answer for implementation is NO, then further arguing is pointless.

westfw

Quote
AVR use modified Harvard architecture, which means (relevant for this case) it can read code space as data. Reading data directly from code space allow to spare RAM from copying string literals.
The AVR harvard architecture isn't "modified" enough to allow C to "read data direction from code space." (CM3 and CM4 are examples of "modified harvard architectures" that are compatible.
strcmp("abc", stringvar), strcmp(stringvar, "abc"),  and strcmp(strvar2, stringvar) would each require (significantly) different code if literal strings were placed in flash by default.

(Interestingly, perhaps, there are some newer AVR chips (ATtiny817 and friends) that change the memory layout such that this will no longer be the case!)

westfw

(If you're arguing just for Arduino pre-processing to ALWAYS treat/force Serial.print("stringliteral"); the same as Serial.print(F("stringliteral")), that might be more doable.   But it would be arduino specific pre-processing (which many feel should be kept to a minimum), and wouldn't/couldn't apply to string literals or string functions in general.)


Delta_G

It would sure break a ton of libraries that have functions that take a const char * but aren't overloaded to also take __FlashStringHelper.
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

arduino_314

#14
Nov 24, 2016, 08:40 am Last Edit: Nov 24, 2016, 09:34 am by arduino_314
(If you're arguing just for Arduino pre-processing to ALWAYS treat/force Serial.print("stringliteral"); the same as Serial.print(F("stringliteral")), that might be more doable.   But it would be arduino specific pre-processing (which many feel should be kept to a minimum), and wouldn't/couldn't apply to string literals or string functions in general.)
Exactly. It would be Arduino specific. As state in the first post, I refer only on some Arduino used class methods allow literals stored in ROM only as a parameter : Serial.print, LCD.print and similar.

I do not really care about AVR-GCC implementation, nor support for automatic conversion for low level functions. Some bases then in AVR-GCC need to be carefully redesigned, but I do not consider it would be worthed.

Thank you for understanding

Go Up