[PATCH v6 3/3] RISC-V: add riscv vector support for memcpy
daichengrong
daichengrong@iscas.ac.cn
Fri Mar 7 09:23:46 GMT 2025
在 2025/2/15 8:53:07, Palmer Dabbelt 写道:
> On Fri, 14 Feb 2025 16:43:39 PST (-0800), aswaterman@gmail.com wrote:
>> On Fri, Feb 14, 2025 at 9:22 AM Palmer Dabbelt <palmer@rivosinc.com>
>> wrote:
>>>
>>> On Fri, 14 Feb 2025 04:59:22 PST (-0800), aswaterman@gmail.com wrote:
>>> > On Fri, Feb 14, 2025 at 3:24 AM Andrew Waterman
>>> <aswaterman@gmail.com> wrote:
>>> >>
>>> >> This is not what a generic RVV memcpy routine should look like. It
>>> >> should look like this:
>>> >>
>>> https://github.com/riscvarchive/riscv-v-spec/blob/2f68ef7256d6ec53e4d2bd7cb12862f406d64e34/example/memcpy.s#L7
>>> >>
>>> >> The version you are proposing looks like it is optimized for a
>>> >> particular microarchitecture. That's fine, but it is a
>>> >> de-optimization for other microarchitectures. The generic version I
>>> >> reference contains fewer branches, fewer static instructions, and
>>> >> fewer dynamic instructions, for any memcpy size > 0. (And 0 is
>>> not a
>>> >> case we should be aggressively optimizing for.)
>>> >
>>> > My previous message isn't entirely accurate. The proposed
>>> > implementation does elide a couple ALU ops in the short-vector case,
>>> > but it does so at the cost of another branch. It remains the case
>>> > that the reference code from the spec will be better for most
>>> > implementations.
>>> So I think we need to see some numbers from some actual HW in order to
>>> reason about this stuff? These string routines are the sort of place
>>> where uarch-specific special cases can be useful, but without numbers
>>> we're just going to pile up special cases that don't help.
>>
>> Indeed. But there's benefit to making progress now. My
>> recommendation is to incorporate the routines in the ISA spec, as
>> they're sensible defaults that will provide speedup over the status
>> quo across a broad range of implementations, even if they aren't quite
>> optimal for some. Routines optimized for specific uarches can be
>> contributed over time.
>
> Ya, it's hard to get a whole lot simpler than the ones in the ISA
> manual. I think Jeff had a patch set to add them for the mem/str
> routines, but I don't think it got posted?
>
> Shouldn't be too hard to just run them on some V-supporting board
> (we've got a bunch of the Banana Pi boards floating around). IIRC
> there's some benchmarks in the test suite so it sort of needs to get
> done before merging them anyway. If they're not faster than the
> scalar code on any HW then something's gone off the rails, but
> hopefully we don't need to worry about that.
> Then if someone's got other fancy stuff that makes some design go fast
> we can look at it, but at least we've got a reasonable baseline to
> compare against to tell if it's worth taking the extra complexity.
On banana pi-F3 (256bit RVV), the optimization can also be obtained in
the short vector case by testing the bench-memcpy in the test suite.
"ifuncs": ["generic_memcpy", "__memcpy_vector", "__memcpy_generic"],
"results": [
{
"length": 1,
"align1": 0,
"align2": 0,
"dst > src": 0,
"timings": [14.4246, 7.52759, 10.6609]
},
……
{
"length": 32,
"align1": 2053,
"align2": 5,
"dst > src": 1,
"timings": [58.5632, 10.03, 46.9155]
},
{
"length": 64,
"align1": 0,
"align2": 0,
"dst > src": 0,
"timings": [47.699, 28.1677, 33.1523]
},
……
{
"length": 256,
"align1": 2056,
"align2": 8,
"dst > src": 1,
"timings": [72.7029, 34.4136, 60.6484]
},
{
"length": 512,
"align1": 0,
"align2": 0,
"dst > src": 0,
"timings": [109.06, 63.7817, 96.9036]
},
……
>>> > Separately, there is a subtle bug in the proposed code; the bgt
>>> should
>>> > be bgtu, otherwise lengths >= 2^(XLEN-1) will be incorrectly handled.
>>> >
>>> >>
>>> >> On Fri, Feb 14, 2025 at 12:52 AM <daichengrong@iscas.ac.cn> wrote:
>>> >> >
>>> >> > From: daichengrong <daichengrong@iscas.ac.cn>
>>> >> >
>>> >> > Change in v6:
>>> >> > Optimize the RVV memcpy for small lengths less than VLEN/8 bytes
>>> >> >
>>> >> > Changes in v5:
>>> >> > fix Indentation in memcpy.c
>>> >> > check ifunc-impl-list memcpy vector support with by dl_hwcap
>>> >> >
>>> >> > Changes in v4:
>>> >> > update rvv memcpy support by compiler
>>> >> > check whether rvv enabled by dl_hwcap
>>> >> >
>>> >> > Changes in v3:
>>> >> > Remove unnecessary whitespace
>>> >> >
>>> >> > Changes in v2:
>>> >> > delete size-0 branch
>>> >> >
>>> >> > ---
>>> >> > sysdeps/riscv/multiarch/memcpy_vector.S | 43
>>> +++++++++++++++++++
>>> >> > .../unix/sysv/linux/riscv/multiarch/Makefile | 8 ++++
>>> >> > .../linux/riscv/multiarch/ifunc-impl-list.c | 13 ++++++
>>> >> > .../unix/sysv/linux/riscv/multiarch/memcpy.c | 7 +++
>>> >> > 4 files changed, 71 insertions(+)
>>> >> > create mode 100644 sysdeps/riscv/multiarch/memcpy_vector.S
>>> >> >
>>> >> > diff --git a/sysdeps/riscv/multiarch/memcpy_vector.S
>>> b/sysdeps/riscv/multiarch/memcpy_vector.S
>>> >> > new file mode 100644
>>> >> > index 0000000000..4783640776
>>> >> > --- /dev/null
>>> >> > +++ b/sysdeps/riscv/multiarch/memcpy_vector.S
>>> >> > @@ -0,0 +1,43 @@
>>> >> > +/* memcpy for RISC-V Vector.
>>> >> > + Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>> >> > + This file is part of the GNU C Library.
>>> >> > +
>>> >> > + The GNU C Library is free software; you can redistribute it
>>> and/or
>>> >> > + modify it under the terms of the GNU Lesser General Public
>>> >> > + License as published by the Free Software Foundation; either
>>> >> > + version 2.1 of the License, or (at your option) any later
>>> version.
>>> >> > +
>>> >> > + The GNU C Library is distributed in the hope that it will
>>> be useful,
>>> >> > + but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> >> > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
>>> the GNU
>>> >> > + Lesser General Public License for more details.
>>> >> > +
>>> >> > + You should have received a copy of the GNU Lesser General
>>> Public
>>> >> > + License along with the GNU C Library; if not, see
>>> >> > + <https://www.gnu.org/licenses/>. */
>>> >> > +
>>> >> > +
>>> >> > +#include <sysdep.h>
>>> >> > +#include <sys/asm.h>
>>> >> > +
>>> >> > +ENTRY (__memcpy_vector)
>>> >> > + beq a2, zero, L(ret)
>>> >> > + mv a6, a0
>>> >> > +
>>> >> > + /* if LEN > VLMAX jump to block_copy handling. */
>>> >> > + vsetvli a4,zero,e8,m1,ta,ma
>>> >> > + bgt a2,a4,L(block_copy)
>>> >> > + vle8.v v8,(a1)
>>> >> > + vse8.v v8,(a6)
>>> >> > + ret
>>> >> > +L(block_copy):
>>> >> > + vsetvli a3,a2,e8,m8,ta,ma
>>> >> > + vle8.v v8,(a1)
>>> >> > + vse8.v v8,(a6)
>>> >> > + add a1,a1,a3
>>> >> > + add a6,a6,a3
>>> >> > + sub a2,a2,a3
>>> >> > + bnez a2,L(block_copy)
>>> >> > +L(ret):
>>> >> > + ret
>>> >> > +END (__memcpy_vector)
>>> >> > diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
>>> b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
>>> >> > index fcef5659d4..394033e077 100644
>>> >> > --- a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
>>> >> > +++ b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
>>> >> > @@ -5,5 +5,13 @@ sysdep_routines += \
>>> >> > memcpy_noalignment \
>>> >> > # sysdep_routines
>>> >> >
>>> >> > +ifeq ($(have-gcc-riscv-rvv),yes)
>>> >> > +sysdep_routines += \
>>> >> > + memcpy_vector \
>>> >> > + # rvv sysdep_routines
>>> >> > +
>>> >> > +ASFLAGS-memcpy_vector.S += -march=rv64gcv
>>> >> > +endif
>>> >> > +
>>> >> > CFLAGS-memcpy_noalignment.c += -mno-strict-align
>>> >> > endif
>>> >> > diff --git
>>> a/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
>>> b/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
>>> >> > index 1c1deca8f6..2088097558 100644
>>> >> > --- a/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
>>> >> > +++ b/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
>>> >> > @@ -19,6 +19,7 @@
>>> >> > #include <ifunc-impl-list.h>
>>> >> > #include <string.h>
>>> >> > #include <sys/hwprobe.h>
>>> >> > +#include <ldsodefs.h>
>>> >> >
>>> >> > size_t
>>> >> > __libc_ifunc_impl_list (const char *name, struct
>>> libc_ifunc_impl *array,
>>> >> > @@ -27,6 +28,9 @@ __libc_ifunc_impl_list (const char *name,
>>> struct libc_ifunc_impl *array,
>>> >> > size_t i = max;
>>> >> >
>>> >> > bool fast_unaligned = false;
>>> >> > +#if defined(HAVE_RISCV_ASM_VECTOR_SUPPORT)
>>> >> > + bool rvv_ext = false;
>>> >> > +#endif
>>> >> >
>>> >> > struct riscv_hwprobe pair = { .key =
>>> RISCV_HWPROBE_KEY_CPUPERF_0 };
>>> >> > if (__riscv_hwprobe (&pair, 1, 0, NULL, 0) == 0
>>> >> > @@ -34,7 +38,16 @@ __libc_ifunc_impl_list (const char *name,
>>> struct libc_ifunc_impl *array,
>>> >> > == RISCV_HWPROBE_MISALIGNED_FAST)
>>> >> > fast_unaligned = true;
>>> >> >
>>> >> > +#if defined(HAVE_RISCV_ASM_VECTOR_SUPPORT)
>>> >> > + if (GLRO(dl_hwcap) & COMPAT_HWCAP_ISA_V)
>>> >> > + rvv_ext = true;
>>> >> > +#endif
>>> >> > +
>>> >> > IFUNC_IMPL (i, name, memcpy,
>>> >> > +#if defined(HAVE_RISCV_ASM_VECTOR_SUPPORT)
>>> >> > + IFUNC_IMPL_ADD (array, i, memcpy, rvv_ext,
>>> >> > + __memcpy_vector)
>>> >> > +#endif
>>> >> > IFUNC_IMPL_ADD (array, i, memcpy, fast_unaligned,
>>> >> > __memcpy_noalignment)
>>> >> > IFUNC_IMPL_ADD (array, i, memcpy, 1,
>>> __memcpy_generic))
>>> >> > diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/memcpy.c
>>> b/sysdeps/unix/sysv/linux/riscv/multiarch/memcpy.c
>>> >> > index 8544f5402a..1982e4ab65 100644
>>> >> > --- a/sysdeps/unix/sysv/linux/riscv/multiarch/memcpy.c
>>> >> > +++ b/sysdeps/unix/sysv/linux/riscv/multiarch/memcpy.c
>>> >> > @@ -32,11 +32,18 @@ extern __typeof (__redirect_memcpy)
>>> __libc_memcpy;
>>> >> >
>>> >> > extern __typeof (__redirect_memcpy) __memcpy_generic
>>> attribute_hidden;
>>> >> > extern __typeof (__redirect_memcpy) __memcpy_noalignment
>>> attribute_hidden;
>>> >> > +extern __typeof (__redirect_memcpy) __memcpy_vector
>>> attribute_hidden;
>>> >> >
>>> >> > static inline __typeof (__redirect_memcpy) *
>>> >> > select_memcpy_ifunc (uint64_t dl_hwcap, __riscv_hwprobe_t
>>> hwprobe_func)
>>> >> > {
>>> >> > unsigned long long int v;
>>> >> > +
>>> >> > +#if defined(HAVE_RISCV_ASM_VECTOR_SUPPORT)
>>> >> > + if (dl_hwcap & COMPAT_HWCAP_ISA_V)
>>> >> > + return __memcpy_vector;
>>> >> > +#endif
>>> >> > +
>>> >> > if (__riscv_hwprobe_one (hwprobe_func,
>>> RISCV_HWPROBE_KEY_CPUPERF_0, &v) == 0
>>> >> > && (v & RISCV_HWPROBE_MISALIGNED_MASK) ==
>>> RISCV_HWPROBE_MISALIGNED_FAST)
>>> >> > return __memcpy_noalignment;
>>> >> > --
>>> >> > 2.25.1
>>> >> >
More information about the Libc-alpha
mailing list