Execution time problem of Memset() function in C
There are multiple factors here to consider. You may be falling into only one of them, but you still have to be careful. First and foremost it is trivial to demonstrate wide performance differences with a simple two instruction loops:

top:
subs r0,r0,#1
bne top


Which CPU, which may or may not determine how it fetches, they normally fetch a block of instructions in one bus transaction, for example 4 or 8 instructions. And then start working on those, if they figure out that you are branching backward, they might not have to prefetch another 4, certainly if these span two fetch lines as I like to call them then you will have to fetch those all the time (assuming no cache, but the same problem exists with a cache enabled, processor still has to fetch).

Just those two instructions and a single STM32 I can make the performance vary widely.

That may or may not be part of the problem.

memset is a C library thing so you have to look at your C library. These are often hand crafted in assembly, and you might think this is a bad implementation, but I am already impressed by your numbers. They take advantage of the instruction set and alignment. Your array is of 32-bit things, so it is aligned on a 32 bit boundary. If your array was a bunch of bytes and by tuning the variables ahead of it, you could possibly make it unaligned on that end, certainly on the backend by making it an odd number of things.

In general, memset will want to use the less efficient byte instruction to get to a halfword alignment and if that is not word aligned then a halfword store to get to a word alignment, then using multiple words per store go through a loop until that is near the end but not going over. Then as needed, a two-word store, a one-word store, a halfword store, and a byte store. It is like a standard transmission working through the gears to the high gear and stay there as long as you can then work back down to stop.

The first eight would be single, double, and quad word stores, because this is all aligned plus the overhead of prepping the registers to do the store.

There is prep work both in zeroing some registers to be used for storing zeros plus some work to determine alignment on each end.

mov r4,#0
str r4,[r0]

mov r4,#0
mov r5,#0
std r4,[r0]

mov r4,#0
mov r5,#0
std r4,[r0],#8
str r4,[r0]

mov r4,#0
mov r5,#0
std r4,[r0],#8
std r4,[r0]


Working your way up to loops of,

stm r0!,{r4,r5,r6,r7}


or even unrolled loops of

stm r0!,{r4,r5,r6,r7}
stm r0!,{r4,r5,r6,r7}
stm r0!,{r4,r5,r6,r7}
stm r0!,{r4,r5,r6,r7}


and then gear shifting down as needed.

You are using a generic C library so that was probably pre-compiled and fixed and has the overhead assuming any alignment of address and any number of bytes. You are doing aligned words so you could hand craft something faster. You still fall into the instruction alignment problem, but you can work through that too...

I would start with things like

two_word_fill:
   stm r1!,{r2,r3}
   subs r0,r0,#1
   bne two_word_fill:


and call that with

two_word_fill(13,ADC_Sum,0,0);


having the C compiler prep r2, and r3 for us. The stack is not needed on the assembly side, but it will probably be on the C side to call the function. You can't get around that unless you hand assemble the whole function this lives in (or play inline assembly games).

You could try something like this:

test2:
   push {r4,r5,r6,r7}
   mov r4,#0
   mov r5,#0
   mov r6,#0
   mov r7,#0
t2loop:
   stm r0!,{r4,r5,r6,r7}
   stm r0!,{r4,r5,r6,r7}
   stm r0!,{r4,r5,r6,r7}
   stm r0!,{r4,r5,r6,r7}
   subs r1,r1,#1
   bne t2loop
   pop {r4,r5,r6,r7}
   bx lr


Call it with:

test2(ADC_Sum,n);


It does 16 words per n, so if your array was 16 deep then you would use a 4 for n. Ideally make your struct a multiple of 16 words.

You can hand tune the alignment of the code by using the assembler syntax which is something like this for gas:

.align 8
nop
nop
top:
subs r0,r0,#1
bne top


Get it to align on a good sized boundary (the 8 doesn't necessarily mean 8 words play with different values and look at the disassembly of the linked result). Then by adding nops you can tune where in the fetch line(s) your main loop lives.

Your array is pretty small so you are likely running into another problem of accurately timing your test, plus the problem of the setup needed to get into the high-speed loop and the cleanup if any at the end are a larger percentage of the overall time as the array gets smaller. If you look at memset and memcpy hand assembled code you find they sometimes have a threshold. If less than N, to hell with it; just do a loop of byte stores. When you cross that threshold then let's try something else and so on. You may already be seeing that with your tests. The 16 words is an easy one to detect and go faster, and the ones between 8 and 16 can cause performance pain. The 8 and below may be in the. It's just faster to do a character store loop to N and be done section of the code.

So if this is really that timing-critical you shouldn't be using a generic C library call that at any time in the future can change with the toolchain or library upgrades, causing maintenance nightmares in the future. You have to own this one if you absolutely have to get the time down.

Note that you are probably running this from flash too I assume. Flash is slow on these devices and as you boost the clock rate the flash wait states go up, so relative to the main clock your flash performance can be within some range of its max speed, but the RAM tends to not have wait states. If you were to copy this code to RAM and run it from there, your performance will go up, double if not more.