Hi,
This patch adds an RVV-accelerated, bounded NUL-byte scan for pq_getmsgstring() and pq_getmsgrawstring(). Both functions currently use strlen() and then verify that the terminating NUL byte is within the message boundary. On RVV builds, the patch replaces that scan with a common helper that searches for the NUL byte using RVV intrinsics. The vector length is limited to the number of bytes remaining in the message before every load. Therefore, the helper does not read beyond the PostgreSQL message boundary. If no NUL byte is found within that boundary, the existing protocol-violation error path is preserved. The RVV implementation is enabled only when __riscv_vector is defined and <riscv_vector.h> is available. Non-RVV builds retain the existing strlen() path. The public APIs and the character-set conversion behavior of pq_getmsgstring() remain unchanged. The patch is intended for PostgreSQL master. Full validation was performed on PostgreSQL commit: 86f7c82cf1023e3599f40f939727791a7090cd44 Correctness and portability results: - git apply and whitespace checks passed - RISC-V baseline with GCC 14: 240/240 regression tests passed - RISC-V patched build with GCC 14: 240/240 passed - RISC-V patched build with GCC 15: 240/240 passed - RISC-V patched build with Clang 17: 240/240 passed - x86_64 fallback with GCC 11.4: 240/240 passed - x86_64 fallback with Clang 14: 240/240 passed - guard-page, missing-NUL, length, and unaligned-boundary tests passed with GCC 14, GCC 15, and Clang 17 The RISC-V builds used: CFLAGS="-O2 -g -march=rv64gcv -mabi=lp64d" Disassembly of the GCC 14 and GCC 15 builds confirmed that the generated code contains vsetvli, vle8.v, vmseq, and vfirst.m instructions. Clang 17 generated the same RVV operations, although the target system's objdump displayed some of them as .insn. End-to-end performance was measured using GCC 15.1 with a 4 KiB simple-query workload, one client and one thread. The test contained 24 baseline/patched pairs, with each member running for 8 seconds. Execution order alternated between baseline-first and patched-first. Results: - 23 of 24 pairs favored the patched build - mean TPS improvement: 3.52% - median TPS improvement: 3.59% - bootstrap median 95% confidence interval: [3.17%, 3.89%] - no failed transactions in the 48 measurement runs A separate microbenchmark covering 55 string-length and alignment conditions showed a median 3.34x speedup over strlen() and a median 3.97x speedup over memchr() for the RVV helper. The performance measurements were performed on one RVV system, and the end-to-end result is specific to the 4 KiB simple-query, single-client workload. It should not be assumed to represent every PostgreSQL workload or RISC-V implementation. No new SQL regression test is included because the change does not alter SQL-visible behavior. The existing regression suite passed on both the RVV and scalar fallback paths, while the message-boundary and missing-NUL cases were covered by the targeted tests described above. No user-facing documentation change is needed because this is an internal, architecture-specific optimization. Feedback on the bounded scanning approach and the placement of the RISC-V-specific implementation would be appreciated. Regards, Hongyan Wang
From 890872e8dafeab3080022876d6c601d80624deaa Mon Sep 17 00:00:00 2001 From: wanghongyan <[email protected]> Date: Fri, 18 Sep 2026 14:44:06 +0800 Subject: [PATCH v1] add validated RVV pq_getmsgstring candidate Co-authored-by: Ni Jincheng <[email protected]> Co-authored-by: Yuansheng <[email protected]> --- src/backend/libpq/pqformat.c | 59 ++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c index 8b41aa4f1cb..4a1dd5a45c2 100644 --- a/src/backend/libpq/pqformat.c +++ b/src/backend/libpq/pqformat.c @@ -79,6 +79,13 @@ #include "port/pg_bswap.h" #include "varatt.h" +#if defined(__riscv_vector) && defined(__has_include) +#if __has_include(<riscv_vector.h>) +#include <riscv_vector.h> +#define USE_RISCV_VECTOR_NUL_SCAN +#endif +#endif + /* -------------------------------- * pq_beginmessage - initialize for sending a message @@ -567,6 +574,10 @@ pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes) return p; } +#ifdef USE_RISCV_VECTOR_NUL_SCAN +static int pq_find_nul_rvv(const char *str, int maxlen); +#endif + /* -------------------------------- * pq_getmsgstring - get a null-terminated text string (with conversion) * @@ -587,8 +598,12 @@ pq_getmsgstring(StringInfo msg) * have a trailing null byte. But check we found a null inside the * message. */ +#ifdef USE_RISCV_VECTOR_NUL_SCAN + slen = pq_find_nul_rvv(str, msg->len - msg->cursor); +#else slen = strlen(str); - if (msg->cursor + slen >= msg->len) +#endif + if (slen < 0 || msg->cursor + slen >= msg->len) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid string in message"))); @@ -597,6 +612,42 @@ pq_getmsgstring(StringInfo msg) return pg_client_to_server(str, slen); } +#ifdef USE_RISCV_VECTOR_NUL_SCAN +/* + * Find a null byte without reading beyond the message boundary. Keeping the + * vector length bounded by maxlen avoids the object and page-boundary + * over-read that an unbounded vector strlen implementation could perform. + */ +static int +pq_find_nul_rvv(const char *str, int maxlen) +{ + size_t offset = 0; + size_t remaining; + + if (maxlen <= 0) + return -1; + remaining = maxlen; + + while (remaining > 0) + { + size_t vl = __riscv_vsetvl_e8m8(remaining); + vuint8m8_t bytes; + vbool1_t zeroes; + long position; + + bytes = __riscv_vle8_v_u8m8((const uint8_t *) str + offset, vl); + zeroes = __riscv_vmseq_vx_u8m8_b1(bytes, 0, vl); + position = __riscv_vfirst_m_b1(zeroes, vl); + if (position >= 0) + return (int) (offset + (size_t) position); + offset += vl; + remaining -= vl; + } + + return -1; +} +#endif + /* -------------------------------- * pq_getmsgrawstring - get a null-terminated text string - NO conversion * @@ -616,8 +667,12 @@ pq_getmsgrawstring(StringInfo msg) * have a trailing null byte. But check we found a null inside the * message. */ +#ifdef USE_RISCV_VECTOR_NUL_SCAN + slen = pq_find_nul_rvv(str, msg->len - msg->cursor); +#else slen = strlen(str); - if (msg->cursor + slen >= msg->len) +#endif + if (slen < 0 || msg->cursor + slen >= msg->len) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid string in message"))); -- 2.43.0
