On Sun, Apr 20, 2025 at 4:19 AM Jan Hubicka <hubi...@ucw.cz> wrote:
>
> > On Tue, Apr 8, 2025 at 3:52 AM H.J. Lu <hjl.to...@gmail.com> wrote:
> > >
> > > Simplify memcpy and memset inline strategies to avoid branches for
> > > -mtune=generic:
> > >
> > > 1. With MOVE_RATIO and CLEAR_RATIO == 17, GCC will use integer/vector
> > >    load and store for up to 16 * 16 (256) bytes when the data size is
> > >    fixed and known.
>
> Originally we set CLEAR_RATION smaller than MOVE_RATIO because to store
> zeros we use:
>
>    0:   48 c7 07 00 00 00 00    movq   $0x0,(%rdi)
>    7:   48 c7 47 08 00 00 00    movq   $0x0,0x8(%rdi)
>    e:   00
>    f:   48 c7 47 10 00 00 00    movq   $0x0,0x10(%rdi)
>   16:   00
>   17:   48 c7 47 18 00 00 00    movq   $0x0,0x18(%rdi)
>   1e:   00
>
> so about 8 bytes per instructions.   We could optimize it by loading 0

This is orthogonal to this patch.

> to scratch register but we don't.  SSE variant is shorter:
>
>    4:   0f 11 07                movups %xmm0,(%rdi)
>    7:   0f 11 47 10             movups %xmm0,0x10(%rdi)
>
> So I wonder if we care about code size with -mno-sse (i.e. for building
> kernel).

This patch doesn't change -Os behavior which uses x86_size_cost,
not generic_cost.

> > >  static stringop_algs generic_memcpy[2] = {
> > > -  {libcall, {{32, loop, false}, {8192, rep_prefix_4_byte, false},
> > > -             {-1, libcall, false}}},
> > > -  {libcall, {{32, loop, false}, {8192, rep_prefix_8_byte, false},
> > > -             {-1, libcall, false}}}};
> > > +  {libcall,
> > > +   {{256, rep_prefix_1_byte, true},
> > > +    {256, loop, false},
> False/true here is stringop_algs->noalign field which is used to control
> enable/siable alignment prologue.   For rep_prefix_1_byte it should be
> noop except for pentiumpro which preferred alignment of 8.
>
> decide_alg picks first useable algorithm with size greater than expected
> size of the block.  rep_prefix_1_byte may become unuseable if user fixes
> AX/CX/SI/DI, but it won't pick loop if size is known.
>
> A reason why we use loop for small blocks is that Buldozers were quite
> poor on handling rep movsb for very small blocks.  We probably want to
> retune generic w/o too much of buldozer specific considerations.
> There is a simple microbenchmark in contrib/bench-stringops that cycles
> through different algs and different average sizes
>
> on znver5 and memcpy I get:
> memcpy
>   block size  libcall rep1    noalg   rep4    noalg   rep8    noalg   loop    
> noalg   unrl    noalg   sse     noalg   byte    PGO     dynamic    BEST
>      8192000  0:00.07 0:00.11 0:00.11 0:00.11 0:00.09 0:00.11 0:00.08 0:00.15 
> 0:00.15 0:00.10 0:00.11 0:00.10 0:00.08 0:01.18 0:00.07 0:00.07    0:00.07 
> libcall
>       819200  0:00.07 0:00.07 0:00.07 0:00.07 0:00.07 0:00.07 0:00.07 0:00.15 
> 0:00.15 0:00.09 0:00.10 0:00.09 0:00.09 0:01.17 0:00.07 0:00.07    0:00.07 
> libcall
>        81920  0:00.08 0:00.08 0:00.11 0:00.05 0:00.06 0:00.07 0:00.06 0:00.20 
> 0:00.20 0:00.12 0:00.13 0:00.10 0:00.10 0:01.57 0:00.08 0:00.08    0:00.05 
> rep4
>        20480  0:00.06 0:00.05 0:00.05 0:00.05 0:00.05 0:00.05 0:00.05 0:00.15 
> 0:00.15 0:00.08 0:00.10 0:00.06 0:00.06 0:01.18 0:00.05 0:00.06    0:00.05 
> rep1
>         8192  0:00.05 0:00.04 0:00.04 0:00.03 0:00.05 0:00.03 0:00.05 0:00.15 
> 0:00.16 0:00.09 0:00.10 0:00.06 0:00.07 0:01.17 0:00.03 0:00.05    0:00.03 
> rep4
>         4096  0:00.03 0:00.04 0:00.04 0:00.04 0:00.05 0:00.04 0:00.05 0:00.16 
> 0:00.17 0:00.09 0:00.10 0:00.07 0:00.07 0:01.18 0:00.04 0:00.04    0:00.03 
> libcall
>         2048  0:00.03 0:00.05 0:00.05 0:00.05 0:00.06 0:00.05 0:00.05 0:00.18 
> 0:00.18 0:00.08 0:00.10 0:00.09 0:00.08 0:01.20 0:00.05 0:00.05    0:00.03 
> libcall
>         1024  0:00.04 0:00.07 0:00.06 0:00.07 0:00.07 0:00.07 0:00.06 0:00.19 
> 0:00.19 0:00.09 0:00.10 0:00.09 0:00.10 0:01.23 0:00.07 0:00.07    0:00.04 
> libcall
>          512  0:00.06 0:00.11 0:00.11 0:00.11 0:00.11 0:00.11 0:00.10 0:00.18 
> 0:00.19 0:00.10 0:00.11 0:00.12 0:00.13 0:01.30 0:00.11 0:00.11    0:00.06 
> libcall
>          256  0:00.11 0:00.18 0:00.18 0:00.17 0:00.17 0:00.17 0:00.16 0:00.17 
> 0:00.19 0:00.12 0:00.13 0:00.17 0:00.20 0:01.40 0:00.17 0:00.17    0:00.11 
> libcall
>          128  0:00.16 0:00.27 0:00.27 0:00.20 0:00.18 0:00.19 0:00.18 0:00.20 
> 0:00.21 0:00.17 0:00.18 0:00.31 0:00.48 0:01.41 0:00.19 0:00.19    0:00.16 
> libcall
>           64  0:00.24 0:00.23 0:00.23 0:00.39 0:00.36 0:00.37 0:00.34 0:00.26 
> 0:00.26 0:00.26 0:00.27 0:00.68 0:00.81 0:01.57 0:00.36 0:00.37    0:00.23 
> rep1
>           48  0:00.30 0:00.34 0:00.34 0:00.51 0:00.50 0:00.49 0:00.47 0:00.33 
> 0:00.32 0:00.32 0:00.33 0:00.84 0:00.96 0:01.48 0:00.49 0:00.49    0:00.30 
> libcall
>           32  0:00.40 0:00.46 0:00.47 0:00.76 0:00.71 0:00.71 0:00.65 0:00.43 
> 0:00.42 0:00.43 0:00.42 0:01.26 0:01.13 0:01.26 0:00.71 0:00.43    0:00.40 
> libcall
>           24  0:00.54 0:00.67 0:00.65 0:01.01 0:00.98 0:00.95 0:00.89 0:00.57 
> 0:00.52 0:00.53 0:00.52 0:01.21 0:01.21 0:01.18 0:00.95 0:00.57    0:00.52 
> loopnoalign
>           16  0:00.71 0:00.90 0:00.91 0:01.48 0:01.36 0:01.39 0:01.17 0:00.71 
> 0:00.66 0:00.59 0:00.59 0:01.21 0:01.14 0:01.21 0:00.72 0:00.72    0:00.59 
> unrl
>           14  0:00.86 0:01.13 0:01.15 0:01.73 0:01.64 0:01.62 0:01.41 0:00.83 
> 0:00.74 0:00.70 0:00.66 0:01.40 0:01.40 0:01.29 0:00.83 0:00.83    0:00.66 
> unrlnoalign
>           12  0:01.02 0:01.31 0:01.30 0:01.98 0:01.88 0:01.87 0:01.58 0:00.93 
> 0:00.82 0:00.80 0:00.74 0:01.31 0:01.33 0:01.31 0:00.93 0:00.94    0:00.74 
> unrlnoalign
>           10  0:01.19 0:01.57 0:01.55 0:02.37 0:02.20 0:02.21 0:01.79 0:01.06 
> 0:00.92 0:00.94 0:00.87 0:01.40 0:01.37 0:01.38 0:01.06 0:01.05    0:00.87 
> unrlnoalign
>            8  0:01.47 0:01.79 0:01.78 0:02.87 0:02.55 0:02.60 0:01.92 0:01.25 
> 0:01.13 0:01.13 0:01.13 0:01.49 0:01.49 0:01.48 0:01.26 0:01.26    0:01.13 
> loopnoalign
>            6  0:01.94 0:02.59 0:02.55 0:03.87 0:03.40 0:03.09 0:02.39 0:01.54 
> 0:01.44 0:01.50 0:01.44 0:01.73 0:01.73 0:01.71 0:01.53 0:01.54    0:01.44 
> loopnoalign
>            4  0:02.55 0:03.56 0:03.57 0:05.30 0:04.35 0:02.11 0:02.12 0:02.10 
> 0:02.11 0:02.10 0:02.13 0:02.08 0:02.07 0:02.06 0:02.11 0:02.07    0:02.06 
> byte
>
> so while rep stosb improved, it is still slower than the open-coded version
> for blocks of average size <32
>
> SPEC is not very sensitive to string op implementation.  I wonder if you
> have specific testcases where using loop variant for very small blocks
> is a loss?

For small blocks with known sizes, loop is slower because of branches.

> We are also better on picking codegen choice with PGO since we
> value-profile size of the block.
>
> Inlining memcpy is bigger win in situation where it prevents spilling
> data from caller saved registers.  This makes it a bit hard to guess how
> microbenchmarks relate to more real-world situations where the
> surrounding code may need to hold data in SSE regs etc.
> If we had a special entry-point to memcpy/memset that does not clobber
> registers and does its own callee save, this problem would go away...
>
> Honza



-- 
H.J.

Reply via email to