[PATCH] riscv: Add RVV strrchr

daichengrong daichengrong@iscas.ac.cn
Sun Jun 14 05:41:56 GMT 2026


Hi,

I would like to ping my strrchr optimization patch and provide some
additional performance data.

When I submitted my optimized version on 2026-03-03, the initial RVV
strrchr implementation had not been merged yet. About one week ago, on
2026-06-05, an earlier version was merged, and that is now the current
implementation in the source tree.

I may have missed some context in the review process, so I would like to
better understand the current status and the expected next step for my
pending optimization. My intention is not to question the merge, but to
understand whether the pending version should be rebased and submitted
as an incremental improvement, or whether there are other concerns that
need to be addressed first.

I compared the current implementation from the git tree with the version
from my pending patch. 

For strrchr, the cost is not only related to the string length. A simple
model is that strrchr first needs to find the terminating NUL byte, and
then it needs to find the last matching character from the end of the
string. Therefore I used the following effective byte count:

  distance = len - pos

If pos is larger than len, I treated it as a no-match case:

  distance = len

Then:

  effective_bytes = len + distance

For each individual test result, I computed:

  CPB_eff = timing / effective_bytes

I then grouped the results into effective-bytes buckets. The small
ranges below 256 bytes are merged into one 0-256 bucket to make the
threshold behavior easier to see.

The winner below is determined by the average time per call, while
CPB_eff is included to show the per-byte efficiency trend.

  Bucket     N     Git_eff  New_eff  Git_t    New_t
  -------------------------------------------------
  0-256      1086  2.125    2.150    46.753   38.518
    winner: pending version is about 1.21x faster

  257-511    354   0.298    0.164    118.920  66.162
    winner: pending version is about 1.80x faster

  512-1023   258   0.286    0.132    193.527  89.087
    winner: pending version is about 2.17x faster

  1024-2047  216   0.279    0.112    383.950  155.671
    winner: pending version is about 2.47x faster

  2048-4095  294   0.251    0.099    720.090  289.023
    winner: pending version is about 2.49x faster

  4096-8191  24    0.174    0.073    710.987  298.913
    winner: pending version is about 2.38x faster

Here, Git means the current __strrchr_vector implementation in the
source tree, and New means the __strrchr_vector implementation from my
pending patch. Git_eff/New_eff are computed as timing / effective_bytes.
Git_t/New_t are the average time per call in the benchmark timing unit.

The data shows that the pending version is faster in every effective
bucket on K3. The improvement is small but still positive in the 0-256
bucket, and becomes much more visible from 257 effective bytes onward.
For effective sizes from 512 bytes and above, the pending version is
about 2.17x to 2.49x faster in this benchmark.

Could you please let me know if there are any concerns with the pending
optimization, such as correctness coverage, maintainability, code size,
threshold selection, or benchmark methodology? This would help me
understand whether the patch needs further changes beyond the
performance results shown above.

If the current implementation in the source tree is now the preferred
baseline, I can rebase my patch on top of it and resend it as an
incremental optimization.

Thanks.


> 2026年3月3日 10:58,daichengrong <daichengrong@iscas.ac.cn> 写道:
> 
> This patch adds an RVV-optimized implementation of strrchr for RISC-V.
> 
> Benchmark results show no noticeable regression on small inputs,
> while delivering clear performance improvements on larger inputs.
> 
> Signed-off-by: daichengrong <daichengrong@iscas.ac.cn>
> ---
> sysdeps/riscv/multiarch/strrchr-generic.c     |  28 ++++
> sysdeps/riscv/multiarch/strrchr-vector.S      |  26 ++++
> sysdeps/riscv/rvv/strrchr.S                   | 126 ++++++++++++++++++
> .../unix/sysv/linux/riscv/multiarch/Makefile  |   3 +
> .../linux/riscv/multiarch/ifunc-impl-list.c   |   5 +
> .../unix/sysv/linux/riscv/multiarch/strrchr.c |  59 ++++++++
> 6 files changed, 247 insertions(+)
> create mode 100644 sysdeps/riscv/multiarch/strrchr-generic.c
> create mode 100644 sysdeps/riscv/multiarch/strrchr-vector.S
> create mode 100644 sysdeps/riscv/rvv/strrchr.S
> create mode 100644 sysdeps/unix/sysv/linux/riscv/multiarch/strrchr.c
> 
> diff --git a/sysdeps/riscv/multiarch/strrchr-generic.c b/sysdeps/riscv/multiarch/strrchr-generic.c
> new file mode 100644
> index 0000000000..c61c6e1a03
> --- /dev/null
> +++ b/sysdeps/riscv/multiarch/strrchr-generic.c
> @@ -0,0 +1,28 @@
> +/* Re-include the default strrchr implementation.
> +   Copyright (C) 2026 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 <string.h>
> +
> +#if IS_IN(libc)
> +# define STRRCHR __strrchr_generic
> +# undef libc_hidden_builtin_def
> +# define libc_hidden_builtin_def(x)
> +# undef weak_alias
> +# define weak_alias(x, x2)
> +# include <string/strrchr.c>
> +#endif
> diff --git a/sysdeps/riscv/multiarch/strrchr-vector.S b/sysdeps/riscv/multiarch/strrchr-vector.S
> new file mode 100644
> index 0000000000..8fef68eae0
> --- /dev/null
> +++ b/sysdeps/riscv/multiarch/strrchr-vector.S
> @@ -0,0 +1,26 @@
> +/* Re-include the RISC-V RVV based strrchr implementation.
> +   Copyright (C) 2026 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/>.  */
> +
> +#if IS_IN(libc)
> +# define STRRCHR __strrchr_vector
> +# undef libc_hidden_builtin_def
> +# define libc_hidden_builtin_def(name)
> +# undef weak_alias
> +# define weak_alias(name, alias)
> +# include <sysdeps/riscv/rvv/strrchr.S>
> +#endif
> diff --git a/sysdeps/riscv/rvv/strrchr.S b/sysdeps/riscv/rvv/strrchr.S
> new file mode 100644
> index 0000000000..f1471b2c32
> --- /dev/null
> +++ b/sysdeps/riscv/rvv/strrchr.S
> @@ -0,0 +1,126 @@
> +/* RISC-V RVV zbb based strrchr: find the last instance of a character in a string.
> +   Copyright (C) 2026 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>
> +
> +#ifndef STRRCHR
> +# define STRRCHR strrchr
> +#endif
> +
> +#define str a0
> +#define ch a1
> +#define len a2
> +#define in a3
> +#define match a4
> +#define end a5
> +#define vmask v0
> +#define vmask_t v0.t
> +#define vdata v8
> +#define vmatch_null v16
> +#define vmatch_ch v24
> +
> +#define vindex v16
> +#define vmax v24
> +
> +#define chr_hit t1
> +#define null_hit t2
> +
> +#define cur_vl t0
> +#define tmp t0
> +
> +ENTRY (STRRCHR)
> +.option push
> +.option arch, +v
> +.option arch, +zbb
> +    lbu         tmp, (str)
> +    beq         tmp, ch, L(hit_0)
> +    beqz        ch, L(search_null)
> +    add         tmp, str, -1
> +L(start):
> +    mv          match, tmp
> +    mv          in, str
> +    mv          cur_vl, zero
> +L(loop):
> +    add         str, str, cur_vl
> +    vsetvli     cur_vl, zero, e8, m4, ta, ma
> +    vle8ff.v    vdata, (str)
> +    vmseq.vx    vmatch_null, vdata, zero
> +    vmseq.vx    vmatch_ch, vdata, ch
> +    vfirst.m    null_hit, vmatch_null
> +    vfirst.m    chr_hit, vmatch_ch
> +
> +    bgez        null_hit, L(finish)
> +    csrr        cur_vl, vl
> +    bgez        chr_hit, L(update)
> +    j           L(loop)
> +L(update):
> +    add         match, str, chr_hit
> +    j           L(loop)
> +
> +L(finish):
> +    mv          tmp, zero
> +    bltz        chr_hit, L(no_update)
> +    bgt         chr_hit, null_hit, L(no_update)
> +    sub         tmp, str, match
> +    add         tmp, tmp, chr_hit
> +L(no_update):
> +    add         match, match, tmp
> +    blt         match, in, L(not_found)
> +
> +    add         end, str, null_hit
> +    sub         len, end, match
> +    vsetvli     cur_vl, zero, e8, m4, ta, ma
> +    min         len, len, cur_vl
> +
> +    vsetvli     zero, len, e8, m4, ta, ma
> +    vle8.v      vdata, (match)
> +    vmseq.vx    vmask, vdata, ch
> +    vsetvli     zero, zero, e16, m8, ta, ma
> +    vid.v       vindex
> +    vmv.s.x     vmax, zero
> +    vredmaxu.vs vmax, vindex, vmax, vmask_t
> +    vmv.x.s     tmp, vmax
> +    add         str, match, tmp
> +    ret
> +
> +L(hit_0):
> +    beqz        ch, L(ret_0)
> +    mv          tmp, str
> +    j           L(start)
> +
> +L(search_null):
> +    mv          cur_vl, zero
> +L(search_null_loop):
> +    add         str, str, cur_vl
> +    vsetvli     cur_vl, zero, e8, m8, ta, ma
> +    vle8ff.v    vdata, (str)
> +    vmseq.vx    vmatch_null, vdata, zero
> +    vfirst.m    null_hit, vmatch_null
> +    bltz        null_hit, L(search_null_loop)
> +    add         str, str, null_hit
> +    ret
> +
> +L(not_found):
> +    li      a0, 0
> +L(ret_0):
> +    ret
> +.option pop
> +END (STRRCHR)
> +weak_alias (STRRCHR, rindex)
> +libc_hidden_builtin_def (strrchr)
> diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
> index a865090a53..cd59fb39f3 100644
> --- a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
> +++ b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
> @@ -6,6 +6,9 @@ sysdep_routines += \
>   memset \
>   memset-generic \
>   memset-vector \
> +  strrchr \
> +  strrchr-generic \
> +  strrchr-vector \
>   # sysdep_routines
> 
> CFLAGS-memcpy_noalignment.c += -mno-strict-align
> 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 a3b5731411..dc39335b76 100644
> --- a/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
> +++ b/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
> @@ -53,5 +53,10 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
>      __memset_vector)
>      IFUNC_IMPL_ADD (array, i, memset, 1, __memset_generic))
> 
> +  IFUNC_IMPL (i, name, strrchr,
> +      IFUNC_IMPL_ADD (array, i, strrchr, rvv_enabled,
> +      __strrchr_vector)
> +      IFUNC_IMPL_ADD (array, i, strrchr, 1, __strrchr_generic))
> +
>   return 0;
> }
> diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/strrchr.c b/sysdeps/unix/sysv/linux/riscv/multiarch/strrchr.c
> new file mode 100644
> index 0000000000..3532af0dc0
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/riscv/multiarch/strrchr.c
> @@ -0,0 +1,59 @@
> +/* Multiple versions of strrchr.
> +   All versions must be listed in ifunc-impl-list.c.
> +   Copyright (C) 2026 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/>.  */
> +
> +#if IS_IN (libc)
> +/* Redefine strrchr so that the compiler won't complain about the type
> +   mismatch with the IFUNC selector in strong_alias, below.  */
> +# undef strrchr
> +# define strrchr __redirect_strrchr
> +# include <stdint.h>
> +# include <string.h>
> +# include <ifunc-init.h>
> +# include <riscv-ifunc.h>
> +# include <sys/hwprobe.h>
> +
> +extern __typeof (__redirect_strrchr) __libc_strrchr;
> +
> +extern __typeof (__redirect_strrchr) __strrchr_generic attribute_hidden;
> +extern __typeof (__redirect_strrchr) __strrchr_vector attribute_hidden;
> +
> +static inline __typeof (__redirect_strrchr) *
> +select_strrchr_ifunc (uint64_t dl_hwcap, __riscv_hwprobe_t hwprobe_func)
> +{
> +  unsigned long long v;
> +
> +  if (__riscv_hwprobe_one (hwprobe_func, RISCV_HWPROBE_KEY_IMA_EXT_0, &v) == 0
> +      && (v & RISCV_HWPROBE_IMA_V) == RISCV_HWPROBE_IMA_V)
> +    return __strrchr_vector;
> +  return __strrchr_generic;
> +}
> +
> +riscv_libc_ifunc (__libc_strrchr, select_strrchr_ifunc);
> +
> +# undef strrchr
> +# undef rindex
> +strong_alias (__libc_strrchr, strrchr);
> +weak_alias (strrchr, rindex);
> +# ifdef SHARED
> +__hidden_ver1 (strrchr, __GI_strrchr, __redirect_strrchr)
> +  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (strrchr);
> +# endif
> +#else
> +# include <string/strrchr.c>
> +#endif
> -- 
> 2.25.1



More information about the Libc-alpha mailing list