Author Topic: GCC compiler optimisation  (Read 102701 times)

0 Members and 2 Guests are viewing this topic.

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #275 on: February 10, 2023, 10:10:04 am »
Are memory-memory DMA transfers interruptible?

I know memory-peripheral ones obviously are - in between bytes moved. But there the DRQ stream has large gaps. In memory-memory you would try to have no gaps.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11253
  • Country: fi
Re: GCC compiler optimisation
« Reply #276 on: February 10, 2023, 10:55:40 am »
Are memory-memory DMA transfers interruptible?

I know memory-peripheral ones obviously are - in between bytes moved. But there the DRQ stream has large gaps. In memory-memory you would try to have no gaps.

Why not RTFM? For example from an STM32H7 reference manual:

"At any time, a DMA transfer can be suspended to be restarted later on or to be definitively
disabled before the end of the DMA transfer.
There are two cases:
Note:
• The stream disables the transfer with no later-on restart from the point where it was
stopped. There is no particular action to do, except to clear the EN bit in the
DMA_SxCR register to disable the stream. The stream may take time to be disabled
(ongoing transfer is completed first)."

It says nothing about memory-to-memory transfers being special, so very likely it respects the EN bit.

But the question is, why would you want to do that?
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1831
  • Country: de
Re: GCC compiler optimisation
« Reply #277 on: February 10, 2023, 11:19:56 am »
The question is rather how much memory bandwidth is consumed by the DMA transfer, and is there enough left for the concurrent duties of the CPU.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #278 on: February 10, 2023, 11:43:02 am »
Quote
why would you want to do that?

If a DMA transfer takes say 100us, that is the interrupt service latency.

Not too bad if a byte is really moved on every clock. A max-size ETH packet works out to 10us. But I am already seeing memcpy() times in that approximate range. Never more than 20us. Can't really correlate them to packet sizes (would have to waggle some other GPIO based on the "length" value) but I don't see DMA as being much faster. For real speedups on LWIP one needs to move to zero-copy and that seems to be a can of worms (even though it is a pretty old idea with LWIP).

Quote
The question is rather how much memory bandwidth is consumed by the DMA transfer, and is there enough left for the concurrent duties of the CPU.

Indeed.

The 32F4 manual doesn't say anything about mem-mem transfers being interruptible. Perhaps burst transfers might be but they must not cross a 1k address boundary, which is useless.

This is interesting because the 1970s Z80-family chips could do that :) It was called "cycle stealing". Presumably you could achieve the same by using a timer to trigger each transfer, but then the entire transfer would obviously be slower. But this would be a way to achieve a slow background transfer process.
« Last Edit: February 10, 2023, 02:23:25 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #279 on: February 10, 2023, 03:42:57 pm »
On the topic of bizzare optimisation...

This

Code: [Select]
__attribute__((optimize("O2")))
void * memcpy_fast (void *__restrict dst0, const void *__restrict src0, size_t len0)
{

char *dst = dst0;
const char *src = src0;
uint32_t *aligned_dst;
const uint32_t *aligned_src;

// If the size is >=4 then do 32 bit moves, until exhausted

if ( len0 >=4 )
{
aligned_dst = (uint32_t*)dst;
aligned_src = (uint32_t*)src;

while (len0 >= 4)
{
*aligned_dst++ = *aligned_src++;
len0 -= 4;
}

dst = (char*)aligned_dst;
src = (char*)aligned_src;
}

// Finish with any single byte moves

while (len0--)
*dst++ = *src++;

return dst0;

}

when stepped through, reveals



One has to laugh, given that "prevailing internet wisdom" has been that only -O3 and above does auto loop identification, and I have



I think some compiler writers need to get a life. They must have spent years doing these dumb stdlib substitutions.
« Last Edit: February 10, 2023, 04:51:25 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #280 on: February 10, 2023, 04:58:25 pm »
If a DMA transfer takes say 100us, that is the interrupt service latency.
Of course they are interruptible. DMA controller negotiates access for each transfer just like any other master. Individual transfers within the burst transfer are atomic.

Quote
The question is rather how much memory bandwidth is consumed by the DMA transfer, and is there enough left for the concurrent duties of the CPU.
Indeed.
You are doing blocking memcpy(). How much concurrent stuff do you expect to happen?

Perhaps burst transfers might be but they must not cross a 1k address boundary, which is useless.
This is a strange logic. Burst transfers are not interrtuptible. The whole point of the burst transfer is to negotiable access once and perform it atomically.

And why is 1K useless? Just split your buffer into 1K chunks and do individual transfer for each. But this is not necessary.

As usual, you are way overthinking all this.
Alex
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 4657
  • Country: us
Re: GCC compiler optimisation
« Reply #281 on: February 10, 2023, 05:14:23 pm »
DMA transfers aren’t usually said to be “interruptible”, but rather “stallable”. Each memory transfer (or perhaps each “burst”) is subject to having to wait before it is granted access to memory.
On a risc platform where the cpu can probably access ram nearly as fast as it can go, this might be more of a problem than elsewhere.  Frequently there are priorities involved (sometimes user settable?)

 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #282 on: February 10, 2023, 05:53:41 pm »
Quote
You are doing blocking memcpy()

Blocking yes but ints are serviced as normal, RTOS can switch tasks, etc.

Can anyone explain why the call to the library memcpy() was there? We had a long thread on this ages ago. I took a lot of care to avoid this happening in my boot block (which has no access to stdlib) and it was done with a combination of -O0 function attribute and that distribute-patterns compiler option, but the latter ought to have been sufficient.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6464
  • Country: nz
Re: GCC compiler optimisation
« Reply #283 on: February 10, 2023, 06:14:26 pm »
Can anyone explain why the call to the library memcpy() was there?

Because the compiler is allowed to replace code such as...

Code: [Select]
while (len0--)
  *dst++ = *src++;

... with memcpy.

And, yes, '-fno-tree-loop-distribute-patterns' should stop it doing that.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #284 on: February 10, 2023, 06:55:58 pm »
On a risc platform where the cpu can probably access ram nearly as fast as it can go, this might be more of a problem than elsewhere.  Frequently there are priorities involved (sometimes user settable?)
In case of STM32F4xx bus matrix uses fixed round-robin arbitration, so all masters that want to get access would be granted it with a fixed priority in turn.  But yes, in many cases priorities and scheduling algorithm are configurable.

And the matrix itself is multi-layer, so multiple masters may get access at the same time as long as they are accessing different slaves. It may not help as much in case of SRAM access, but even here SRAM is split into 3 blocks, which may be accessed independently. So, if you are really deep into optimizing stuff, you can shuffle the buffers around the memory. In practice all this hits diminishing returns real fast. If you have to do this - get a faster CPU.
Alex
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11253
  • Country: fi
Re: GCC compiler optimisation
« Reply #285 on: February 10, 2023, 07:26:46 pm »
If a DMA transfer takes say 100us, that is the interrupt service latency.

Wait, wat?!

Where do you get these ideas? I mean, I have never ever seen someone come up with stuff like that. That would render DMA completely useless.

No. That's not how it works. Bus arbiter gives the CPU memory access cycles. If my memory serves right, for STM32, it's at least every other memory cycle (see the documentation for exacts).
 
The following users thanked this post: SiliconWizard

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #286 on: February 10, 2023, 08:44:15 pm »
Quote
Where do you get these ideas? I mean, I have never ever seen someone come up with stuff like that

As you know, I am stupid, so treat this as yet another opportunity to show me that you are much more clever ;) Seriously, I really like working with people much smarter than I am, because every day is a school day (an English saying, but I am not actually English).

I could not find an answer to whether mem-mem DMA transfers are interruptible in the RM.

Quote
And, yes, '-fno-tree-loop-distribute-patterns' should stop it doing that.

Yes, it should, but for some reason it doesn't.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #287 on: February 10, 2023, 08:47:35 pm »
Yes, it should, but for some reason it doesn't.
-fno-builtin would disable use of any compiler-builtin version of the libc functions. This would disable this optimization, since compiler can no longer rely in memcpy() being there.
Alex
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17801
  • Country: fr
Re: GCC compiler optimisation
« Reply #288 on: February 10, 2023, 08:56:04 pm »
Yes, it should, but for some reason it doesn't.
-fno-builtin would disable use of any compiler-builtin version of the libc functions. This would disable this optimization, since compiler can no longer rely in memcpy() being there.

Not sure why you'd want to do that though.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #289 on: February 10, 2023, 09:09:01 pm »
Not sure why you'd want to do that though.
May be useful in freestanding code, but there is a separate  flag for this anyway.

And on a closer look it is also not that. Even though it seems to work as a side effect, -fno-builtin is meant to disable replacement of the library functions with internal code, so the opposite of what happens here.

In this case some other optimization that recognizes memcpy semantics kicks in. The call is external anyway in this case.

-fno-builtin-memcpy, which should only disable memcpy, does not do anything here. More investigation is required.

If you want to experiment - https://godbolt.org/z/cx1GPxEE1

And -fno-tree-loop-distribute-patterns does work as far as I can see. But I don't think is guaranteed, since this does not actually disable replacement with library calls, it just disables optimizations that can allow replacements.

And upon further reading, -ftree-loop-distribute-patterns is the optimization that does this. So, it should always work.
« Last Edit: February 10, 2023, 09:21:05 pm by ataradov »
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #290 on: February 10, 2023, 09:26:24 pm »
Quote
May be useful in freestanding code, but there is a separate  flag for this anyway.

What flag is that?

That's exactly what I am doing (normally, e.g. a boot loader).

But also I wrote a faster replacement for memcpy, and the compiler recognised the loop and replaced it with a slower memcpy! That's just daft, and it got it wrong!

A -O0 function attribute does seem to work ok in suppressing these substitutions, at the cost of ~ 1/3 more code.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #291 on: February 10, 2023, 09:30:02 pm »
What flag is that?
Literally -ffreestanding and you may also want -fnostdlib

But -fno-tree-loop-distribute-patterns should disable that replacement too. If it does not, I'd investigate that. Something is not right in your setup. Maybe the flag does not actually get to the compiler or something.
Alex
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17801
  • Country: fr
Re: GCC compiler optimisation
« Reply #292 on: February 10, 2023, 09:38:46 pm »
Not sure why you'd want to do that though.
May be useful in freestanding code, but there is a separate  flag for this anyway.

There are two separate things here. While you may want to have no dependency on the std lib at all, which would preclude any call to a memcpy/memset/... , if the compiler's optimizer actually inlines some memcpy-like code without any function call, then technically is it using the std lib or not? And why would you want to prevent the compiler from doing that, while the resulting code would be pretty much the same as directly compiling your hand-written copy, except usually better.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #293 on: February 10, 2023, 10:06:10 pm »
See the example code I posted.

It takes advantage of 25% of moves being fully aligned (src+dest), 50% of moves being partly aligned (src or dest), and (in the 32F4) still works with 25% of moves being fully unaligned.

Whereas the code in Newlib is suboptimal 75% of the time.

In the specific case of memcpy this is more complicated because while the byte-only memcpy is from Newlib, the full memcpy in one Newlib source I found
https://github.com/eblot/newlib/blob/master/newlib/libc/string/memcpy.c
does have the fuller source which does the long optimisation. My faster memcpy was based on that.

My current settings are below and thus my byte-only memcpy suggests that ST compiled that library for minimum size, but I think, from other stuff I found, only Newlib-nano should be doing that, but my settings here are obviously not going to change a precompiled library.



The flag is being used on the command line



Quote
And why would you want to prevent the compiler from doing that

In this case, the compiler isn't smart enough to know what runs faster on that particular CPU. I can understand why it called a sub-optimal memcpy but can I assume that it would replace am optimised memcpy with an even more optimised one? I don't think so.
« Last Edit: February 10, 2023, 10:08:31 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #294 on: February 10, 2023, 10:45:53 pm »
Library memcpy() is whatever it is. But if you just write your version in plain c and let compiler substitute internal implementation, you will get the same exact optimized version.

Here is test code:
Code: [Select]
void *memcpy_my(void *restrict dest, const void *restrict src, int len)
{
    char *dp = (char *restrict)dest;
    const char *sp = (const char *restrict)src;
    while( len-- )
    {
        *dp++ = *sp++;
    }
    return dest;
}

If you compiler this with -Og, then it would result in a simple byte by byte loop. But if you use real optimization levels, then it generates the same code that checks alignment and does whatever is necessary to take care of the edge conditions:

arm-none-eabi-gcc -O3 -mthumb -mcpu=cortex-m4 -fno-tree-loop-distribute-patterns -mno-unaligned-access  -c test.c -o test

And if you remove -mno-unaligned-access, then it will generate a simpler version of the code that uses unaligned word access, which is still plenty fast and not worth thinking about in practice.
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #295 on: February 10, 2023, 11:06:35 pm »
Quote
But if you just write your version in plain c and let compiler substitute internal implementation, you will get the same exact optimized version.

Evidently not.

Quote
If you compiler this with -Og, then it would result in a simple byte by byte loop

Yes.

Quote
But if you use real optimization levels, then it generates the same code that checks alignment and does whatever is necessary to take care of the edge conditions

Evidently not if it calls a library memcpy. But maybe Yes if the library happened to have been built with an optimised version.

I did a google on -mno-unaligned-access and it is a big can of worms.

Quote
not worth thinking about in practice.

That would be generally true, but only because most people write some code and if it works they move on, so they don't discover what is going on ;)

Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #296 on: February 10, 2023, 11:13:30 pm »
Evidently not if it calls a library memcpy. But maybe Yes if the library happened to have been built with an optimised version.
It only calls the library in your code where you already attempt to optimize everything.

If you take my code from above and use it as is with the flags I provided, it should generate optimized code that does not call the library. If it does not - something is broken in your compiler.

What compiler version you are using anyway?

I did a google on -mno-unaligned-access and it is a big can of worms.
-munaligned-access may be dangerous, but -mno-unaligned-access is always safe.

If you want it to be absolutely optimal, use DMA.
Alex
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 6039
  • Country: gb
  • Doing electronics since the 1960s...
Re: GCC compiler optimisation
« Reply #297 on: February 10, 2023, 11:18:52 pm »
Quote
What compiler version you are using anyway?

The latest with latest Cube. V10.something.

Not sure where it is easily visible, short of running one of the executables on a command line.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 12485
  • Country: us
    • Personal site
Re: GCC compiler optimisation
« Reply #298 on: February 10, 2023, 11:25:12 pm »
All the experiments above were done with "gcc version 10.2.1 20201103 (release) (GNU Arm Embedded Toolchain 10-2020-q4-major)" (official ARM release).

Works here as expected.
Alex
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11253
  • Country: fi
Re: GCC compiler optimisation
« Reply #299 on: February 11, 2023, 06:55:16 am »
As you know, I am stupid,

I don't think so at all. This is a perfect example. Normal stupid people don't think about DMA vs. CPU bus arbitration at all, and everything just works out because the engineers at ARM / ST did a sane design, for those stupid people to use.

Instead, you are smart enough to come up with a unique scenario. You are not stupid, you are overthinking and if anything, fixating into things that should be ruled out quickly, for too long, which eats time you could use for more fruitful thinking.

I suggest you learn to instrument your code. That way, you can always do a simple test/measurement when you feel like reading the documentation is not efficient use of time.

Quote
I could not find an answer to whether mem-mem DMA transfers are interruptible in the RM.

This is because you came up with your own search term which does not make sense ("interruptible"). Kinda X-Y problem. Your real question is, does using DMA slow down CPU doing normal CPU things (e.g., serve interrupts), and if it does, by how much. But if you just say "is DMA interruptible", no one understands what you are asking. I mean, the whole point of DMA is that it runs parallel with the CPU. That's literally the only raison d'etre. So usually there are very few cases in which you want to pause or cancel the DMA operation.

For example, this is explained on the Wikipedia page about DMA:
Quote
Direct memory access (DMA) is a feature of computer systems that allows certain hardware subsystems to access main system memory independently of the central processing unit (CPU).
and
Quote
With DMA, the CPU first initiates the transfer, then it does other operations while the transfer is in progress
« Last Edit: February 11, 2023, 07:00:59 am by Siwastaja »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->