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

0 Members and 3 Guests are viewing this topic.

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11284
  • Country: fi
Re: Disabling Compiler Optimization vs Using volatile
« Reply #75 on: July 04, 2025, 06:26:21 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.

NooOoOoo, do not use unnecessary volatile qualifier for this. This is perfectly normal C and works by design.

Which is kind of an interesting point in itself - it forces compiler to make assumptions that global state could be modified in function calls, making some optimizations impossible. LTO could be able to solve that, by seeing what is exactly modified and where.

But volatile is definitely not required and misusing it causes even further unnecessary performance penalty.
 
The following users thanked this post: newbrain, gf

Offline hans

  • Super Contributor
  • ***
  • Posts: 1984
  • Country: 00
Re: Disabling Compiler Optimization vs Using volatile
« Reply #76 on: July 04, 2025, 06:35:31 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.

In addition, many algorithms simply make a memory and time tradeoff.

The example peter-h gave is basically trading more memory usage (extra RAM for a shift register, redunant copy of data) or more processor cycles (strcmp for e.g. a circular buffer, which can't simply ptr++ to move forward).

There are many more examples of this in common data structures, look-up tables, database indices, path finding, sorting algorithms, graphics (e.g. mipmaps) etc. etc.

With MCUs gaining more (high-speed) RAM, it only makes sense that some tradeoffs will be made differently over time.
« Last Edit: July 04, 2025, 06:37:27 pm by hans »
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: Disabling Compiler Optimization vs Using volatile
« Reply #77 on: July 04, 2025, 06:58:38 pm »
A related case is when modifying a volatile function from interrupt context atomically, disabling and enabling interrupts:
Code: [Select]
    oldstate = disable_interrupts();
    volatile_var++;
    restore_interrupts(oldstate);
Here, the implementation of disable_interrupts() and restore_interrupts() must include a compiler barrier (like __COMPILER_BARRIER(), or asm volatile ("":::"memory"); in GCC and Clang), so that the compiler does not move the (volatile) accesses across the points where interrupts are enabled/disabled.

You see, if the interrupt enable/disable functions are "pure", not interacting with the volatile variable accesses as far as the compiler can tell, and especially if they do not involve any kind of memory accesses, the compiler may reorder them with respect to the volatile accesses, moving the volatile access outside the interrupt-protected region.

The compiler barrier, as its name suggests, generates no code, and only tells the compiler to not move any reads or writes (any memory accesses) across that barrier.
If you have inner functions that may use circuitous ways to access the variables in ways the compiler may not see, adding a compiler barrier before and after such a function call means you don't need to mark the variables accessed in both the outer and inner functions as volatile.  Technically, a function call should be a compiler barrier, so explicitly adding the barriers before and after such calls should not do any harm.  I've only seen this happen when I have weak-linkage do-nothing inner functions defined in the same scope, replaced/interposed with different functions at link time.  As the weak-linked functions are globally visible, the compiler cannot omit the function calls, but since they are in the same scope, it may assume that because the functions do nothing, the call can be reordered wrt. variable accesses, unless explicit compiler barriers are used.

Correct qualification of variables and the use of compiler barrier is not complicated per se; it is just so deeply technical that it always takes some thinking to get it right.  It's like the proverbial triangular hole, when us human developers are used to round or square holes instead.  Having a good intuitive or explicit model of how different parts of the code interact and what the code is trying to achieve –– and having comments describing the intent of the code and the understanding of the developer, not just describing what the code does –– is absolutely crucial for this.  I personally wish I had learned to write such comments when I learned to write proper code, because I still find it hard to write such useful comments; they take at least the same amount of time as the code itself for me!
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Disabling Compiler Optimization vs Using volatile
« Reply #78 on: July 04, 2025, 07:13:25 pm »
In addition, many algorithms simply make a memory and time tradeoff.

It goes further than that...

“Almost all programming can be viewed as an exercise in caching.” - Terje Mathisen
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 brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #79 on: July 04, 2025, 07:16:00 pm »
As the weak-linked functions are globally visible, the compiler cannot omit the function calls, but since they are in the same scope, it may assume that because the functions do nothing, the call can be reordered wrt. variable accesses, unless explicit compiler barriers are used.

That doesn't sound right to me. And if right then it's awful!

I don't see how a compiler can know that the implementation it sees may be replaced, and at the same time make assumptions about what the runtime function does based on the internals of the implementation it can see -- it should base decisions only on the interface (prototype) of the function.
 

Online tooki

  • Super Contributor
  • ***
  • Posts: 15992
  • Country: ch
Re: Disabling Compiler Optimization vs Using volatile
« Reply #80 on: July 04, 2025, 07:34:42 pm »
C is like COBOL. It continues to be used because teachers (think they are) familiar with it

This is a ridiculously detached-from-reality argument. Maybe in 1980's this was true. C was nearly totally abandoned by the academy already in late 1990's, and actually one of the biggest problems of C is that it is not taught properly anywhere, so most academic literature about it and code examples are seriously outdated.

In 2005, we were taught C++; many other universities used Java as their primary teaching language. Today I'm sure Python is very popular also in teaching.

Come to think of it, I suspect you are right. I haven't had contact with university output since then.

Nonetheless, the rest of that sentence stands and is supported by that observation, viz: C is like COBOL. It continues to be used because teachers (think they are) familiar with it and compilers are ubiquitous - not because it is inherently the best language.
COBOL and C will never go away, and it has nothing to do with higher education. What will keep them around forever is existing code bases.

COBOL was designed for and thus used for business applications, which in practice meant financial institutions (insurance companies and banks, mostly). And while financial institutions are certainly happy to migrate their front-end software and middleware to whatever the latest programming language trend may be, they are notoriously (and understandably!!) EXTRAORDINARILY cautious when it comes to modifying the core bookkeeping software that actually runs the numbers. They aren’t going to take on the risk of rewriting their known-good COBOL software in C/C#/Java/Python/whatever comes next, because whatever advantages it might bring simply are not worth the risk of fucking something up. Financial institutions pay IBM the really big money for mainframes that can still run OS/360 so that the core bookkeeping software runs just as it always has. And they train new programmers in COBOL so they can modify the existing COBOL software (after the whole stack of approvals and reviews has been collected) rather than do a rewrite in C or whatever.

Similarly, there is just tons and tons of C software out there, and the risk of rewriting it in another language just isn’t worth it.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Disabling Compiler Optimization vs Using volatile
« Reply #81 on: July 04, 2025, 08:31:09 pm »
C is like COBOL. It continues to be used because teachers (think they are) familiar with it

This is a ridiculously detached-from-reality argument. Maybe in 1980's this was true. C was nearly totally abandoned by the academy already in late 1990's, and actually one of the biggest problems of C is that it is not taught properly anywhere, so most academic literature about it and code examples are seriously outdated.

In 2005, we were taught C++; many other universities used Java as their primary teaching language. Today I'm sure Python is very popular also in teaching.

Come to think of it, I suspect you are right. I haven't had contact with university output since then.

Nonetheless, the rest of that sentence stands and is supported by that observation, viz: C is like COBOL. It continues to be used because teachers (think they are) familiar with it and compilers are ubiquitous - not because it is inherently the best language.
COBOL and C will never go away, and it has nothing to do with higher education. What will keep them around forever is existing code bases.

COBOL was designed for and thus used for business applications, which in practice meant financial institutions (insurance companies and banks, mostly). And while financial institutions are certainly happy to migrate their front-end software and middleware to whatever the latest programming language trend may be, they are notoriously (and understandably!!) EXTRAORDINARILY cautious when it comes to modifying the core bookkeeping software that actually runs the numbers. They aren’t going to take on the risk of rewriting their known-good COBOL software in C/C#/Java/Python/whatever comes next, because whatever advantages it might bring simply are not worth the risk of fucking something up. Financial institutions pay IBM the really big money for mainframes that can still run OS/360 so that the core bookkeeping software runs just as it always has. And they train new programmers in COBOL so they can modify the existing COBOL software (after the whole stack of approvals and reviews has been collected) rather than do a rewrite in C or whatever.

Similarly, there is just tons and tons of C software out there, and the risk of rewriting it in another language just isn’t worth it.

We agree completely. I'll add that two other reasons for not replacing existing COBOL code is lack of engineers and the requirement to duplicate functionality exactly, including "errors" and where there is no design specification.

But that wasn't the point. COBOL is not the future for all those reasons plus we have learned to create better languages in the 65 years since COBOL was created. And exactly the same is true for the 45 year old language, C.
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 Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: Disabling Compiler Optimization vs Using volatile
« Reply #82 on: July 05, 2025, 12:47:09 am »
As the weak-linked functions are globally visible, the compiler cannot omit the function calls, but since they are in the same scope, it may assume that because the functions do nothing, the call can be reordered wrt. variable accesses, unless explicit compiler barriers are used.
That doesn't sound right to me. And if right then it's awful!
Here, the "may" refers to practical reality, and not that it is allowed by the C standard.
If a compiler assumes the variable access/caching can be reordered wrt. to such a function call, I do consider it an optimization bug.

I did check a few cases at godbolt.org, and could not reproduce the bad behaviour with GCC, Clang, or ICC on x86-64, rv32gc, or rv64gc.  They always do the proper variable accesses before the function call, sometimes leaving the values in registers doing any arithmetic with the registers after the function call, which is perfectly okay.

So, I cannot be certain if I recall correctly and have seen it happen, or just recall incorrectly.

Also, while GCC, Clang, and ICC do, technically the C compiler doesn't know that the runtime functions can be replaced because that (and symbol weakness) is outside the scope of the C standard, and is just a "linker trick".

(One technically can modify the symbol linkage directly in the ELF object file, for example via objcopy --globalize-symbol=symbolname, but that is dangerous because the compiler can make assumptions about local visibility objects (static) it cannot for global visibility objects, and will optimize code differently.)



The practical effect of what I described is that if one uses GCC/Clang/ICC extended inline assembly to implement the disable/restore interrupt functions, ie.
Code: [Select]
static inline UINT disable_interrupts(void) {
    UINT oldmask;
    asm volatile ( "..ARCH-SPECIFIC-ASSEMBLY.."
                 : "..REGISTERSPEC.." (oldmask)
                 : /* no inputs */
                 : "memory" );
    return oldmask;
}

static inline void restore_interrupts(UINT mask) {
    asm volatile ( "..ARCH-SPECIFIC-ASSEMBLY.."
                 : /* no outputs */
                 : "..REGISTERSPEC.." (mask)
                 : "memory" );
}
the "memory" extended asm clobber is essential, as it turns the inline assembly statement (often just one or two instructions) into a compiler barrier also.
Leave it out at your own peril.  With it included, the behaviour is what one would expect, and generates no unnecessary or extra code, and does not harm desired optimizations at all.

With other C compilers, something like a __COMPILER_BARRIER() macro (specific to that compiler!) must be added into the function bodies before and after accessing the interrupt mask, to ensure correct ordering with respect to all other operations.

One discovers these kinds of details not via debugging per se, but by carefully observing the actual generated assembly code, and comparing that to the developer requirements and expectations.  Fortunately, reading assembly is much easier than writing fast/efficient assembly, so I do consider it a necessary skill for any low-level/embedded C developer.
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1832
  • Country: de
Re: Disabling Compiler Optimization vs Using volatile
« Reply #83 on: July 05, 2025, 09:20:54 am »
the "memory" extended asm clobber is essential, as it turns the inline assembly statement (often just one or two instructions) into a compiler barrier also.

I'm not sure it's essential for your example. A function call implies two sequence points, and volatile memory access is not re-ordered across sequence points.

Quote
§1.9.17 "When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function."

So I think your example

Code: [Select]
    oldstate = disable_interrupts();
    volatile_var++;
    restore_interrupts(oldstate);

should be sequenced correctly even without explicit barrier, provied that disable_interrupts() an restore_interrupts() are functions (not macros!), and volatile_var is declared volatile.


Quote
Technically, a function call should be a compiler barrier...

My understanding ist that a function call is not a compiler barrier, but de facto, calling a non-inlined external function has to act like a compiler barrier with regard to all C objects which are visible by the caller and which may be visible by the called function (including all C objects whose address might have "escaped" indirectly to the called function). In doubt, if it cannot prove the opposite, the compiler must assume that an object is visible by the callee (and in many cases it won't be able to disprove visibility).

If a function call is inlined then the callee becomes part of the caller's control flow graph and the call is no longer an implied barrier with regard to any non-volatile object. The inlined function call still adds two sequence points to the control flow graph, though.

I don't know if common compilers really "look into" non-inlined global functions defined in the same compilation unit as the caller, in order to aid optimization of the caller. At least for weak functions, they must not. Naturally, weak functions also don't qualify for inlining.
« Last Edit: July 05, 2025, 09:23:32 am by gf »
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17809
  • Country: fr
Re: Disabling Compiler Optimization vs Using volatile
« Reply #84 on: July 05, 2025, 06:59:53 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.

NooOoOoo, do not use unnecessary volatile qualifier for this. This is perfectly normal C and works by design.

Which is kind of an interesting point in itself - it forces compiler to make assumptions that global state could be modified in function calls, making some optimizations impossible. LTO could be able to solve that, by seeing what is exactly modified and where.

But volatile is definitely not required and misusing it causes even further unnecessary performance penalty.

Well, that's right. The cases I had in mind for 2. eventually circle back to my point 1.

As an illustration, one can play with this "minimal" example: https://godbolt.org/z/6Tozej3h4
Uncomment the call to Bar() in Foo() and see the difference.
Of course, this minimal example makes it rather obvious. There are lots of real-life cases where things are not so obvious.
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 6062
  • Country: gb
  • Doing electronics since the 1960s...
Re: Disabling Compiler Optimization vs Using volatile
« Reply #85 on: July 05, 2025, 07:21:24 pm »
Surely the time when the over-use of volatile will cause a performance hit is when accessing peripherals (accessing which generates some large number of wait states) but all those I/O defitions are volatile anyway.
« Last Edit: July 06, 2025, 12:39:13 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: Disabling Compiler Optimization vs Using volatile
« Reply #86 on: July 05, 2025, 10:34:24 pm »
the "memory" extended asm clobber is essential, as it turns the inline assembly statement (often just one or two instructions) into a compiler barrier also.

I'm not sure it's essential for your example. A function call implies two sequence points, and volatile memory access is not re-ordered across sequence points.
You're right, and I was wrong.  :palm:

I don't know what I was thinking there, really.  I should've actually verified first that I recall the details right!
(I did some more godbolt tests, and I cannot replicate any of the scenarios of volatile + compiler barrier I was thinking of.)   

Teaches me to check before I blab from memory!  Apologies if my assertions scared anyone.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4660
  • Country: us
Re: Disabling Compiler Optimization vs Using volatile
« Reply #87 on: July 06, 2025, 12:37:06 am »
Quote
Surely the time when the user-use of volatile will cause a performance hit is when accessing peripherals (accessing which generates some large number of wait states) but all those I/O defitions are volatile anyway.
Accessing memory is slower than accessing registers.
Accessing peripherals is not always slower than accessing memory (for instance, CM0+ may have single-cycle IOBUS, while RAM isn't necessarily single-cycle.)

Consider this "not unusual" clock tick ISR:
Code: [Select]
volatile uint32_t seconds;
volatile uint32_t ticks;
void RTC_handler() {
  ticks += 1;
  if (ticks >= TICKSPERSECOND) {
    seconds += 1;
    ticks -= TICKSPERSECOND;
  }
}
How many times, worst case, does it load or store "ticks" (and for no good reason.)

 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #88 on: July 06, 2025, 01:02:46 am »
Quote
Surely the time when the user-use of volatile will cause a performance hit is when accessing peripherals (accessing which generates some large number of wait states) but all those I/O defitions are volatile anyway.
Accessing memory is slower than accessing registers.
Accessing peripherals is not always slower than accessing memory (for instance, CM0+ may have single-cycle IOBUS, while RAM isn't necessarily single-cycle.)

Consider this "not unusual" clock tick ISR:
Code: [Select]
volatile uint32_t seconds;
volatile uint32_t ticks;
void RTC_handler() {
  ticks += 1;
  if (ticks >= TICKSPERSECOND) {
    seconds += 1;
    ticks -= TICKSPERSECOND;
  }
}
How many times, worst case, does it load or store "ticks" (and for no good reason.)

This is an interesting case and there are two efficient solutions:

1) load ticks into a local variable at the start of the ISR and write it back at the end. This is what I'd just automatically do myself for any function that repeatedly uses a global variable or array or struct member. I know compilers can CSE it, but I like to be explicit.

2) it is completely valid under the C standard to declare ticks without volatile in the module with the interrupt handler, allowing the compiler to load and store it only once in the ISR, but with volatile in the header file used by the main program to ensure a loop reading it repeatedly will see when it changes. The two declarations are compatible, the volatile does not affect linkage, they are the same variable. All that volatile affects is local code generation.  This is a less intrusive change, but is probably too subtle for most maintainers of the code, and it has the unfortunate effect that you can't #include the header in the implementation module (at least without other preprocessor trickery) as the compiler will complain about the redefinition.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11284
  • Country: fi
Re: Disabling Compiler Optimization vs Using volatile
« Reply #89 on: July 06, 2025, 06:30:02 am »
2) it is completely valid under the C standard to declare ticks without volatile in the module with the interrupt handler, allowing the compiler to load and store it only once in the ISR, but with volatile in the header file used by the main program to ensure a loop reading it repeatedly will see when it changes. The two declarations are compatible, the volatile does not affect linkage, they are the same variable. All that volatile affects is local code generation.  This is a less intrusive change, but is probably too subtle for most maintainers of the code, and it has the unfortunate effect that you can't #include the header in the implementation module (at least without other preprocessor trickery) as the compiler will complain about the redefinition.

Opinions about adding volatile qualifier by type casting?

It often happens that I could not care less when the variable is exactly written within the ISR, as long as it is eventually written in the ISR before it returns. Which is of course what would be guaranteed to happen, since the ISR would be a non-static-qualified function, writing to a non-static qualified variable.

Then it should work to only apply the volatile qualifier to any loop which polls the variable. Right?

I admit I have never tested that.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4660
  • Country: us
Re: Disabling Compiler Optimization vs Using volatile
« Reply #90 on: July 06, 2025, 09:41:43 am »
Quote
it is completely valid under the C standard to declare ticks without volatile in the module with the interrupt handler, allowing the compiler to load and store it only once in the ISR, but with volatile in the header file used by the main program
I didn't know that!  It'd make me very nervous, though, worrying about the isr-handling declaration getting out-of-sync with other declarations...
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Disabling Compiler Optimization vs Using volatile
« Reply #91 on: July 06, 2025, 10:16:19 am »
Quote
Consider this "not unusual" clock tick ISR:
Code: [Select]
volatile uint32_t seconds;
volatile uint32_t ticks;
void RTC_handler() {
  ticks += 1;
  if (ticks >= TICKSPERSECOND) {
    seconds += 1;
    ticks -= TICKSPERSECOND;
  }
}
How many times, worst case, does it load or store "ticks" (and for no good reason.)

Assuming this is an mcu with multiple irq levels such as a cortex-m (which _handler suggests), and assuming this example is complete and not just an example to point out volatile use, I would be more worried about a user of them having no guarantee they belong to the same second. The need for interrupt protection does not go away when inside an isr, at least for mcu's with multiple irq levels.

edit- this would also be an example where allowing anyone to access these vars is not a great idea as the user of them would also need to make sure they have a pair that belongs to the same second. Interrupt protecting the write in the isr takes care of the writing, but the reading of the two vars also needs special care either by also using interrupt protection or by reading seconds, ticks, then reading seconds again to see if it changed (no change means seconds and ticks belong to the same second). So, instead of making a user deal with that, you would provide a function for all to use which takes care of those details and returns the 'time' (struct), also making the vars static so not accessible to others.
« Last Edit: July 06, 2025, 11:41:50 am by cv007 »
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #92 on: July 06, 2025, 11:56:29 am »
Assuming this is an mcu with multiple irq levels such as a cortex-m (which _handler suggests), and assuming this example is complete and not just an example to point out volatile use, I would be more worried about a user of them having no guarantee they belong to the same second. The need for interrupt protection does not go away when inside an isr, at least for mcu's with multiple irq levels.

I would consider it very poor system design to have the handler for a high priority interrupt depending on internal consistency of values maintained by a low priority interrupt.

If you're ok with having that higher priority interrupt wait while stuff inside a SEI/CLI pair (or whatever they are on your CPU) happens then you should also be ok with having have the ticks handler run at a higher priority in the first place -- especially on machines such as Cortex-M that chain interrupts so that the register save/restore happens only once, not between each handler.
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1061
Re: Disabling Compiler Optimization vs Using volatile
« Reply #93 on: July 06, 2025, 12:44:37 pm »
Quote
I would consider it very poor system design to have the handler for a high priority interrupt depending on internal consistency of values maintained by a low priority interrupt.
I guess if you want to keep track of 16 priority levels, and have them fixed so they never can be adjusted, then I guess you can figure it all out ahead of time. As soon as you start making adjustments to priorities you will have to revisit all those previous decisions you made, and by then you will have long forgotten about the details that were inside of the isr. So I think it would be a good design to not make assumptions about priority levels.

If my isr was set to the highest priority, for today anyway, it would be much simpler to just assume it can be interrupted and do the protection for something that may need protection whether in the current priority it is needed or not. That protection, if not needed, will not harm anyone and the day I start adjusting priorities I will not have to revisit the decision about these priority dependent things. I can easily live with the dozen 3 or so extra cpu cycles when the interrupt protection was not really needed such as already being in the highest priority, or disabling irq's for those ~12 cpu cycles when there were no other higher priority irq's that were actually interested in my vars. For something like 'time' in the example, it would be safe to assume anyone will be wanting to use the time var(s).

« Last Edit: July 06, 2025, 01:59:27 pm by cv007 »
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: Disabling Compiler Optimization vs Using volatile
« Reply #94 on: July 06, 2025, 02:21:56 pm »
Quote
I would consider it very poor system design to have the handler for a high priority interrupt depending on internal consistency of values maintained by a low priority interrupt.
I guess if you want to keep track of 16 priority levels, and have them fixed so they never can be adjusted, then I guess you can figure it all out ahead of time. As soon as you start making adjustments to priorities you will have to revisit all those previous decisions you made, and by then you will have long forgotten about the details that were inside of the isr. So I think it would be a good design to not make assumptions about priority levels.

If my isr was set to the highest priority, for today anyway, it would be much simpler to just assume it can be interrupted and do the protection for something that may need protection whether in the current priority it is needed or not.

My bad.

If you don't have a carefully figured out reason to make things different priorities, and know exactly which ones should be higher and which lower, with all the mental work that involves, then just make them all the same priority and then ISRs will never interrupt each other at all, but automatically always wait until the previous one is finished, since on any sensible ISA entering an interrupt at a given priority will automatically prevent any ISRs for the same or lower priority from firing until either the ISR returns, or else explicitly reenables interrupts at that level.
 
The following users thanked this post: Siwastaja

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Disabling Compiler Optimization vs Using volatile
« Reply #95 on: July 06, 2025, 03:24:03 pm »
Quote
I would consider it very poor system design to have the handler for a high priority interrupt depending on internal consistency of values maintained by a low priority interrupt.
I guess if you want to keep track of 16 priority levels, and have them fixed so they never can be adjusted, then I guess you can figure it all out ahead of time. As soon as you start making adjustments to priorities you will have to revisit all those previous decisions you made, and by then you will have long forgotten about the details that were inside of the isr. So I think it would be a good design to not make assumptions about priority levels.

My attitude is there should be only three priorities: normal processing, (short) ISR, and panic shutdown.

If absolutely necessary, have some software decide what fragment to process next - but do that at the normal priority.

One alternative is for software to jiggle the priorities randomly, to attempt to break dead/livelocks. Apparently WindowsNT does that; I hope it wouldn't be beneficial in an embedded system!
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 ejeffrey

  • Super Contributor
  • ***
  • Posts: 4849
  • Country: us
Re: Disabling Compiler Optimization vs Using volatile
« Reply #96 on: July 06, 2025, 03:42:48 pm »
Opinions about adding volatile qualifier by type casting?

It often happens that I could not care less when the variable is exactly written within the ISR, as long as it is eventually written in the ISR before it returns. Which is of course what would be guaranteed to happen, since the ISR would be a non-static-qualified function, writing to a non-static qualified variable.

Then it should work to only apply the volatile qualifier to any loop which polls the variable. Right?

I admit I have never tested that.

I think that should work in practice, I'm not sure if it's technically defined.  However, keep in mind that volatile access isn't a compiler barrier for non-volatile access.  So even without casting, if the isr only writes a single volatile variable the compiler is free to reorder anything it wants.
 

Online ejeffrey

  • Super Contributor
  • ***
  • Posts: 4849
  • Country: us
Re: Disabling Compiler Optimization vs Using volatile
« Reply #97 on: July 06, 2025, 04:08:22 pm »
If my isr was set to the highest priority, for today anyway, it would be much simpler to just assume it can be interrupted and do the protection for something that may need protection whether in the current priority it is needed or not. That protection, if not needed, will not harm anyone and the day I start adjusting priorities I will not have to revisit the decision about these priority dependent things.

That doesn't make any sense to me.  If you some day decide that a specific ISR needs a higher priority, presumably that means you explicitly don't want it blocked by a low priority interrupt.  Disabling interrupts in the low priority interrupt largely defeats the purpose of having those priorities.  Which is fine: people seem to often over think interrupt priorities when they don't really need them.  But then just set everything to the same priority.

Unless your system is *extremely* complicated, evaluating which variables are read and which are potentially modified at eqch nesting level should be pretty easy.  You should just repeat that each time you propose changing priorities to ensure you don't have a high priority interrupt depending on a low priority one.  If you paint yourself into a corner and absolutely must do that, it's probably more appropriate to block a single interrupt, rather than globally disable them.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Disabling Compiler Optimization vs Using volatile
« Reply #98 on: July 06, 2025, 05:50:28 pm »
However, keep in mind that volatile access isn't a compiler barrier for non-volatile access.  So even without casting, if the isr only writes a single volatile variable the compiler is free to reorder anything it wants.

That kind of subtlety is missed by too many people, even nominally experienced people.

"volatile" is to C as "glory" is to Humpty Dumpty. https://libquotes.com/lewis-carroll/quote/lbu9j1c
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 #99 on: July 06, 2025, 06:34:07 pm »
Quote
You should just repeat that each time you propose changing priorities to ensure you don't have a high priority interrupt depending on a low priority one
That sounds good in theory, and if there is some formal process you are required to go through to make proposals/changes, then I guess it would be taken care of.

I see it more along these lines- a 'time' isr that is updating your system time is created which of course has an isr which updates var(s), something like the systick timer. You give it the highest priority since its small, and was your only reason for using the high priority since calling the time function which makes use of the vars will account for any missed irq (as timer keeps running no matter what). So, you think about it and conclude since the highest priority is being used there is no need to protect updating the vars inside the isr. All good.

Then, you start thinking about the priorities available, or in the case of an M0 think about the lack of them comparatively speaking, and start thinking that there are more important irqs that should get serviced before others, and eventually after you have forgotten about the lack of protection in that systick isr it gets demoted to a lower priority because it will be perfectly fine in any priority. Sure, you may catch it at the time priorities are changed, but maybe not. The problems that would show up if missed are not going to be obvious, or typically very frequent so will most likely conclude the changes were good as nothing broke. A week or a month later...

I'm not sure what the downside is in allowing for different priorities to be used, without the need to change or look over the code each time. Different priorities were already accounted for when it was created, and the cost was minimal in terms of code and cpu cycles. Even if you wanted to use a single priority level to make your life simpler, you may still reach a point where some of them are taking longer than you would like so will want to give one or more a chance to run while this happens. Since your code already does not depend on priorities, you simply change them as you see fit. Any problems you have by doing so will not be because you have not taken into account any needed interrupt protection, because its already in place.

 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->