Author Topic: [ARM] optimization and inline functions  (Read 20235 times)

0 Members and 1 Guest are viewing this topic.

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
[ARM] optimization and inline functions
« on: January 18, 2022, 09:36:27 am »
I'm playing. So I wrote a test project for a SAMC. I am toggling a port pin in a loop - the hello world project.

So  on a 4MHz clock with a static inline function:
with no optimization I get a symmetrical 50% duty output at 45kHz,
With optimization 1 I get 666kHz 50% duty output
with optimization 2 I get 1MHz but now it's a 25% duty
With optimization 3 I get the same

With a non inline-d function:
with no optimization I get a symmetrical 50% duty output at 45kHz,
With optimization 1 I get 111kHz 50% duty output
with optimization 2 I get 111kHz 50% duty output
With optimization 3 I get 111kHz 50% duty output

So actually forcing the inlining makes the biggest gain, but surely if the compiler is optimizing hard it is looking at size of code, execution time and time to call the function and it would inline automatically.

I'm a little confused about the 25% duty, the instructions to go low and high are identical, it's like the instructions are prefetched in two's into some sort of father cache? it's only an M0+
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 638
  • Country: be
Re: [ARM] optimization and inline functions
« Reply #1 on: January 18, 2022, 10:48:12 am »
Would you share the generated assembly code?
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: [ARM] optimization and inline functions
« Reply #2 on: January 18, 2022, 10:56:51 am »
le confused about the 25% duty, the instructions to go low and high are identical
Including the loop jump?

If you use something like
    while (keep_generating) {
        GPIOn_PSOR = 1 << GPIOn_BIT;  /* Set output high */
        GPIOn_PCOR = 1 << GPIOn_BIT;  /* Set output low */
    }
then the pin stays high for the duration of the single instruction, but low for the duration of the loop jump (at least one clock cycle on Cortex-M0+) and whatever the keep_generating test takes, if it is not always true.

If your code is
    while (pulses-->0) {
        GPIOn_PSOR = 1 << GPIOn_BIT;  /* Set output high */
        GPIOn_PCOR = 1 << GPIOn_BIT;  /* Set output low */
    }
then in the optimized Thumb code for Cortex-M0+, the output would be high for one clock cycle, but low for three (one for the assignment/store, one for decrementing the pulse count in a register variable, and one for the branch), giving a 25% duty cycle.

The tightest GPIO toggle loop with 50% duty without unrolling uses the GPIO TOGGLE register (GPIOn_PTOR in NXP KL26 family Cortex-M0+ controllers), using
    while (keep_generating) {
        GPIOn_PTOR = 1 << GPIOn_BIT;
    }
which, if keep_generating always evaluates to True, and GPIOn_BIT is 0, compiles to the following Thumb assembly on Cortex-M0+:
        ldr     rA, .address
        movs    rB, 1
    .loop:
        str     rB, [rA]
        b       .loop
    .address:
        .long   2684354628
where rA and rB are some general registers, often r0 and r1.

The TOGGLE register is useful in that the bit mask on the right side can toggle any set of bits in the same GPIO port, and if they are initialized to opposite output states, they will also toggle in opposite states.  For more complex sequences, you can use an array of toggle masks, say
Code: [Select]
void toggle32(volatile uint32_t *reg, uint32_t mask[], uint32_t masks, uint32_t pulses)
{
    while (pulses-->0) {
        uint32_t *const  ends = mask + masks;
        uint32_t        *curr = mask;

        while (curr < ends) {
            *reg = *(curr++);
        }
    }
}
where masks is usually even, and (mask[0] ^ mask[1] ^ ... ^ mask[masks-2] ^ mask[masks-1]) == 0, i.e. the full toggle sequence returns the GPIO outputs to their original states.  That sequence is then repeated pulses times, but note that there is a "delay" (due to the outer loop and inner loop setup) after the last toggle.  If you have the memory, and especially if you can limit to a single byte within the toggle register (at least NXP KL26 allows 8-bit, 16-bit and 32-bit accesses to the register), you can do pretty complicated digital waveform patterns on the output at a pretty darned high rate.
« Last Edit: January 18, 2022, 11:01:54 am by Nominal Animal »
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [ARM] optimization and inline functions
« Reply #3 on: January 18, 2022, 11:30:39 am »
I am using toggle, so it's the same instructions all round, I guess the initial state is 0 so it looks like it is faster to flip low to high than it is to flip high to low.
 

Offline MK14

  • Super Contributor
  • ***
  • Posts: 5385
  • Country: gb
Re: [ARM] optimization and inline functions
« Reply #4 on: January 18, 2022, 01:18:09 pm »
The peripheral clock, might be running slower than the main cpu clock. So because of aliasing effects, it can do strange things. I'm not sure of the part number (MCU), so I can't check the datasheet, to see a possible divider ratio.
« Last Edit: January 18, 2022, 01:23:04 pm by MK14 »
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: [ARM] optimization and inline functions
« Reply #5 on: January 18, 2022, 01:56:08 pm »
I am using toggle, so it's the same instructions all round, I guess the initial state is 0 so it looks like it is faster to flip low to high than it is to flip high to low.
If you have an oscilloscope, it would be very interesting to see the result if you initialize two outputs (say, consecutive bits in the same GPIO register) to different states, and then toggle both of them, using either one as the trigger.  (Just write a two-bit mask, instead of an one-bit mask, to the toggle register.)

I would expect them to change in tandem, and the asymmetric delay be explained by the assembly code (that you can see with the -S flag, or by using godbolt.org and selecting the correct compiler and setting the compiler options).  However, if the toggles indeed are asymmetric, it would be very useful and interesting to see exactly how; whether it is just a pin output driving issue (going high being slower than going low), or something else.

(In case anyone reading this is not aware, each GPIO port on Atmel/Microchip SAMC (and NXP KL26 family) has four registers that define the output pin states in that port: the data register, a set register, a clear register, and a toggle register.  Only the data register is read-write, the set/clear/toggle are write-only.  Writing a value to the set/clear/toggle register will set/clear/toggle the bits in the output state register that were set in the value.  So, writing 0x00000001 to the clear register will set bit 0 in the data register to zero, but leave all other bits unset.  The register names and the macros/variables to access them do vary a bit, but the logic is the same.)

Which reminds me:

If this is a SAMC20 or SAMC21, make sure you access the toggle register using the IOBUS; the address range for the port registers via the IOBUS being between 0x60000000 and 0x60000200.  Via the slower AHB-AHP bridge B, at 0x41000000 to 0x41000200, the latency depends on the AHP clock 0.  Thus, you'll want to use
    #define  PORTA_TOGGLE  (*(volatile uint32_t *)0x6000021C)
    #define  PORTB_TOGGLE  (*(volatile uint32_t *)0x6000029C)
    #define  PORTC_TOGGLE  (*(volatile uint32_t *)0x6000031C)
for 32-bit accesses to the toggle register, to make sure you use the single-cycle IOBUS, and not the slower AHB-AHP bridge B:
Code: [Select]
    while (counter-->0) {
        *(volatile uint32_t *)0x6000021C = (1<<3) | (1<<6);  /* Toggle PA03 and PA06 */
    }
 
The following users thanked this post: MK14

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3567
  • Country: ca
Re: [ARM] optimization and inline functions
« Reply #6 on: January 18, 2022, 03:05:43 pm »
So  on a 4MHz clock with a static inline function:
with optimization 2 I get 1MHz but now it's a 25% duty

1 clock high, 3 clocks low. It is probably doing some sort of loop unrolling. You need to post the disassembler.
 
The following users thanked this post: MK14

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [ARM] optimization and inline functions
« Reply #7 on: January 18, 2022, 03:26:41 pm »
The peripheral clock, might be running slower than the main cpu clock. So because of aliasing effects, it can do strange things. I'm not sure of the part number (MCU), so I can't check the datasheet, to see a possible divider ratio.

it's a bog standard SAMC start with default clock, I think everything gets 4MHz
 
The following users thanked this post: MK14

Offline MK14

  • Super Contributor
  • ***
  • Posts: 5385
  • Country: gb
Re: [ARM] optimization and inline functions
« Reply #8 on: January 18, 2022, 03:47:29 pm »
it's a bog standard SAMC start with default clock, I think everything gets 4MHz

Then it's a 1 to 1 clock ratio (no clock prescalers), assuming you're not using the slower buses, mentioned by Nominal Animal, in an earlier post. So, it should be fine, as regards port clock frequency.
I didn't mention seeing the assembly listing, as others had already done that.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17822
  • Country: fr
Re: [ARM] optimization and inline functions
« Reply #9 on: January 18, 2022, 05:52:31 pm »
So, someone is apparently still trying to predict execution time on a modern ARM MCU down to the cycle and still wonders why it doesn't work.  :popcorn:
 
The following users thanked this post: hans, grumpydoc, mikerj

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [ARM] optimization and inline functions
« Reply #10 on: January 18, 2022, 07:38:30 pm »
it's a bog standard SAMC start with default clock, I think everything gets 4MHz

Then it's a 1 to 1 clock ratio (no clock prescalers), assuming you're not using the slower buses, mentioned by Nominal Animal, in an earlier post. So, it should be fine, as regards port clock frequency.
I didn't mention seeing the assembly listing, as others had already done that.

default is that the internal clock source is divided to 4MHz for the entire chip as it's the oscillator that is being prescaled not any of the other stuff down the line.
 
The following users thanked this post: MK14

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [ARM] optimization and inline functions
« Reply #11 on: January 18, 2022, 07:42:38 pm »
So, someone is apparently still trying to predict execution time on a modern ARM MCU down to the cycle and still wonders why it doesn't work.  :popcorn:


actually my biggest realization is how misguided people are who just throw code at a compiler and say: modern compilers are so good that they will deal with it and optimize it. I don't know how the optimization works but it is obviously not good enough to take the single line of code out of a function and inline it as with 256kB of flash and just the few kB at most of code speed optimization is a given.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [ARM] optimization and inline functions
« Reply #12 on: January 18, 2022, 07:56:59 pm »
It's also interesting that I get the 25% duty when I use the inline and 50% with non inline. I sort of assume that given that running at 9x the speedup with in-lining and down to doing it in 2 cycles on average per loop maybe there is caching and starting at low the first two instructions are done fast in one cycle and then it takes another 2 cycles for the fetching to catch up. I'm not relying on these timings for anything, it's just interesting to see how the inlining optimizes beyond what optimization can do.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11318
  • Country: fi
Re: [ARM] optimization and inline functions
« Reply #13 on: January 18, 2022, 08:01:12 pm »
ARM assembly is surprisingly simple, so I suggest you enable generation of assembly files; on GCC, it's -S flag, so instead of
gcc file.c -o file.o, use
gcc file.c -S -o file.s

Then look at the generated code, and you'll learn even more.
 
The following users thanked this post: MK14

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2867
  • Country: us
Re: [ARM] optimization and inline functions
« Reply #14 on: January 18, 2022, 08:43:46 pm »
It's also interesting that I get the 25% duty when I use the inline and 50% with non inline. I sort of assume that given that running at 9x the speedup with in-lining and down to doing it in 2 cycles on average per loop maybe there is caching and starting at low the first two instructions are done fast in one cycle and then it takes another 2 cycles for the fetching to catch up. I'm not relying on these timings for anything, it's just interesting to see how the inlining optimizes beyond what optimization can do.

Well, there's a fair amount of housekeeping that comes with entering a function and then returning.  Registers need to be saved to the stack, and there's a jump to the function entry point, which may cause a pipeline flush as well as a delay in retrieving new instructions from flash depending on the prefetch behavior, flash speed relative to the core, and whether or not the jump is conditional.  If the function is not inlined then the instructions in the function itself must not make any assumptions about the context in which they occur, so that also takes more time.  All of that can easily swamp the additional jump required to close the main loop in a program, which is why you see something much closer to 50% (but almost certainly not exactly 50%) with an inlined function.
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 22435
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: [ARM] optimization and inline functions
« Reply #15 on: January 18, 2022, 08:58:12 pm »
Inlining is something of a non sequitur, anyway.  The compiler treats inline as a recommendation, not a requirement.  On various -O's, inlining is attempted even if you don't specify it, and if it doesn't meet various optimization goals or semantic requirements, will be rejected even if you do.

For example, my typical programs all end up with monstrous main() routines, because all the one-off init and do-this and do-that functions all end up in one big pile.  It's structured that way for my readability and flexibility; the compiler knows to disregard that structure and cram it all together, saving both time and space.  (I forget what all options enable this; it may do it for example within a compilation unit (main.c contains main() which calls foo() and bar() in the same file) but not across files (main() calls baz() from baz.c), on the assumption that those functions may need to be independent for linkage purposes (what if bam.c happens to call foo() as well?).  In any case, -flto (link-time optimization) says to cram them all together if useful, don't worry about leaving references.)

(And by "references", I mean a usable pointer to a function.  An inlined function is stripped of pre/postamble (i.e. saving/restoring registers on the stack, return instruction, etc.), so cannot be called from anywhere at random.  The compiler needs to know if you're dereferencing a function pointer, so that it will either avoid inlining that function at all (more likely -Os), or leave a free copy in addition to the inlined version(s) (more likely -O3).)

That said, it's interesting that, evidently your case isn't inlining on any -O.  You didn't show your code so it's impossible to tell why.  Perhaps you used the set/reset pair Nominal Animal led with, and the compiler sees the duplication and declines to inline it for that reason (it'll seemingly take more time/space to double it up)?  Perhaps the inlining is done at an earlier stage -- before other optimizations have been applied -- and therefore it doesn't realize the true gain of this operation.  (Mind also that GCC optimizes its internal representation, not the final target output.  Which has fairly embarrassing results on something ill-fitting like AVR, but should be pretty good on ARM.)


ARM assembly is surprisingly simple, so I suggest you enable generation of assembly files; on GCC, it's -S flag, so instead of
gcc file.c -o file.o, use
gcc file.c -S -o file.s

Then look at the generated code, and you'll learn even more.

I'm fond of using this for assembly generation:
Code: [Select]
objdump -h -S project_output.elf > Release/project_output.lssI put that in the project post-build options (among other things), and this generates the commented (sometimes) output, along with everything else.  (Commented with the source, that is, if optimization is low, and symbols are preserved.  On -O3 or -Os, it's just the raw listing with only labels (symbols).)

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: [ARM] optimization and inline functions
« Reply #16 on: January 18, 2022, 09:42:21 pm »
.
« Last Edit: August 19, 2022, 05:09:38 pm by emece67 »
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 4662
  • Country: us
Re: [ARM] optimization and inline functions
« Reply #17 on: January 18, 2022, 10:04:42 pm »
Please show us your actual code.

 
The following users thanked this post: mikerj, MK14

Offline hans

  • Super Contributor
  • ***
  • Posts: 1994
  • Country: 00
Re: [ARM] optimization and inline functions
« Reply #18 on: January 18, 2022, 11:09:49 pm »
Don't want to repeat what others have said: but if you really want to dive into this problem and know what a compiler is doing.. post the code.. post the assembly listing. There is even a website for reviewing compiled code with many many GCC/CLang compilers, including crosscompilers: https://godbolt.org/

Here is an example to a simple toggle I/O program (which I assume is similar to what a blinky you've made -- but this is completely assumption based): https://godbolt.org/z/KrzYE3ffx
Code: [Select]
#include <cstdint>

volatile uint32_t* port = reinterpret_cast<uint32_t*>(0xCAFECAFE);

int toggle() {
    while (1) {
        *port = 1;
    }
}

On the Godbolt link you can play around with the compiler setting. The assembly output on O2 is:
Quote
toggle():
        ldr     r3, .L4
        ldr     r2, [r3]
        movs    r3, #1
.L2:
        str     r3, [r2]
        str     r3, [r2]
        b       .L2
.L4:
        .word   .LANCHOR0
port:
        .word   -889271554

Label L2 is the while loop. In the compilers infinite wisdom, it has unrolled the loop twice. You could try compiling for size (-Os in GCC), and see that it then only does 1 store to save on code size. I presume you then get a 50% 667kHz square wave again, as a 1 store (1 cycle)+1 branch(2 cycle), with 2 toggles, results in 6 cycles. 4M/6=667kHz.

I do agree with SiliconWizard though; it gets incrementally harder to predict how CPUs run code these days. Seemingly simple code can even lead to odd behaviour. This story is perhaps a bit off-topic on Simon's program, but something I encountered last week that was also heavily confusing with compiler settings.

I was debugging a timebase. A hardware timer is extended in software with a 64-bit integer value. The live timestamp is calculated with the hardware and software value added together. Example: https://godbolt.org/z/76GGM8qT7

The timer runs at high speed, which is the reason to use 64-bit ints. This code looks quite simple and works... The code waits till multiples of 0x100000 cycles have passed. If 'target' is a multiple of 0x100000, then obviously (N * 0x100000)&0x10000 will always be 0? Not quite.. this code will hang at some point on C-code line 25. If you look in the assembly listing at line 5 and 6, there is a 1 cycle opportunity for register `CNT` to be loaded as 0xFFFF on line 5, which would overflow and fire the IRQ before line 6. Now the variable time is loaded, which was just incremented by 0x10000, and so 0xFFFF has been counted twice. 1 instruction doesn't sound like a big deal. What are the odds?  Well.....

- I could add (one) extra line of debug or computation statement in foo() (e.g. toggle a LED), and the problem would disappear (or reappear).
- I could shift some lines of code in foo() around, and the problem would disappear or reappear.
- I could enable the CPU cache (was a Cortex-m7 chip), and the problem would disappear, mostly.
- I could flush the instruction buffer, and the problem would appear again, mostly.
- I could influence the problem with compiler settings, as well, with varying results.

It's like probing your circuit with your oscilloscope will stop it resonate. The actual fix was to deal with the race condition on variable time.

The morale of my story is to not expect the compiler to be some magic tool that fixes your code or will always make it run fast, or the way you want it to run. In my example, if it would have loaded `time` before CNT, then this problem would not occur. But since volatile is not "magic", and is allowed to be reordered (among other non-volatile values that are manipulated), you can get some annoying bugs. An more aggressive (optimizations) compiler can lead to weird-er behaviour. The worst of all is that from the C-code perspective everything looks like predictable C code (as in: trivial statements), but how it compiles and runs on the hardware varies a lot (especially on the m7 with caches, dual issue, etc.). So best to look at assembly output, and assume the worst will at some point happen.
« Last Edit: January 18, 2022, 11:15:19 pm by hans »
 
The following users thanked this post: MK14

Online westfw

  • Super Contributor
  • ***
  • Posts: 4662
  • Country: us
Re: [ARM] optimization and inline functions
« Reply #19 on: January 19, 2022, 12:57:49 am »
Quote
In the compilers infinite wisdom, it has unrolled the loop twice.
That's a particularly odd choice, IMO...
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 30318
  • Country: nl
    • NCT Developments
Re: [ARM] optimization and inline functions
« Reply #20 on: January 19, 2022, 01:13:04 am »
ARM assembly is surprisingly simple, so I suggest you enable generation of assembly files; on GCC, it's -S flag, so instead of
gcc file.c -o file.o, use
gcc file.c -S -o file.s

Then look at the generated code, and you'll learn even more.
I agree. Only with the actual code, resulting assembly and compiler / version used there is something useful to write towards the question.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3567
  • Country: ca
Re: [ARM] optimization and inline functions
« Reply #21 on: January 19, 2022, 01:30:16 am »
... it gets incrementally harder to predict how CPUs run code these days. Seemingly simple code can even lead to odd behaviour. This story is perhaps a bit off-topic on Simon's program, but something I encountered last week that was also heavily confusing with compiler settings.

I don't think this example has anything to do with C compilers or with modern CPUs. The same thing can happen on very old 8-bit CPUs as well, and even if you write in assembler. Actually, it is more likely to happen on old 8-bitters because timers are shorter.

Moreover, you can get similar effect even without ISR. For example, an 8-bit MCU may have a 16-bit timer consisting of two 8-bit registers - TIMERL and TIMERH. To get the 16-bit value you need to read them both. Since both are controlled by the hardware, the overflow from TIMERL and TIMERH may happen after you read  TIMERL, but before you read TIMERH, so the result will be off by 255.

Most timer modules are aware of this and handle such cases by latching TIMERH every time you read TIMERL, and then giving you the latch every time you read TIMERH. For this to work, you need to read TIMERL before TIMERH, but if you write in C:

Code: [Select]
time = TIMERL | (TIMERH << 8);
where the order of fetching TIMERL and TIMERH is undetermind, you may get garbage. Finding such thing may be difficult. Not only the errors are rare, but the error may go away simple because the compiler changed the reading order after another recompile.

These are very old things. Any embedded programmer must be aware of them, whether old or new.

PS. Sorry, this is probably off topic.
 
The following users thanked this post: hans, newbrain

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3567
  • Country: ca
Re: [ARM] optimization and inline functions
« Reply #22 on: January 19, 2022, 01:35:19 am »
Quote
In the compilers infinite wisdom, it has unrolled the loop twice.
That's a particularly odd choice, IMO...

Makes it much faster at the expense of a single extra command.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [ARM] optimization and inline functions
« Reply #23 on: January 19, 2022, 09:05:26 am »
main.c
Code: [Select]

#include "pinControl.h"

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
makePinOutput(PB9);


    /* Replace with your application code */
    while (1)
    {
pinT(PB9) ;
    }
}

pinControl.h
Code: [Select]
static inline void makePinOutput(uint8_t x)
{
( ( *(volatile uint32_t * )( PORTS_BASE + (x >> 5) * PORTn_offset + PORTn_DIRSET ) ) = 0x01 << (x & PinNumberMask) );
}

static inline void pinT(uint8_t x)
{
( ( *(volatile uint32_t * )( PORTS_BASE + (x >> 5) * PORTn_offset + PORTn_OUTTGL ) ) = 0x01 << (x & PinNumberMask) );
}

From what Tim said it is more about the inlined functions being in header files rather than another c file that this c file is aware of.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18909
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [ARM] optimization and inline functions
« Reply #24 on: January 19, 2022, 09:14:22 am »
If I remove the inlining I get the same result, it's the fact that the code is in a header file that is seen as part as the same compilation unit.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->