optimize base64 for riscv64 with RVV vrgather and vectorized decode#3388
Conversation
- Add RVV-accelerated base64 encode using vrgather vector indexed load for parallel table lookup (16 bytes per iteration, LMUL=4) - Add RVV-accelerated base64 decode using vectorized range comparisons and vmerge chain for classification (16 input bytes per iteration) - Integrate RVV dispatch into modp_b64.cc with compile-time selection when __riscv_vector is defined - Add modp_b64_rvv.cc to CMakeLists.txt build sources - Performance (SG2044 rv64gcv): decode 3-5x faster, up to 764 MB/s - Tested with RFC 4648 vectors and 100-round random data roundtrip Signed-off-by: Xiaofei Gong <gongxiaofei24@iscas.ac.cn> Signed-off-by: YuanSheng <yuansheng@isrc.iscas.ac.cn>
0b651dc to
0b98453
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces RISC-V Vector (RVV) accelerated base64 encode/decode paths in the existing modp_b64 implementation under src/butil/third_party/, aiming to improve performance on rv64gcv while keeping non-RVV builds unchanged via compile-time dispatch.
Changes:
- Add RVV-specific base64 encode/decode implementation (
modp_b64_rvv.{h,cc}) and dispatch to it frommodp_b64.ccwhen__riscv_vectoris available. - Include the RVV header from
modp_b64.hfor RVV builds. - Add the new RVV source file to the CMake build.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/butil/third_party/modp_b64/modp_b64.h | Includes RVV header when building with RVV enabled. |
| src/butil/third_party/modp_b64/modp_b64.cc | Adds compile-time RVV dispatch for encode/decode. |
| src/butil/third_party/modp_b64/modp_b64_rvv.h | Declares RVV-accelerated encode/decode entry points. |
| src/butil/third_party/modp_b64/modp_b64_rvv.cc | Implements RVV-accelerated encode/decode routines. |
| CMakeLists.txt | Adds the RVV implementation file to BUTIL_SOURCES. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,23 @@ | |||
| /* ... Apache 2.0 license header ... */ | |||
There was a problem hiding this comment.
Fixed. Replaced the placeholder with the full Apache-2.0 license header matching the style used in other modp_b64 files.
| break; | ||
| } | ||
|
|
||
| return (size_t)(d - (uint8_t*)dest); |
There was a problem hiding this comment.
Fixed. Added *d = '\0' before return in both the RVV fast path and scalar tail, matching modp_b64_encode() behavior.
| if (len == 0) return 0; | ||
|
|
||
| size_t i = 0; | ||
| uint8_t* p = (uint8_t*)dest; | ||
|
|
There was a problem hiding this comment.
Fixed. Added trailing '=' stripping loop before decode, matching modp_b64_decode() validation logic.
| /* Scalar fallback for remaining <16 bytes or chunks with errors */ | ||
| { | ||
| size_t remaining = len - i; | ||
| const uint8_t* s = (const uint8_t*)(src + i); | ||
| uint8_t* d = p; | ||
|
|
||
| for (size_t si = 0; si + 4 <= remaining; si += 4) { | ||
| uint8_t c0 = s[si], c1 = s[si+1], c2 = s[si+2], c3 = s[si+3]; | ||
| int d0, d1, d2, d3; | ||
|
|
||
| #define DECODE_CHAR(c, val) do { if (c >= 'A' && c <= 'Z') val = c - 'A'; else if (c >= 'a' && c <= 'z') val = c - 'a' + 26; else if (c >= '0' && c <= '9') val = c - '0' + 52; else if (c == '+') val = 62; else if (c == '/') val = 63; else return MODP_B64_RVV_ERROR; } while (0) | ||
|
|
||
| DECODE_CHAR(c0, d0); DECODE_CHAR(c1, d1); | ||
| DECODE_CHAR(c2, d2); DECODE_CHAR(c3, d3); | ||
|
|
||
| #undef DECODE_CHAR | ||
|
|
||
| *d++ = (uint8_t)((d0 << 2) | (d1 >> 4)); | ||
| *d++ = (uint8_t)(((d1 & 0x0F) << 4) | (d2 >> 2)); | ||
| *d++ = (uint8_t)(((d2 & 0x03) << 6) | d3); | ||
| } | ||
| return (size_t)(d - (uint8_t*)dest); | ||
| } |
There was a problem hiding this comment.
Fixed. Added explicit handling for leftover 2 and 3 chars after the main decode loop, producing the correct partial output (1 or 2 bytes respectively).
| ${PROJECT_SOURCE_DIR}/src/butil/third_party/modp_b64/modp_b64.cc | ||
| ${PROJECT_SOURCE_DIR}/src/butil/third_party/modp_b64/modp_b64_rvv.cc |
There was a problem hiding this comment.
Fixed. Added modp_b64_rvv.cc to the BUTIL_SRCS list in BUILD.bazel.
- Replace placeholder license in modp_b64_rvv.h with Apache-2.0 header - Add NUL terminator in rvv_modp_b64_encode scalar tail - Strip trailing '=' padding at entry of rvv_modp_b64_decode - Handle leftover 2/3 chars in decode scalar fallback - Add modp_b64_rvv.cc to BUILD.bazel BUTIL_SRCS All 5 fixes verified on real rv64gcv hardware (8/8 gtest pass). Signed-off-by: Xiaofei Gong <gongxiaofei24@iscas.ac.cn> Signed-off-by: YuanSheng <yuansheng@isrc.iscas.ac.cn>
|
LGTM |
RVV-accelerated base64 encode/decode for RISC-V 64-bit (rv64gcv).
Changes
modp_b64_rvv.cc: RVV vrgather encode + vectorized decode (80% RVV)modp_b64_rvv.h: Apache-2.0 license headermodp_b64.cc: Runtime dispatch viaUSE_RVV_BASE64BUILD.bazel: Addmodp_b64_rvv.cctoBUTIL_SRCSEncode
Scalar 6-bit index extraction + vectorized vrgather lookup (16 bytes/iter, LMUL=4).
Processes 12 input bytes → 16 output bytes per vector loop iteration.
Decode
Vectorized range classification (A-Z, a-z, 0-9, +, /) + vmerge chain.
Final 4×6→3 byte packing remains scalar (data dependencies prevent vectorization).
Performance (SG2044 rv64gcv, 10-round avg, -O2)
Tested
brpc_base64_unittestall passed