When running alr build –release I seem to get the same Used Size number for the FLASH region but shouldn’t the binary be ever so slightly smaller in the second case at the cost of putting it together at runtime?
I would guess that constant folding simply does not take optimisation flags in to account. If you look at the -gnatG output you’ll see that folding for constant strings actually happens in the frontend.
The answer here is ‘it depends’.
We’re getting well into the weeds, and there are multiple ways to handle things, these ways also are impacted by the capabilities of (1) the compiler you are using, (2) the medium you are using [RAM vs ROM], (3) the capabilities of the architecture you are running on.
For example, in the ‘deep past’ of computing, there were many designs for specialized computers. (eg LISP Machine, Database Machine, etc.) So, imagine a system wherein text is the main thing a “String Machine”, wherein Strings, Ropes, and such are first-order base types (even at the ASM machine-mnemonics level) and decomposing some texts into “shared substrings” is a normal way to handle text. — In such a system you could have your separator as a substring, common to both constants, and burn these to ROM.
Now, consider a system where C’s notions are common and embraces, a microcontroller: there’s no way to have a common separator substring that doesn’t take up more space in addition to the other strings, the closest of which would be an init function belching the base string into some memory location and then appending the separator on… but even that is likely too ‘high level’ for such a system… after all null-terminated strings are terrible.
I was hoping more generally that defining a constant string in a parent package and re-using it in procedure arguments could result in only one copy of the strings bytes being in the binary and re-used rather than building a string from it and storing each fully constructed string. Rust has a defmt package but that isn’t what I want as I want the logs to always be useful in any situation.
If you are really trying to squeeze all the memory out of a program, GCC has a flag that is even more challenging than -Os, that being -Oz. It is documented as potentially requiring more operations to be computed but a lower binary size.
Though as mentioned here, I suppose that GNAT is just constexpr the constant expressions and adding them to the static part of the program, so they probably will not change. Also, it may be for the best. If you need to build strings at runtime, the compiler may start using more registers and stack, while if they live in the .static part of the program, they will just be a load instruction.
-gnatG seems to show only one instance but running strings on the created binary shows repeated instances of the same re-used constant string. Perhaps there is no clever size optimisation here as perhaps address handling could potentially use more space in some cases but if there is a gnat feature that can help then please let me know.
Edit: posted before I noticed @Irvise response. Unfortunately -Oz doesn’t seem to have any affect as you suspected. I guess there isn’t any kind of .rodata compression possibility though I do wonder about how binary obfuscation tools work but I might just have to revise my strategy.
Ledgard, Nagin, Hueras, 1979. Pascal with Style: Programming Proverbs:
Shortening the code, running the program faster, or using fewer variables are all popular pastimes. Not mentioning … the extra testing time needed to check the new and often subtle boundary conditions, are you sure that fewer machine instructions or faster machine execution is likely?
M. A. Jackson, Rules of Optimization:
Rule 1: Don’t do it.
Rule 2 (for experts only): Don’t do it yet.
W. A. Wulf More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason - including blind stupidity.
Donald Knuth We should forget about small efficiencies, say about 97% of the time: Premature optimization is the root of all evil.
Kernighan & Plauger, 1974. Elements of Programming Style:
Make it right before you make it faster.
Keep it right when you make it faster.
Make it clear before you make it faster.
Don’t sacrifice clarity for small gains in “efficiency.”
Let your compiler do the simple optimizations.
Keep it simple to make it faster.
Don’t diddle code to make it faster - find a better algorithm.
Instrument your programs. Measure before making “efficiency” changes.
It’s important to remember that in 1992, it was reported that an Ada (83) compiler, given unoptimized, straightforward Ada, produced smaller and faster machine code than assembly code hand-optimized by a team of experts (Ada Outperforms Assembly). I hope that optimizers have not become worse in the past 35 years. Clearly there is no point in even thinking about these things in 99.9% of cases, because the compiler will do it for you.
I guessgnat is always? optimising for speed in this case. My mistake was assuming an optimisation without testing (I thought about it a number of times). I am only at 70% but it is more of a concern now.
I guess I shall just make the log messages as short as possible now but still be a direct aid to immediate troubleshooting and keep a database of error codes to fuller information as necessary.