From 0bb0fe4ca4dcf14841d837791b7741b05962a001 Mon Sep 17 00:00:00 2001 From: Affan Khan Date: Wed, 1 Jul 2026 16:01:30 -0400 Subject: [PATCH] perf: speed up msgpack_extract/lookup on the map hot path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mpLookup() skipped over each map key with a full mpSkipOne() even though the key header was already decoded, and mpSkipOneD() tested fixstr — the most common element type (every map key plus short strings) — only after the whole switch and two further branches. Derive the value offset directly from the decoded key header (dropping the redundant per-key skip) and check fixstr before the switch. Output is unchanged; only the traversal is cheaper. Extracting every property of a 256-key map (per-row, us): int 176 -> 131 real 213 -> 161 text 223 -> 180 blob 198 -> 150 msgpack_extract is now faster-or-equal to json_extract/jsonb_extract across all value types (text was ~12% slower and blob slower; both are now parity/faster), a 19-26% improvement. Validated with 40k randomized extract/msgpack_to_json cases (identical to the prior implementation) and the full ctest suite (spec p4 extract + fuzz corpus). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/msgpack.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/msgpack.c b/src/msgpack.c index c467fee..305f597 100644 --- a/src/msgpack.c +++ b/src/msgpack.c @@ -404,6 +404,12 @@ static u32 mpSkipOneD(const u8 *a, u32 n, u32 i, int depth){ if( b <= 0x7f ) return i; /* negative fixint */ if( b >= 0xe0 ) return i; + /* fixstr: 0xa0-0xbf — checked early; it is the most common element type + ** (all map keys plus short strings) on the extract/lookup hot path. */ + if( (b & 0xe0) == 0xa0 ){ + u32 sz = b & 0x1f; + return (i+sz<=n)?i+sz:0; + } switch( b ){ /* fixed-length scalars */ @@ -505,11 +511,7 @@ static u32 mpSkipOneD(const u8 *a, u32 n, u32 i, int depth){ for( j=0; j= n ) return SQLITE_ERROR; kb = a[iCur]; + /* Decode the key header and derive the value offset directly, avoiding a + ** redundant mpSkipOne() over the (string) key on this hot lookup path. */ if( kb >= 0xa0 && kb <= 0xbf ){ - kLen = kb & 0x1f; kStr = (const char*)(a+iCur+1); + kLen = kb & 0x1f; kStr = (const char*)(a+iCur+1); valOff = iCur+1+kLen; } else if( kb == MP_STR8 && iCur+2 <= n ){ - kLen = a[iCur+1]; kStr = (const char*)(a+iCur+2); + kLen = a[iCur+1]; kStr = (const char*)(a+iCur+2); valOff = iCur+2+kLen; } else if( kb == MP_STR16 && iCur+3 <= n ){ - kLen = mpRead16(a+iCur+1); kStr = (const char*)(a+iCur+3); + kLen = mpRead16(a+iCur+1); kStr = (const char*)(a+iCur+3); valOff = iCur+3+kLen; } else if( kb == MP_STR32 && iCur+5 <= n ){ - kLen = mpRead32(a+iCur+1); kStr = (const char*)(a+iCur+5); + kLen = mpRead32(a+iCur+1); kStr = (const char*)(a+iCur+5); valOff = iCur+5+kLen; + } else { + valOff = mpSkipOne(a, n, iCur); /* non-string key */ } - valOff = mpSkipOne(a, n, iCur); - if( !valOff ) return SQLITE_ERROR; + if( !valOff || valOff > n ) return SQLITE_ERROR; if( kStr && (int)kLen==nKey && memcmp(kStr,zKey,(size_t)nKey)==0 ){ iCur = valOff; found = 1;