From a0d91f0f42451b733f3da1957b2b44660940719e Mon Sep 17 00:00:00 2001 From: Felix-Gong Date: Thu, 16 Jul 2026 03:06:52 +0000 Subject: [PATCH 1/2] optimize StringPiece memcmp for RISC-V with RVV This patch adds RVV-accelerated memcmp for StringPiece operations on RISC-V 64-bit platforms. The implementation follows glibc's official RVV pattern: - e8m8 LMUL for maximum throughput - Hardware-adaptive vector length via vsetvl - vfirst.m for early-out on first difference Performance on SOPHGO SG2044 (RVV 1.0, VLEN >= 128, GCC 15.1): glibc 2.38 scalar memcmp (no RVV acceleration) as baseline. memcmp (worst-case full scan, 50000 iterations): 64 B: 20 ns -> 6 ns (3.4x) 256 B: 54 ns -> 15 ns (3.6x) 1 KB: 120 ns -> 55 ns (2.2x) 4 KB: 435 ns -> 225 ns (1.9x) 16 KB: 1968 ns -> 1258 ns (1.6x) 64 KB: 10962 ns -> 9044 ns (1.2x) 256 KB: 46893 ns -> 43108 ns (1.1x) 1 MB: 446161 ns -> 454717 ns (1.01x) Correctness: - 59/59 expanded correctness tests passed (64B - 1MB) - string_piece_unittest: 24/24 passed - test_butil: 724/725 passed (1 pre-existing StackTrace failure) - test_bvar: 64/64 passed Files changed: - src/butil/string_compare_rvv.cc (new): RVV memcmp implementation - src/butil/strings/string_piece.h: RVV dispatch in wordmemcmp (N >= 16) - BUILD.bazel: added string_compare_rvv.cc to BUTIL_SRCS - CMakeLists.txt: added string_compare_rvv.cc Signed-off-by: Xiaofei Gong Signed-off-by: YuanSheng --- BUILD.bazel | 1 + CMakeLists.txt | 1 + src/butil/string_compare_rvv.cc | 105 +++++++++++++++++++++++++++++++ src/butil/strings/string_piece.h | 11 ++++ 4 files changed, 118 insertions(+) create mode 100644 src/butil/string_compare_rvv.cc diff --git a/BUILD.bazel b/BUILD.bazel index 3a5030845a..5dc5fcf726 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -189,6 +189,7 @@ BUTIL_SRCS = [ "src/butil/strings/string_number_conversions.cc", "src/butil/strings/string_split.cc", "src/butil/strings/string_piece.cc", + "src/butil/string_compare_rvv.cc", "src/butil/strings/string_util.cc", "src/butil/strings/string_util_constants.cc", "src/butil/strings/stringprintf.cc", diff --git a/CMakeLists.txt b/CMakeLists.txt index bff13e0687..85270acc1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -478,6 +478,7 @@ set(BUTIL_SOURCES ${PROJECT_SOURCE_DIR}/src/butil/strings/string_number_conversions.cc ${PROJECT_SOURCE_DIR}/src/butil/strings/string_split.cc ${PROJECT_SOURCE_DIR}/src/butil/strings/string_piece.cc + ${PROJECT_SOURCE_DIR}/src/butil/string_compare_rvv.cc ${PROJECT_SOURCE_DIR}/src/butil/strings/string_util.cc ${PROJECT_SOURCE_DIR}/src/butil/strings/string_util_constants.cc ${PROJECT_SOURCE_DIR}/src/butil/strings/stringprintf.cc diff --git a/src/butil/string_compare_rvv.cc b/src/butil/string_compare_rvv.cc new file mode 100644 index 0000000000..d01704f1c4 --- /dev/null +++ b/src/butil/string_compare_rvv.cc @@ -0,0 +1,105 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ +/** + * \file + * RISC-V Vector (RVV) accelerated memcmp/memchr for StringPiece operations. + * + * Algorithm follows glibc's RVV memcmp/memchr patterns: + * - Use vsetvli with e8/m8 for maximum throughput (hardware-adaptive VL) + * - Use vfirst.m to find first differing/matching byte (no vcpop needed) + * - Simple loop: load → compare → find first → branch + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * License); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "butil/strings/string_piece.h" + +#if defined(__riscv) && defined(__riscv_vector) +#include +#include +#include + +namespace butil { + +/** + * rvv_memcmp - RVV-accelerated byte comparison following glibc pattern. + * + * Compares two byte arrays and returns: + * < 0 if p1 < p2 + * 0 if p1 == p2 + * > 0 if p1 > p2 + * + * Uses RVV m8 with hardware-adaptive VL for maximum throughput. + */ +int rvv_memcmp(const void* p1, const void* p2, size_t n) { + const uint8_t* src1 = static_cast(p1); + const uint8_t* src2 = static_cast(p2); + size_t remaining = n; + + while (remaining > 0) { + size_t vl = __riscv_vsetvl_e8m8(remaining); + vuint8m8_t v1 = __riscv_vle8_v_u8m8(src1, vl); + vuint8m8_t v2 = __riscv_vle8_v_u8m8(src2, vl); + vbool1_t neq = __riscv_vmsne_vv_u8m8_b1(v1, v2, vl); + long first = __riscv_vfirst_m_b1(neq, vl); + + if (first >= 0) { + uint8_t b1 = src1[first]; + uint8_t b2 = src2[first]; + return (b1 < b2) ? -1 : 1; + } + + src1 += vl; + src2 += vl; + remaining -= vl; + } + return 0; +} + +/** + * rvv_memchr - RVV-accelerated byte search following glibc pattern. + * + * Searches for the first occurrence of byte 'c' in buffer 's' of length 'n'. + * Returns pointer to first match, or NULL if not found. + * + * Uses RVV m8 with hardware-adaptive VL for maximum throughput. + */ +const void* rvv_memchr(const void* s, int c, size_t n) { + const uint8_t* src = static_cast(s); + uint8_t ch = static_cast(c); + size_t remaining = n; + + while (remaining > 0) { + size_t vl = __riscv_vsetvl_e8m8(remaining); + vuint8m8_t v = __riscv_vle8_v_u8m8(src, vl); + vbool1_t eq = __riscv_vmseq_vx_u8m8_b1(v, ch, vl); + long first = __riscv_vfirst_m_b1(eq, vl); + + if (first >= 0) { + return src + first; + } + + src += vl; + remaining -= vl; + } + return nullptr; +} + +} // namespace butil + +#endif // __riscv && __riscv_vector diff --git a/src/butil/strings/string_piece.h b/src/butil/strings/string_piece.h index dbfd69e6e0..419e742da0 100644 --- a/src/butil/strings/string_piece.h +++ b/src/butil/strings/string_piece.h @@ -43,6 +43,12 @@ namespace butil { +// RVV-accelerated byte comparison and search (implemented in string_compare_rvv.cc) +#if defined(__riscv) && defined(__riscv_vector) +int rvv_memcmp(const void* p1, const void* p2, size_t n); +const void* rvv_memchr(const void* s, int c, size_t n); +#endif + template class BasicStringPiece; typedef BasicStringPiece StringPiece; typedef BasicStringPiece StringPiece16; @@ -286,6 +292,11 @@ template class BasicStringPiece { static int wordmemcmp(const value_type* p, const value_type* p2, size_type N) { +#if defined(__riscv) && defined(__riscv_vector) + if (sizeof(value_type) == 1 && N >= 16) { + return rvv_memcmp(p, p2, N); + } +#endif return STRING_TYPE::traits_type::compare(p, p2, N); } From 5fb665e6fbfc1d95e7f602d9723dd36bbdb9fa54 Mon Sep 17 00:00:00 2001 From: Felix-Gong Date: Fri, 17 Jul 2026 03:27:19 +0000 Subject: [PATCH 2/2] optimize StringPiece memcmp for RISC-V with RVV This patch adds RVV-accelerated memcmp for StringPiece operations on RISC-V 64-bit platforms. The implementation follows glibc's official RVV pattern: - e8m8 LMUL for maximum throughput - Hardware-adaptive vector length via vsetvl - vfirst.m for early-out on first difference Performance on SOPHGO SG2044 (RVV 1.0, VLEN >= 128, GCC 15.1): glibc 2.38 scalar memcmp (no RVV acceleration) as baseline. memcmp (worst-case full scan, 50000 iterations): 64 B: 20 ns -> 6 ns (3.4x) 256 B: 54 ns -> 15 ns (3.6x) 1 KB: 120 ns -> 55 ns (2.2x) 4 KB: 435 ns -> 225 ns (1.9x) 16 KB: 1968 ns -> 1258 ns (1.6x) 64 KB: 10962 ns -> 9044 ns (1.2x) 256 KB: 46893 ns -> 43108 ns (1.1x) 1 MB: 446161 ns -> 454717 ns (1.01x) Correctness: - 59/59 expanded correctness tests passed (64B - 1MB) - string_piece_unittest: 24/24 passed - test_butil: 724/725 passed (1 pre-existing StackTrace failure) - test_bvar: 64/64 passed Files changed: - src/butil/string_compare_rvv.cc (new): RVV memcmp implementation - src/butil/strings/string_piece.h: RVV dispatch in wordmemcmp (N >= 16) - BUILD.bazel: added string_compare_rvv.cc to BUTIL_SRCS - CMakeLists.txt: added string_compare_rvv.cc Signed-off-by: Xiaofei Gong Signed-off-by: YuanSheng --- BUILD.bazel | 1 - src/butil/string_compare_rvv.cc | 94 ++++++++------------------------ src/butil/strings/string_piece.h | 5 +- 3 files changed, 24 insertions(+), 76 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 5dc5fcf726..cf4560b703 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -128,7 +128,6 @@ BUTIL_SRCS = [ "src/butil/third_party/icu/icu_utf.cc", "src/butil/third_party/superfasthash/superfasthash.c", "src/butil/third_party/modp_b64/modp_b64.cc", - "src/butil/third_party/modp_b64/modp_b64_rvv.cc", "src/butil/third_party/symbolize/demangle.cc", "src/butil/third_party/symbolize/symbolize.cc", "src/butil/third_party/snappy/snappy-sinksource.cc", diff --git a/src/butil/string_compare_rvv.cc b/src/butil/string_compare_rvv.cc index d01704f1c4..3f7985e1c8 100644 --- a/src/butil/string_compare_rvv.cc +++ b/src/butil/string_compare_rvv.cc @@ -1,51 +1,32 @@ -/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ -/* vi: set expandtab shiftwidth=4 tabstop=4: */ -/** - * \file - * RISC-V Vector (RVV) accelerated memcmp/memchr for StringPiece operations. - * - * Algorithm follows glibc's RVV memcmp/memchr patterns: - * - Use vsetvli with e8/m8 for maximum throughput (hardware-adaptive VL) - * - Use vfirst.m to find first differing/matching byte (no vcpop needed) - * - Simple loop: load → compare → find first → branch - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * License); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// RVV-accelerated memcmp for StringPiece operations. +// Algorithm follows glibc's RVV memcmp pattern: +// - e8m8 LMUL with hardware-adaptive VL via vsetvl +// - vfirst.m for early-out on first mismatch #include "butil/strings/string_piece.h" #if defined(__riscv) && defined(__riscv_vector) #include -#include -#include namespace butil { -/** - * rvv_memcmp - RVV-accelerated byte comparison following glibc pattern. - * - * Compares two byte arrays and returns: - * < 0 if p1 < p2 - * 0 if p1 == p2 - * > 0 if p1 > p2 - * - * Uses RVV m8 with hardware-adaptive VL for maximum throughput. - */ int rvv_memcmp(const void* p1, const void* p2, size_t n) { const uint8_t* src1 = static_cast(p1); const uint8_t* src2 = static_cast(p2); @@ -59,9 +40,7 @@ int rvv_memcmp(const void* p1, const void* p2, size_t n) { long first = __riscv_vfirst_m_b1(neq, vl); if (first >= 0) { - uint8_t b1 = src1[first]; - uint8_t b2 = src2[first]; - return (b1 < b2) ? -1 : 1; + return static_cast(src1[first]) - static_cast(src2[first]); } src1 += vl; @@ -71,35 +50,6 @@ int rvv_memcmp(const void* p1, const void* p2, size_t n) { return 0; } -/** - * rvv_memchr - RVV-accelerated byte search following glibc pattern. - * - * Searches for the first occurrence of byte 'c' in buffer 's' of length 'n'. - * Returns pointer to first match, or NULL if not found. - * - * Uses RVV m8 with hardware-adaptive VL for maximum throughput. - */ -const void* rvv_memchr(const void* s, int c, size_t n) { - const uint8_t* src = static_cast(s); - uint8_t ch = static_cast(c); - size_t remaining = n; - - while (remaining > 0) { - size_t vl = __riscv_vsetvl_e8m8(remaining); - vuint8m8_t v = __riscv_vle8_v_u8m8(src, vl); - vbool1_t eq = __riscv_vmseq_vx_u8m8_b1(v, ch, vl); - long first = __riscv_vfirst_m_b1(eq, vl); - - if (first >= 0) { - return src + first; - } - - src += vl; - remaining -= vl; - } - return nullptr; -} - } // namespace butil #endif // __riscv && __riscv_vector diff --git a/src/butil/strings/string_piece.h b/src/butil/strings/string_piece.h index 419e742da0..b1a4ed73a5 100644 --- a/src/butil/strings/string_piece.h +++ b/src/butil/strings/string_piece.h @@ -43,10 +43,9 @@ namespace butil { -// RVV-accelerated byte comparison and search (implemented in string_compare_rvv.cc) +// RVV-accelerated byte comparison (implemented in string_compare_rvv.cc) #if defined(__riscv) && defined(__riscv_vector) -int rvv_memcmp(const void* p1, const void* p2, size_t n); -const void* rvv_memchr(const void* s, int c, size_t n); +BUTIL_EXPORT int rvv_memcmp(const void* p1, const void* p2, size_t n); #endif template class BasicStringPiece;