Skip to content

Commit 24d0b81

Browse files
committed
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 <gongxiaofei24@iscas.ac.cn> Signed-off-by: YuanSheng <yuansheng@isrc.iscas.ac.cn>
1 parent cbb4ccd commit 24d0b81

4 files changed

Lines changed: 119 additions & 0 deletions

File tree

BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ BUTIL_SRCS = [
128128
"src/butil/third_party/icu/icu_utf.cc",
129129
"src/butil/third_party/superfasthash/superfasthash.c",
130130
"src/butil/third_party/modp_b64/modp_b64.cc",
131+
"src/butil/third_party/modp_b64/modp_b64_rvv.cc",
131132
"src/butil/third_party/symbolize/demangle.cc",
132133
"src/butil/third_party/symbolize/symbolize.cc",
133134
"src/butil/third_party/snappy/snappy-sinksource.cc",
@@ -188,6 +189,7 @@ BUTIL_SRCS = [
188189
"src/butil/strings/string_number_conversions.cc",
189190
"src/butil/strings/string_split.cc",
190191
"src/butil/strings/string_piece.cc",
192+
"src/butil/string_compare_rvv.cc",
191193
"src/butil/strings/string_util.cc",
192194
"src/butil/strings/string_util_constants.cc",
193195
"src/butil/strings/stringprintf.cc",

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ set(BUTIL_SOURCES
477477
${PROJECT_SOURCE_DIR}/src/butil/strings/string_number_conversions.cc
478478
${PROJECT_SOURCE_DIR}/src/butil/strings/string_split.cc
479479
${PROJECT_SOURCE_DIR}/src/butil/strings/string_piece.cc
480+
${PROJECT_SOURCE_DIR}/src/butil/string_compare_rvv.cc
480481
${PROJECT_SOURCE_DIR}/src/butil/strings/string_util.cc
481482
${PROJECT_SOURCE_DIR}/src/butil/strings/string_util_constants.cc
482483
${PROJECT_SOURCE_DIR}/src/butil/strings/stringprintf.cc

src/butil/string_compare_rvv.cc

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2+
/* vi: set expandtab shiftwidth=4 tabstop=4: */
3+
/**
4+
* \file
5+
* RISC-V Vector (RVV) accelerated memcmp/memchr for StringPiece operations.
6+
*
7+
* Algorithm follows glibc's RVV memcmp/memchr patterns:
8+
* - Use vsetvli with e8/m8 for maximum throughput (hardware-adaptive VL)
9+
* - Use vfirst.m to find first differing/matching byte (no vcpop needed)
10+
* - Simple loop: load → compare → find first → branch
11+
*
12+
* Licensed to the Apache Software Foundation (ASF) under one
13+
* or more contributor license agreements. See the NOTICE file
14+
* distributed with this work for additional information
15+
* regarding copyright ownership. The ASF licenses this file
16+
* to you under the Apache License, Version 2.0 (the
17+
* License); you may not use this file except in compliance
18+
* with the License. You may obtain a copy of the License at
19+
*
20+
* http://www.apache.org/licenses/LICENSE-2.0
21+
*
22+
* Unless required by applicable law or agreed to in writing,
23+
* software distributed under the License is distributed on an
24+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25+
* KIND, either express or implied. See the License for the
26+
* specific language governing permissions and limitations
27+
* under the License.
28+
*/
29+
30+
#include "butil/strings/string_piece.h"
31+
32+
#if defined(__riscv) && defined(__riscv_vector)
33+
#include <riscv_vector.h>
34+
#include <stdint.h>
35+
#include <string.h>
36+
37+
namespace butil {
38+
39+
/**
40+
* rvv_memcmp - RVV-accelerated byte comparison following glibc pattern.
41+
*
42+
* Compares two byte arrays and returns:
43+
* < 0 if p1 < p2
44+
* 0 if p1 == p2
45+
* > 0 if p1 > p2
46+
*
47+
* Uses RVV m8 with hardware-adaptive VL for maximum throughput.
48+
*/
49+
int rvv_memcmp(const void* p1, const void* p2, size_t n) {
50+
const uint8_t* src1 = static_cast<const uint8_t*>(p1);
51+
const uint8_t* src2 = static_cast<const uint8_t*>(p2);
52+
size_t remaining = n;
53+
54+
while (remaining > 0) {
55+
size_t vl = __riscv_vsetvl_e8m8(remaining);
56+
vuint8m8_t v1 = __riscv_vle8_v_u8m8(src1, vl);
57+
vuint8m8_t v2 = __riscv_vle8_v_u8m8(src2, vl);
58+
vbool1_t neq = __riscv_vmsne_vv_u8m8_b1(v1, v2, vl);
59+
long first = __riscv_vfirst_m_b1(neq, vl);
60+
61+
if (first >= 0) {
62+
uint8_t b1 = src1[first];
63+
uint8_t b2 = src2[first];
64+
return (b1 < b2) ? -1 : 1;
65+
}
66+
67+
src1 += vl;
68+
src2 += vl;
69+
remaining -= vl;
70+
}
71+
return 0;
72+
}
73+
74+
/**
75+
* rvv_memchr - RVV-accelerated byte search following glibc pattern.
76+
*
77+
* Searches for the first occurrence of byte 'c' in buffer 's' of length 'n'.
78+
* Returns pointer to first match, or NULL if not found.
79+
*
80+
* Uses RVV m8 with hardware-adaptive VL for maximum throughput.
81+
*/
82+
const void* rvv_memchr(const void* s, int c, size_t n) {
83+
const uint8_t* src = static_cast<const uint8_t*>(s);
84+
uint8_t ch = static_cast<uint8_t>(c);
85+
size_t remaining = n;
86+
87+
while (remaining > 0) {
88+
size_t vl = __riscv_vsetvl_e8m8(remaining);
89+
vuint8m8_t v = __riscv_vle8_v_u8m8(src, vl);
90+
vbool1_t eq = __riscv_vmseq_vx_u8m8_b1(v, ch, vl);
91+
long first = __riscv_vfirst_m_b1(eq, vl);
92+
93+
if (first >= 0) {
94+
return src + first;
95+
}
96+
97+
src += vl;
98+
remaining -= vl;
99+
}
100+
return nullptr;
101+
}
102+
103+
} // namespace butil
104+
105+
#endif // __riscv && __riscv_vector

src/butil/strings/string_piece.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343

4444
namespace butil {
4545

46+
// RVV-accelerated byte comparison and search (implemented in string_compare_rvv.cc)
47+
#if defined(__riscv) && defined(__riscv_vector)
48+
int rvv_memcmp(const void* p1, const void* p2, size_t n);
49+
const void* rvv_memchr(const void* s, int c, size_t n);
50+
#endif
51+
4652
template <typename STRING_TYPE> class BasicStringPiece;
4753
typedef BasicStringPiece<std::string> StringPiece;
4854
typedef BasicStringPiece<string16> StringPiece16;
@@ -286,6 +292,11 @@ template <typename STRING_TYPE> class BasicStringPiece {
286292
static int wordmemcmp(const value_type* p,
287293
const value_type* p2,
288294
size_type N) {
295+
#if defined(__riscv) && defined(__riscv_vector)
296+
if (sizeof(value_type) == 1 && N >= 16) {
297+
return rvv_memcmp(p, p2, N);
298+
}
299+
#endif
289300
return STRING_TYPE::traits_type::compare(p, p2, N);
290301
}
291302

0 commit comments

Comments
 (0)