Author Topic: Disabling Compiler Optimization vs Using volatile  (Read 28245 times)

0 Members and 1 Guest are viewing this topic.

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Disabling Compiler Optimization vs Using volatile
« Reply #50 on: June 27, 2025, 12:50:46 pm »
Quote
Why unintended?  You wrote code that does nothing except set a to 0. The optimised code has precisely the same effect, but faster. That's what you WANT.
It was an example to make a point that you can go from intended results to unintended results with a simple optimization change. If you had wanted a loop to kill time of course volatile is the answer, but if you had something similar in place which was not correct, but was acting perfectly fine while you sat on -Og for a month, as soon as you go the some other optimization all your code that acted fine may change to something you didn't expect. If you had been on -Os from the start, that incorrect code would never have produced the results you wanted and you would be forced to deal with it at the time of code creation.

Quote
To be honest with a non-extern variable I'm surprised it didn't just delete the useless variable entirely.
Seems odd it didn't disappear, and if you remove the static the code doesn't change either. Sort of my point- for this simple example, even though pointless code, ends up producing about 3 different versions with the various optimizations. Why not deal with just a single version, always.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #51 on: June 27, 2025, 03:39:32 pm »
Quote
Why unintended?  You wrote code that does nothing except set a to 0. The optimised code has precisely the same effect, but faster. That's what you WANT.
It was an example to make a point that you can go from intended results to unintended results with a simple optimization change. If you had wanted a loop to kill time

That's where you're going wrong. That is not a valid use of a high level language. If you want to write assembly language then write assembly language, don't abuse C.

If you want a delay then use a timer. Or write in assembly language if your CPU gives execution time guarantees.

Quote
if you had something similar in place which was not correct, but was acting perfectly fine

"acting" perfectly fine doesn't mean it's correct, only that you got lucky. Today, a particular optimisation level on a particular compiler might seem to work. But a different compiler, a different machine, even a new release of the same compiler — any of of those can expose your buggy code for what it is.
 
The following users thanked this post: JPortici

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17824
  • Country: fr
Re: Disabling Compiler Optimization vs Using volatile
« Reply #52 on: June 27, 2025, 03:55:21 pm »
On the simplest, single-core architectures, the usual way of implementing atomic operations when there's lack of specific instructions is just to disable interrupts / instructions that must execute "atomically" / enable interrupts (if they were enabled before), as the only way of interrupting instruction flow is precisely from interrupts. Sounds barbarian, but that works and that's pretty much how atomics are usually implemented, for instance, on the Cortex-M0.
You mean, like the example I showed for OP in reply #34? ;D

Yes, pretty much.
 

Online tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Disabling Compiler Optimization vs Using volatile
« Reply #53 on: June 27, 2025, 07:09:40 pm »
Rust is indeed imperfect,... I bet that it will succeed in the long term,...
If I had my time again, I would learn Rust in preference to C.

We hear you: C is horrible and no-go, because the standard has a few special edge cases which are undefined, meaning that compiler writers can do whatever they want with a few select constructs, and programmers should avoid those.

The problem is that the edge cases and "surprising" cases are commonly encountered and commonly trip people up. And even if they don't trip people up today, the next compiler version may have "better" optimisation. Solution: fix on a compiler version, and don't change it during the project maintenance/enhancement period.

Other languages manage to avoid that.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Online tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Disabling Compiler Optimization vs Using volatile
« Reply #54 on: June 27, 2025, 07:12:09 pm »
We have some long threads on optimisation. I use -Og because one can still debug quite well and the result is not significantly worse than -O3. And developing in say -Og and releasing with -O3 is bad risk management because you lose all the regression testing that was naturally done during development.

The compiler flags should be regarded as part of the source code. Ditto compiler version.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 4666
  • Country: us
Re: Disabling Compiler Optimization vs Using volatile
« Reply #55 on: June 27, 2025, 10:22:28 pm »
Quote
I start out at -Os, stay at -Os, and never move out of -Os. If there is ever a need to understand generated asm code that optimization makes difficult to read, I will make the function temporarily noinline

Yes, that sounds good.

How do we feel about "Link Time Optimization" ("-flto" for gcc; something else for the "pro" versions of MCC, I think.)
It's sure nice in its results on program size, and in simplifying the amount of thought you have to apply "up front" in factoring your libraries/etc.   But it can REALLY obscure the object code!
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #56 on: June 27, 2025, 11:58:54 pm »
We have some long threads on optimisation. I use -Og because one can still debug quite well and the result is not significantly worse than -O3. And developing in say -Og and releasing with -O3 is bad risk management because you lose all the regression testing that was naturally done during development.

The compiler flags should be regarded as part of the source code. Ditto compiler version.

Ditto OS version, libraries depended on, ...
 

Online tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Disabling Compiler Optimization vs Using volatile
« Reply #57 on: June 28, 2025, 12:02:23 am »
We have some long threads on optimisation. I use -Og because one can still debug quite well and the result is not significantly worse than -O3. And developing in say -Og and releasing with -O3 is bad risk management because you lose all the regression testing that was naturally done during development.

The compiler flags should be regarded as part of the source code. Ditto compiler version.

Ditto OS version, libraries depended on, ...

Libraries almost goes without saying.

I didn't include OS version since I was thinking of cross-compiling for an MCU. Probably confusing two independent current threads; if my daughter was part of this conversation she would ask me what day of the week it is and who the PM is :(
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Disabling Compiler Optimization vs Using volatile
« Reply #58 on: June 28, 2025, 12:24:55 am »
Quote
"acting" perfectly fine doesn't mean it's correct, only that you got lucky. Today, a particular optimisation level on a particular compiler might seem to work. But a different compiler, a different machine, even a new release of the same compiler — any of of those can expose your buggy code for what it is.
You seem to be agreeing with me. Creating incorrect code is not an unusual thing, nor is it that unusual to get what appears to be correct results from incorrect code (and could be because of optimization setting). I'm advocating using a single optimization, particularly -Os, so you get incorrect results from incorrect code, sooner. Or more accurately you get consistent results, whatever your code correctness may be or what optimization you choose.

The example was only to point out extremely different results from different optimizations with just a few lines of code, not about how to produce delays. I'll try another example and I know that I'm doing it wrong, that is part of the point, I also know a delay in main does nothing useful or that this code would typically be different-
https://godbolt.org/z/nW6P661x1
in this case if you were using -Og you would get correct results with incorrect code, and when you move from -Og to anything else it will break (in different ways- -O1 will block forever, and -O2/Os will simply optimize completely away). The code IS incorrect, and if you had started at -Os, or in this case anything other than -Og, you would have realized your mistake right away. The point, again, is not the specific details of the example or at what optimization setting the code starts to break, but that optimizations are producing radically different results. Typically -Os gets the compiler to 'critique' your code the most, of course not directly via error or warning messages, but by actual results. Just as I want -Wall and -Werror, I want the compiler to use its optimizations to reveal any code that cannot stand up to those optimizations. Right now, not a month later.

Now, these simple/small examples may not be exact representations of real code, so instead take a project that is of some size and you now have a lot of code so there are much more opportunities to make a mistake that only a higher optimization may reveal. No one is going to switch to a higher optimization just as the mcu goes out the door no matter how confident one may be in the code, so why not pick a final optimization setting when a project starts and stick to it.


Quote
How do we feel about "Link Time Optimization" ("-flto" for gcc; something else for the "pro" versions of MCC, I think.)
Probably depends on how little flash you have or how close you are to filling it with little to no options for an upgraded mcu. It seems to be less of a problem in recent years finding mcu's with enough flash to not get overly concerned about filling them up and I would guess at least in the 32bit world most users are not pushing the limits of flash storage except for maybe those that are in the small range such as 4/8K.

I have an stm32 project up that is 6.8K (-Os), added flto and it compiles to about 6.7K. Probably not a good representation as there are only 2 cpp source files and all the rest are headers (cpu, mcu, nvic, gpio, print, i2c, dma, systick, uart, and so on). I guess its already mostly all-in-one.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #59 on: June 28, 2025, 12:38:31 am »
We have some long threads on optimisation. I use -Og because one can still debug quite well and the result is not significantly worse than -O3. And developing in say -Og and releasing with -O3 is bad risk management because you lose all the regression testing that was naturally done during development.

The compiler flags should be regarded as part of the source code. Ditto compiler version.

Ditto OS version, libraries depended on, ...

Libraries almost goes without saying.

I didn't include OS version since I was thinking of cross-compiling for an MCU. Probably confusing two independent current threads; if my daughter was part of this conversation she would ask me what day of the week it is and who the PM is :(

Any given compiler generally needs a specific range of OS versions and std libs to even run.
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 11320
  • Country: fi
Re: Disabling Compiler Optimization vs Using volatile
« Reply #60 on: June 28, 2025, 05:32:11 am »
Other languages manage to avoid that.

 :-DD

It's nice to have strong faith...
 

Online hans

  • Super Contributor
  • ***
  • Posts: 1995
  • Country: 00
Re: Disabling Compiler Optimization vs Using volatile
« Reply #61 on: June 28, 2025, 07:10:09 am »
I agree with cv007 statement about finding things that fall over now instead of later.

You can keep optimization levels the same during development, but that only works when compiler versions are also kept the same. There a ton of tunables behind optimization levels which can be changed at any moment. If you're using the system compiler which gets updated, then that's already not the case.

For my hobby projects, I've a Jenkins CI environment that checks if all unit tests still pass, and then runs almost all said test cases through a special emulator environment I developed. Its based on RISC-V (either rv32i or rv32im) and basically runs each test case in isolation to monitor for regressions in speed and code size as well. I run these cases against an array of optimizations I'm most interested in: -O0, g, s, 2 and 3. The graphs I post has left, runtime in cycles, and right, image size in bytes.

I personally use -O2 the most. The reason is a balanced optimization in cases where -Os can get really slow. In my experience I often don't need the extra FLASH and I'd rather not incur massive runtime penalties.
Now most code I write is trying to be as insensitive as possible to optimization level (not looping over uninitialized variables etc.). This is the majority of my code base: boring statistics of Os being faster/smaller than Og, and O2 often beating Os speed by a tiny bit (and O3 not doing much at all).

However, some code is much more prone to poor/good optimization than others. In particular in C++ with virtual methods, or in C with some function pointers, the compiler will not perform devirtualization on level Os whereas it will on O2 and O3. For my GUI/display chipset stack, this results in quite a performance hit for framebuffer manipulations.

Another case is in my CircularQueue object which can take any object type and create a circular storage for it. The copying in/out of the object type is done via the assignment operator. In -Os, this will typically be fine, but then when I test a 17-byte object I get a bytewise memcpy that is very very slow. Even -Og is fine with this, so -Os shows a massive regression in performance.

Now these graphs are microbenchmarks so not best evaluated for a whole application, but you can do the same tests (manually) for different optimization levels and reach simillar conclusions.
« Last Edit: June 28, 2025, 07:16:25 am by hans »
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #62 on: June 28, 2025, 07:59:03 am »
I run these cases against an array of optimizations I'm most interested in: -O0, g, s, 2 and 3. The graphs I post has left, runtime in cycles, and right, image size in bytes.

Nice setup.

You missed my default, -O aka -O1. I've never included -Og in my own comparisons, but I think -O does do some size/speed optimisations that -Og won't. But I don't have anything against -O2 and take your point about C++ devirtualisation.

I've sometimes found, especially on RISC-V, that the -Os heuristics aren't all that well tuned -- especially because gcc is NOT aware of the C extension -- that you can surprisingly often end up with fewer instructions but larger code than -O2. But you won't see that effect with rv32i or rv32im.

I'm not aware of any commercial microcontrollers that don't have the C extension (even the 10c CH32V003 is rv32ec), so I guess you're using an FPGA soft core? It gives more code size reduction than -Os with less (or zero) speed cost.
 
The following users thanked this post: hans

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Disabling Compiler Optimization vs Using volatile
« Reply #63 on: June 28, 2025, 10:54:20 am »
Quote
The copying in/out of the object type is done via the assignment operator. In -Os, this will typically be fine, but then when I test a 17-byte object I get a bytewise memcpy that is very very slow. Even -Og is fine with this, so -Os shows a massive regression in performance.
Is this simply the result of using packed structures?

This example shows when packed is used then memcpy is called, and would assume it will resort to byte copy since the destination address will/could be unaligned (I think BIGBLOCKSIZE is 16 bytes so byte copy would not be due to LEN). Without a packed structure, in this case with a M0+ it results in the use of ldmia/stmia-
https://godbolt.org/z/5neP85Tj7


edit- there is also a define in newlib memcpy.c which, when set at lib creation, will select the byte copy mode only. So if the compiler wants to call memcpy it will be using byte mode when using -Os. If the struct is packed then it appears memcpy is always called unless the struct is only a single member (or if the single member is an array, and only a single array element), so would be using byte copy if in -Os because it is calling memcpy. If not packed, then the compiler is happy (in the case of this example anyway) to generate its own copy instructions up to a struct that is 48 bytes in size (padded) via ldmia/stmia.

So if a 17 byte object is being used, and memcpy is then being called, then maybe packed structs are in use (but then would not explain <17 byte being 'fine' in -Os). This example is not your code, probably not your mcu, or your compiler, your library, and so on. It does show there are a lot of variables in play- such as the use of packed structs, or which version of memcpy you end up getting and at what point does the compiler call memcpy instead of doing its own thing, etc. At least in the particular example above it would be a good idea to skip the use of packed structs simply for performance (and probably a good idea in general). I don't use packed structs, but is probably an option in the ide (if used) that could easily be enabled without realizing it (default option, or copy of a project which had it enabled, or turned on for some good reason).

edit again- and another variable in play- which specs you are using and what library then gets used. In the case of using nano specs you only get the byte copy version of memcpy, so if you wanted something better I guess you have to understand what you are getting and why, and figure something out.
« Last Edit: June 29, 2025, 01:42:57 pm by cv007 »
 

Online peter-h

  • Super Contributor
  • ***
  • Posts: 6094
  • Country: gb
  • Doing electronics since the 1960s...
Re: Disabling Compiler Optimization vs Using volatile
« Reply #64 on: July 03, 2025, 05:43:11 pm »
I use -Og exclusively, except for some bits of hardware-tied code which has been extensively scope tested and on which I use -O0 (as an attrib on the specific function only). Obviously I also stay with the same GCC version.

The example above of an ISR decrementing a timer not declared volatile would, IIRC, fail with -Og too.

As I said before developing with say -Og and then releasing with -O3 is poor risk management, because the necessary regression testing is probably impossible on any nontrivial product.

There is another very similar thread on optimisation.
« Last Edit: July 04, 2025, 07:25:26 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Disabling Compiler Optimization vs Using volatile
« Reply #65 on: July 03, 2025, 09:59:04 pm »
Here is another simple example-
https://godbolt.org/z/TzshG55PE

This will work with -Og, but anything else will optimize away the use of ledRate in main. Of course there is a solution to the mistake, but if using -Og all along it will not show up and the led rate changes as you desired. Change optimization and your led blink rate no longer changes like it did for the last 2 months.

This also points out there are two sides of volatile- and the forgotten side in this case is in main, where the compiler sees no need to deal with ledRate as it appears to do nothing so optimizes away from -O1 and on. The compiler cannot see any use of the callback function, in relation to main, so concludes there is no need for this var. The callback function does not write to ledRate, so one can mistakenly think there is no need for volatile as this var is NOT changing anywhere other than in main. Isr's are typically the producer of values, non-isr the consumer of them, but in this case that is reversed.
 

Online peter-h

  • Super Contributor
  • ***
  • Posts: 6094
  • Country: gb
  • Doing electronics since the 1960s...
Re: Disabling Compiler Optimization vs Using volatile
« Reply #66 on: July 04, 2025, 07:31:01 am »
I get the arguments here, and fully agree, but changing from -Og to -O3 is just like rebuilding your half megabyte FLASH image from a GCC compiler to say an IAR compiler and magically expect it to work if it throws up no error messages ;)

It just magically might work but it is a really dumb and risky thing to do.

Of course the purists will say I am talking BS, but they all write perfect code :) Much more importantly they are working for some company and get paid by the hour, and sleep at night, whereas if I send out a product with a bug I will get so much s**t dumped on me that I will wish I never did whatever it was.

Compiler writing has been going sideways for years, with stuff like identification of loop structures and replacing them with memcpy(). The exec time saving is probably zero.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #67 on: July 04, 2025, 08:43:03 am »
Compiler writing has been going sideways for years, with stuff like identification of loop structures and replacing them with memcpy(). The exec time saving is probably zero.

How would you feel about a compiler replacing...

Code: [Select]
while (sz-- > 0) *dst++ = *src++;

... with ...

Code: [Select]
    beqz a2,1f
0:  vsetvli a3, a2, e8,m8
    vle8 v0,(a1)
    vse8 v0,(a0)
    add a0,a0,a3
    add a1,a1,a3
    sub a2,a2,a3
    bnez a2,0b
1:

... instead of ...

Code: [Select]
    beqz a2,1f
0:  lb a4,(a1)
    sb a4,(a0)
    add a0,a0,1
    add a1,a1,1
    sub a2,a2,1
    bnez a2,0b
1:

The first is exactly the entirety of the fastest memcpy implementation on any RISC-V CPU implementing the V extension, copying 256 bytes per loop on the most common SBCs at the moment.

I think you'll agree this is not a hugely difficult or risky transformation for a compiler to do, assuming "restrict" qualifier on the src and dst pointers.

The vsetvli a3, a2 sets a3 equal to either a2 or to the vector register size in bytes is a2 is greater than the register size. It also makes the vle8 and vse8 limit themselves to loading or storing that number of bytes, there is no possibility if overwriting adjacent existing content, or going over a protection boundary and causing a trap (if the original byte by byte code won't).
« Last Edit: July 04, 2025, 08:47:29 am by brucehoult »
 

Online hans

  • Super Contributor
  • ***
  • Posts: 1995
  • Country: 00
Re: Disabling Compiler Optimization vs Using volatile
« Reply #68 on: July 04, 2025, 09:14:44 am »
I run these cases against an array of optimizations I'm most interested in: -O0, g, s, 2 and 3. The graphs I post has left, runtime in cycles, and right, image size in bytes.

Nice setup.

You missed my default, -O aka -O1. I've never included -Og in my own comparisons, but I think -O does do some size/speed optimisations that -Og won't. But I don't have anything against -O2 and take your point about C++ devirtualisation.
Thanks.
Yes, I did spot the accidental omission of O1 too. I will add it later. Running a single sweep of all tests may take a few minutes, but for a (home)server thats otherwise idling all day its no big deal.

I'm also at a point where I may need to include more instruction extensions, like floating point and compressed instructions. I'll have to see how I can plot it in such a way the data is still useful.

Quote
I've sometimes found, especially on RISC-V, that the -Os heuristics aren't all that well tuned -- especially because gcc is NOT aware of the C extension -- that you can surprisingly often end up with fewer instructions but larger code than -O2. But you won't see that effect with rv32i or rv32im.

I've seen this behaviour too without the C extension with RISC-V GCC 13.X as well. The result I showed is from an older compiler version. Not sure why this is exactly though.

Quote
I'm not aware of any commercial microcontrollers that don't have the C extension (even the 10c CH32V003 is rv32ec), so I guess you're using an FPGA soft core? It gives more code size reduction than -Os with less (or zero) speed cost.
Its an emulator I wrote myself in Python which I did as a preparation for becoming familiar with writing a FPGA soft-core. Unfortunately, the soft-core is still a TODO, but the project's utility was quite good so far.

The emulator supports rv32im, recently added a subset of atomic (RTOS work), and also some coprocessor extensions (machine interrupts)

I've some basic peripherals like timers, plus a special emulator peripheral which can interact with the emulator environment.
For example, instrumentation time start/stop, enabling/disabling a full instruction trace for hotpoints in my code, etc.
The emulator has a few debugging features such as simply crashing instruction stream outright on a failed instruction (or infinite loops), which then gives a triggered PC location + C line location (taken from .elf debug symbols). The instruction trace also contains this info, as well as manipulated register values. Tracking down which instruction or line of code caused a null pointer dereference becomes a lot easier.
There is also a "delayed" soft interrupt. Basically you write a delay count to a peripheral register, and it will count down to zero (at CPU clock rate) and fire an interrupt. This way I can sweep a range of cycle counts through function calls that are supposed to be reentrant. Again, super useful for low-level work.

The only thing I'm missing sometimes is GDB stepping through code (sifting through 1GiB traces is not the first thing I would do).. but unit testing higher level code on x86 helps a ton.
Another wishlist thing: more speed. Python is nice in quickly developing tools like this, but it only runs binaries at 50-100kHz. I wrote a basic C++ version that ran at 50MHz, but unfortunately not all these instruction tracing and benchmarking features are included yet.

Quote
The copying in/out of the object type is done via the assignment operator. In -Os, this will typically be fine, but then when I test a 17-byte object I get a bytewise memcpy that is very very slow. Even -Og is fine with this, so -Os shows a massive regression in performance.
Is this simply the result of using packed structures?
I don't think an attribute packed in my code.. but it appears very much like so.
Perhaps GCC decided that a memcpy call is smaller than 5 loads + 5 stores, and that is what it prioritized.
 

Online peter-h

  • Super Contributor
  • ***
  • Posts: 6094
  • Country: gb
  • Doing electronics since the 1960s...
Re: Disabling Compiler Optimization vs Using volatile
« Reply #69 on: July 04, 2025, 02:57:40 pm »
Quote
How would you feel about a compiler replacing...

The real Q is whether it will ever make any measurable difference to the actual product.

In general, if you are having to copy many bytes from A to B then your architecture is probably wrong.

Sometimes one might do it out of laziness. For example I wrote a GPS data processing module which gets streamed NMEA data from a NEO-M9N via SPI, one byte at a time. I had to scan the data for specific strings as it arrived. There are lots of cunning ways to do string searches, including some very cunning recursive (and possibly dangerous) ways, but I am not clever enough and I just shifted the received data buffer left 1 byte (using memmov or memcpy) and searched the first n bytes of the buffer for various strings. On the next byte arriving, I loaded it into the end of the buffer, having shifted the content left 1 byte. It was primitive but obviously works fine. That buffer only needs to be long enough for the longest search pattern, plus whatever following data you might want to pick out.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #70 on: July 04, 2025, 03:11:29 pm »
Quote
How would you feel about a compiler replacing...

The real Q is whether it will ever make any measurable difference to the actual product.

In general, if you are having to copy many bytes from A to B then your architecture is probably wrong.

For large copies you get very little advantage because even the simplest byte by byte loop saturates the memory bandwidth. Where by "large" I mean bigger than your caches -- many KB or even MB.

I did some measurements of the vector version vs GNU libc memcpy on the Allwinner D1 SoC (THead C906 core) back in early 2021. The same core is used today in e.g. the CV1800B in the $5 Milk-V Duo.

For copies between 8 bytes and 128 bytes the vector version is at least 3x faster.

I don't consider that size range to be so unreasonably large as to be indicative of bad architecture.

https://hoult.org/d1_memcpy.txt
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17824
  • Country: fr
Re: Disabling Compiler Optimization vs Using volatile
« Reply #71 on: July 04, 2025, 03:22:53 pm »
Here is another simple example-
https://godbolt.org/z/TzshG55PE

This will work with -Og, but anything else will optimize away the use of ledRate in main. Of course there is a solution to the mistake, but if using -Og all along it will not show up and the led rate changes as you desired. Change optimization and your led blink rate no longer changes like it did for the last 2 months.

This also points out there are two sides of volatile- and the forgotten side in this case is in main, where the compiler sees no need to deal with ledRate as it appears to do nothing so optimizes away from -O1 and on. The compiler cannot see any use of the callback function, in relation to main, so concludes there is no need for this var. The callback function does not write to ledRate, so one can mistakenly think there is no need for volatile as this var is NOT changing anywhere other than in main. Isr's are typically the producer of values, non-isr the consumer of them, but in this case that is reversed.

Yes. There are really 3 (that I can think of) main cases for volatile:

1. A variable is modified in a function that the compiler doesn't see being called. That's typically the case for ISRs / thread functions, which are not explicitely called by any other function in the code.
2. A variable is modified in a function, and used in another, both being explicitely called, but that lie respectively in 2 different compilation units. That's one of the limitations of the "separate compilation" model of C. A C compiler doesn't know what happens outside of the compilation unit it's currently compiling. If the compiler was aware of the whole codebase, thise case would not require a volatile qualifier.
3. All accesses of a given variable must be honored  - that's typically the case not for "normal" variables, but those that point to a specific address in memory (such as a device "register"). For these, all accesses may have a side-effect (for instance, reading a register may also reset its content), so all of them should be honored verbatim.

Many people seem to wish case 3. was default, but they just don't realize how inefficient the generated code would be. The difference in execution time can be very large.
To me, case 2. is really the one that could be alleviated with "smarter" compilers and a slightly different compilation model. (But that's just a wish: not how C is defined.)
Case 1. is trickier, but could also be alleviated IMO with smarter compilers.

Now even with smart compilers, some code could be in a library with no source code or code in a different language and so, determining what is being done with a given variable ("object") in memory is not always possible. So, the simplest approach was just to add this "volatile" keyword and make the programmer responsible. That's the whole idea of C.

One technical issue with this with most existing compilers is that the optimization phase happens *after* any kind of static analysis of the code. In other words, it's too late - optimizers do not emit any warning or report (which I think is a shame).

It can still bite even experienced programmers occasionally. Maybe compilers could emit more useful reports so that programmers could spot potential issues of that kind. Meanwhile, don't hesitate to use static analyzers on your code, which can spot a lot that compilers will just blindly compile.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 4852
  • Country: us
Re: Disabling Compiler Optimization vs Using volatile
« Reply #72 on: July 04, 2025, 05:30:03 pm »
Quote
How would you feel about a compiler replacing...

The real Q is whether it will ever make any measurable difference to the actual product.

In general, if you are having to copy many bytes from A to B then your architecture is probably wrong.

That's just almost categorically wrong.

What's far more common is that people overestimate the value of zero copy architectures.  It's super common for people to spend a lot of effort developing a zero copy system, have a lot of bugs to squash, then find out it makes system performance worse.

And yes, every global profile trace I have seen shows a significant fraction of all CPU cycles are spent running memcpy.  Even small improvements provide real benefits.
 
The following users thanked this post: Siwastaja

Offline gf

  • Super Contributor
  • ***
  • Posts: 1832
  • Country: de
Re: Disabling Compiler Optimization vs Using volatile
« Reply #73 on: July 04, 2025, 05:46:26 pm »
2. A variable is modified in a function, and used in another, both being explicitely called, but that lie respectively in 2 different compilation units. That's one of the limitations of the "separate compilation" model of C. A C compiler doesn't know what happens outside of the compilation unit it's currently compiling. If the compiler was aware of the whole codebase, thise case would not require a volatile qualifier.

If the functions are called from the same thread, you do not need to care whether the functions reside in different compilation units. If the compiler cannot see what a called function does internally, it must assume the worst-case and renounce some load/store optimizations for the caller. There is no need to use volatile to ensure consistent visibility of the modification in both functions.
 
The following users thanked this post: newbrain

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 4852
  • Country: us
Re: Disabling Compiler Optimization vs Using volatile
« Reply #74 on: July 04, 2025, 05:56:46 pm »
2. A variable is modified in a function, and used in another, both being explicitely called, but that lie respectively in 2 different compilation units. That's one of the limitations of the "separate compilation" model of C. A C compiler doesn't know what happens outside of the compilation unit it's currently compiling. If the compiler was aware of the whole codebase, thise case would not require a volatile qualifier.

I'm not sure what you mean here.  Access from separate compilations units doesn't require volatile qualifiers unless some other assumption is also violated (such as your #1). 
 
The following users thanked this post: Siwastaja, newbrain, gf


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->