> From: Konstantin Ananyev [mailto:[email protected]]
> Sent: Wednesday, 5 August 2026 19.10
> 
> > The implementation for copying 64-byte blocks up to 512 (or 256)
> bytes
> > does not depend on address alignment with the size of the CPU's
> vector
> > registers, but is implemented in both unaligned and aligned copy
> > functions.
> > The main rte_memcpy() function was updated, so
> > if the copy size is known at compile time and the other criteria
> match,
> > the copy is performed without checking alignment requirements.
> > This provides two benefits when the optimization comes into play:
> > 1. A performance gain, because the address alignment check is
> avoided.
> > 3. Reduced instruction memory footprint, because the compiler only
> > generates one instance of the function for copying, instead of two
> > instances (one in the unaligned copy function, and one in the aligned
> > copy function).
> >
> > Furthermore, the temporary alignment mask definition (ALIGNMENT_MASK)
> > was prefixed by RTE_MEMCPY_ to prevent potential namespace collision.
> >
> > And finally, the superfluous function declaration at the top of the
> > file was removed, and its description was moved to the function
> > definition.
> > This improves search results with source code browsers.
> >
> > Signed-off-by: Morten Brørup <[email protected]>
> > ---
> >  lib/eal/x86/include/rte_memcpy.h | 64 ++++++++++++++++++++++--------
> --
> >  1 file changed, 44 insertions(+), 20 deletions(-)
> >
> > diff --git a/lib/eal/x86/include/rte_memcpy.h
> > b/lib/eal/x86/include/rte_memcpy.h
> > index 8ed8c55010..ce5a55c36f 100644
> > --- a/lib/eal/x86/include/rte_memcpy.h
> > +++ b/lib/eal/x86/include/rte_memcpy.h
> > @@ -32,21 +32,6 @@ extern "C" {
> >  #define RTE_MEMCPY_AVX
> >  #endif
> >
> > -/**
> > - * Copy bytes from one location to another. The locations must not
> overlap.
> > - *
> > - * @param dst
> > - *   Pointer to the destination of the data.
> > - * @param src
> > - *   Pointer to the source data.
> > - * @param n
> > - *   Number of bytes to copy.
> > - * @return
> > - *   Pointer to the destination data.
> > - */
> > -static __rte_always_inline void *
> > -rte_memcpy(void *__rte_restrict dst, const void *__rte_restrict src,
> size_t n);
> > -
> >  /**
> >   * Copy bytes from one location to another,
> >   * locations must not overlap.
> > @@ -187,7 +172,8 @@ rte_mov256(uint8_t *__rte_restrict dst, const
> uint8_t
> > *__rte_restrict src)
> >   * AVX512 implementation below
> >   */
> >
> > -#define ALIGNMENT_MASK 0x3F
> > +#define RTE_MEMCPY_ALIGNMENT_MASK 0x3F
> > +#define RTE_MEMCPY_BLOCK_64_MAX 512
> >
> >  /**
> >   * Copy 128-byte blocks from one location to another,
> > @@ -333,7 +319,8 @@ rte_memcpy_generic_more_than_64(void
> *__rte_restrict
> > dst, const void *__rte_rest
> >   * AVX implementation below
> >   */
> >
> > -#define ALIGNMENT_MASK 0x1F
> > +#define RTE_MEMCPY_ALIGNMENT_MASK 0x1F
> > +#define RTE_MEMCPY_BLOCK_64_MAX 256
> 
> Wonder why BLOCK_64_MAX is 512 for SSE and AVX512, but 256 for AVX2?
> Some empirical data or ...?

I wonder too.
Intel wrote it that way.
I considered consolidating on 512 for simplicity.
But then I would be changing the algorithm without any data to support the 
change.

Now you tickled my curiosity, so I went hunting...
It was 512 until DPDK 16.04, but in 16.07 it was changed to 256 with this patch:
https://github.com/DPDK/dpdk/commit/4b42e90ef0e421dc777f2b2e377eb237cd3675fa

> 
> >
> >  /**
> >   * Copy 128-byte blocks from one location to another,
> > @@ -444,7 +431,8 @@ rte_memcpy_generic_more_than_64(void
> *__rte_restrict
> > dst, const void *__rte_rest
> >   * SSE implementation below
> >   */
> >
> > -#define ALIGNMENT_MASK 0x0F
> > +#define RTE_MEMCPY_ALIGNMENT_MASK 0x0F
> > +#define RTE_MEMCPY_BLOCK_64_MAX 512
> >
> >  /**
> >   * Macro for copying unaligned block from one location to another
> with
> > constant load offset,
> > @@ -673,6 +661,18 @@ rte_memcpy_aligned_more_than_64(void
> > *__rte_restrict dst, const void *__rte_rest
> >     return ret;
> >  }
> >
> > +/**
> > + * Copy bytes from one location to another. The locations must not
> overlap.
> > + *
> > + * @param dst
> > + *   Pointer to the destination of the data.
> > + * @param src
> > + *   Pointer to the source data.
> > + * @param n
> > + *   Number of bytes to copy.
> > + * @return
> > + *   Pointer to the destination data.
> > + */
> >  static __rte_always_inline void *
> >  rte_memcpy(void *__rte_restrict dst, const void *__rte_restrict src,
> size_t n)
> >  {
> > @@ -707,15 +707,39 @@ rte_memcpy(void *__rte_restrict dst, const void
> > *__rte_restrict src, size_t n)
> >  #endif
> >             return dst;
> >     }
> > +   /* Common way for small copy size of 64-byte blocks. Unlikely, so
> > constant size only */
> > +   if (__rte_constant(n) && (n & 63) == 0 && n <=
> > RTE_MEMCPY_BLOCK_64_MAX) {
> 
> You probably need a check (static_assert?) that RTE_MEMCPY_BLOCK_64_MAX
> is
> Within allowed values (<= 512+256+128+64).
> Otherwise code block below might not be enough to copy everything.

Allowed values stop at 512. Note that src and dst are not incremented for 512.

I would hope that someone changing the RTE_MEMCPY_BLOCK_64_MAX definition (or 
introducing a new value if support for a new vector register length is added) 
doesn't do it without considering what it is used for. A static_assert seems 
overkill here.

> 
> > +           void *ret = dst;
> > +
> > +           if (n & 512) {
> > +                   rte_mov256((uint8_t *)dst + 0 * 256, (const uint8_t
> *)src
> > + 0 * 256);
> > +                   rte_mov256((uint8_t *)dst + 1 * 256, (const uint8_t
> *)src
> > + 1 * 256);
> > +           }
> > +           if (n & 256) {
> > +                   rte_mov256((uint8_t *)dst, (const uint8_t *)src);
> > +                   src = (const uint8_t *)src + 256;
> > +                   dst = (uint8_t *)dst + 256;
> > +           }
> > +           if (n & 128) {
> > +                   rte_mov128((uint8_t *)dst, (const uint8_t *)src);
> > +                   src = (const uint8_t *)src + 128;
> > +                   dst = (uint8_t *)dst + 128;
> > +           }
> > +           if (n & 64)
> > +                   rte_mov64((uint8_t *)dst, (const uint8_t *)src);
> > +
> > +           return ret;
> > +   }
> >
> >     /* Implementation for size > 64 bytes depends on alignment with
> vector
> > register size. */
> > -   if (!(((uintptr_t)dst | (uintptr_t)src) & ALIGNMENT_MASK))
> > +   if (!(((uintptr_t)dst | (uintptr_t)src) &
> RTE_MEMCPY_ALIGNMENT_MASK))
> >             return rte_memcpy_aligned_more_than_64(dst, src, n);
> >     else
> >             return rte_memcpy_generic_more_than_64(dst, src, n);
> >  }
> >
> > -#undef ALIGNMENT_MASK
> > +#undef RTE_MEMCPY_ALIGNMENT_MASK
> > +#undef RTE_MEMCPY_BLOCK_64_MAX
> >
> >  #ifdef __cplusplus
> >  }
> > --
> > 2.43.0

Reply via email to