From 994c1af62994430d83218632e72727c0ab071f5c Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Thu, 10 Sep 2026 20:54:22 +0800 Subject: [PATCH 01/10] =?UTF-8?q?shm:=20ResolveRef/GetByRef/SetPartByRef?= =?UTF-8?q?=20=E4=B8=8D=E7=A9=BF=E9=80=8F=20Ptr=EF=BC=8Cgrow=20=E5=86=99?= =?UTF-8?q?=20forwarding=20tombstone=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 ++ src/durable_abi.c | 30 +++++++++ src/kvspace.c | 117 +++++++++++++++++++++++++++++++---- src/kvspace_shm.h | 12 ++++ tests/test_handle_cache.c | 126 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 tests/test_handle_cache.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 566bba2..2679e4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,4 +50,9 @@ if(KVSPACE_BUILD_TESTS) target_include_directories(test_art_scan PRIVATE src) target_link_libraries(test_art_scan PRIVATE kvspace-c) add_test(NAME art_scan COMMAND test_art_scan) + + add_executable(test_handle_cache tests/test_handle_cache.c) + target_include_directories(test_handle_cache PRIVATE src) + target_link_libraries(test_handle_cache PRIVATE kvspace-c) + add_test(NAME handle_cache COMMAND test_handle_cache) endif() diff --git a/src/durable_abi.c b/src/durable_abi.c index 9b5d224..38db655 100644 --- a/src/durable_abi.c +++ b/src/durable_abi.c @@ -68,6 +68,36 @@ int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, return 0; } +int kvspaceResolveRef(void *h, const char *key, kvspaceRef_t *ref) { + return kvspaceShmResolveRef((kvspace_t *)h, key, ref); +} + +int kvspaceGetByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, + uint8_t **out, uint32_t *out_len) { + int32_t len = 0; + uint8_t *d = kvspaceShmGetByRef((kvspace_t *)h, ref, key_fallback, &len); + if (!d || len <= 0) { + *out = NULL; + *out_len = 0; + return 0; + } + *out = d; + *out_len = (uint32_t)len; + return 0; +} + +int kvspaceSetPartByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, + uint32_t offset, const uint8_t *buf, uint32_t buf_len, + char *err, uint32_t err_cap) { + if (kvspaceShmSetPartByRef((kvspace_t *)h, ref, key_fallback, offset, buf, + buf_len) != 0) { + if (err && err_cap) + snprintf(err, err_cap, "kvspace: set-part-by-ref failed"); + return 1; + } + return 0; +} + /* 指令边界回收读借用池:SHM 常驻映射,借用恒有效,no-op。 */ void kvspaceReadReset(void *h) { (void)h; } diff --git a/src/kvspace.c b/src/kvspace.c index 151ae71..960cde7 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -35,7 +35,8 @@ enum { ART_N4 = 0, ART_N16 = 1, ART_N48 = 2, - ART_N256 = 3 }; + ART_N256 = 3, + ART_MOVED = 255 }; typedef struct { uint8_t type, prefix[ART_PREFIX_MAX], prefix_len; @@ -383,33 +384,49 @@ static int32_t art_child(kvspace_t *kv, void *n, uint8_t b) { } /* ---- art_search ---- */ -static art_hdr_t *art_search(kvspace_t *kv, int32_t nid, const uint8_t *key, - int klen) { +static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key, + int klen) { if (nid < 0 || !key) - return NULL; + return -1; int d = 0; while (nid >= 0) { art_hdr_t *h = art_hdr(kv, nid); - if (!h) - return NULL; + if (!h || h->type == ART_MOVED) + return -1; if (h->prefix_len) { int s = pfx_shared(h->prefix, h->prefix_len, key + d, klen - d); if (s != h->prefix_len) { if (d + s < klen) - return NULL; + return -1; if (s < h->prefix_len) - return NULL; + return -1; } d += h->prefix_len; if (d > klen) - return NULL; + return -1; } if (d == klen) - return h->has_value ? h : NULL; + return h->has_value ? nid : -1; nid = art_child(kv, h, key[d]); d++; } - return NULL; + return -1; +} +static art_hdr_t *art_search(kvspace_t *kv, int32_t nid, const uint8_t *key, + int klen) { + int32_t id = art_find(kv, nid, key, klen); + return id < 0 ? NULL : art_hdr(kv, id); +} +static int32_t art_follow(kvspace_t *kv, int32_t id) { + for (int i = 0; i < 8 && id >= 0; i++) { + art_hdr_t *h = art_hdr(kv, id); + if (!h) + return -1; + if (h->type != ART_MOVED) + return id; + id = (int32_t)h->box_offset; + } + return -1; } /* ---- node create ---- */ @@ -523,6 +540,9 @@ static int32_t art_grow(kvspace_t *kv, void *on) { x->children[j] = tc; } } + oh->type = ART_MOVED; + oh->has_value = 0; + oh->box_offset = (uint64_t)(uint32_t)nid; return nid; } @@ -1292,6 +1312,81 @@ uint8_t *kvspaceShmGet(kvspace_t *kv, const char *key, int resolve, return raw; } +static int32_t ref_live(kvspace_t *kv, kvspaceRef_t *ref) { + int32_t id = art_follow(kv, (int32_t)ref->block_id); + if (id >= 0) + ref->block_id = (uint32_t)id; + return id; +} + +int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { + if (!kv || !key || !ref) + return -1; + if (kv_sync(kv) != 0) + return -1; + int32_t id = + art_find(kv, kv->hdr->art_root, (const uint8_t *)key, (int)strlen(key)); + if (id < 0) + return -1; + ref->block_id = (uint32_t)id; + ref->gen = 0; + return 0; +} + +uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref, + const char *key_fallback, int32_t *ol) { + if (!kv || !ref || !ol) + return NULL; + *ol = 0; + if (kv_sync(kv) != 0) + return NULL; + int32_t id = ref_live(kv, ref); + art_hdr_t *h = id >= 0 ? art_hdr(kv, id) : NULL; + if (h && h->has_value) { + uint8_t *raw; + int32_t rl; + if (read_tlv(kv, h->box_offset, &raw, &rl) == 0) { + *ol = rl; + return raw; + } + } + if (!key_fallback) + return NULL; + uint8_t *raw = kvspaceShmGet(kv, key_fallback, 0, ol); + if (raw) + kvspaceShmResolveRef(kv, key_fallback, ref); + return raw; +} + +int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref, + const char *key_fallback, uint32_t offset, + const uint8_t *buf, uint32_t buf_len) { + if (!kv || !ref || !buf) + return -1; + if (kv_sync(kv) != 0) + return -1; + int32_t id = ref_live(kv, ref); + art_hdr_t *h = id >= 0 ? art_hdr(kv, id) : NULL; + if (!h || !h->has_value) { + if (!key_fallback) + return -1; + int32_t rl = 0; + uint8_t *d = kvspaceShmGet(kv, key_fallback, 0, &rl); + if (!d || offset + buf_len > (uint32_t)rl) + return -1; + memcpy(d + offset, buf, buf_len); + kvspaceShmResolveRef(kv, key_fallback, ref); + return 0; + } + uint8_t *raw; + int32_t rl; + if (read_tlv(kv, h->box_offset, &raw, &rl) < 0 || + offset + buf_len > (uint32_t)rl) + return -1; + memcpy(raw + offset, buf, buf_len); + return 0; +} + /* ── 值/索引分离(方案2,对齐 kvspace-durable backend.rs) ────────── */ /* 坐标段工具见 xvalue.c:kvspaceCoordIsCoord / kvspaceParseCoord / * kvspaceCoordCmp。 */ diff --git a/src/kvspace_shm.h b/src/kvspace_shm.h index 66e0fd1..daf5329 100644 --- a/src/kvspace_shm.h +++ b/src/kvspace_shm.h @@ -15,6 +15,11 @@ typedef struct kvspace kvspace_t; +typedef struct { + uint32_t block_id; + uint32_t gen; +} kvspaceRef_t; + /* ================================================================ * 生命周期 * ================================================================ */ @@ -32,6 +37,13 @@ void kvspaceShmClose(kvspace_t *kv); uint8_t *kvspaceShmGet(kvspace_t *kv, const char *key, int resolve, int32_t *out_len); +int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref); +uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref, + const char *key_fallback, int32_t *out_len); +int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref, + const char *key_fallback, uint32_t offset, + const uint8_t *buf, uint32_t buf_len); + // Set: 写入 value(TLV 编码的字节)。总是穿透 link 写入 target。 int kvspaceShmSet(kvspace_t *kv, const char *key, const uint8_t *val, int32_t val_len); diff --git a/tests/test_handle_cache.c b/tests/test_handle_cache.c new file mode 100644 index 0000000..e7c3e65 --- /dev/null +++ b/tests/test_handle_cache.c @@ -0,0 +1,126 @@ +#define _GNU_SOURCE +#include "kvspace_shm.h" +#include "xvalue.h" +#include +#include +#include +#include +#include + +#define DATA_SIZE (8UL * 64 * 64) +#define CHECK(c) \ + do { \ + if (!(c)) { \ + failures++; \ + fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #c); \ + } \ + } while (0) + +static int failures; + +static int set_i64(kvspace_t *kv, const char *key, int64_t v) { + uint8_t *tlv; + int32_t n = kvspaceXvalueNewInt64(&v, 1, &tlv); + if (n < 0) + return -1; + int rc = kvspaceShmSet(kv, key, tlv, n); + free(tlv); + return rc; +} + +static int64_t get_i64(const uint8_t *d, int32_t len) { + if (!d || len <= 0) + return -999; + xvalue_head_t h = kvspaceXvalueDecodeHead(d, len); + return h.raw_len == 8 ? kvspaceXvalueRawInt64(h.raw) : -999; +} + +static uint64_t nsec(void) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return (uint64_t)t.tv_sec * 1000000000ull + (uint64_t)t.tv_nsec; +} + +int main(void) { + char dir[] = "/tmp/kvs-ref-XXXXXX"; + if (!mkdtemp(dir)) + return 1; + char path[256]; + snprintf(path, sizeof path, "%s/s", dir); + kvspace_t *kv = kvspaceShmOpen(path, DATA_SIZE); + if (!kv) { + fprintf(stderr, "open failed\n"); + return 1; + } + + CHECK(set_i64(kv, "/a", 1) == 0); + kvspaceRef_t ref; + CHECK(kvspaceShmResolveRef(kv, "/a", &ref) == 0); + int32_t len = 0; + uint8_t *d = kvspaceShmGetByRef(kv, &ref, "/a", &len); + CHECK(get_i64(d, len) == 1); + uint32_t id0 = ref.block_id; + + CHECK(set_i64(kv, "/a", 42) == 0); + d = kvspaceShmGetByRef(kv, &ref, "/a", &len); + CHECK(get_i64(d, len) == 42); + CHECK(ref.block_id == id0); + + CHECK(set_i64(kv, "/n", 7) == 0); + kvspaceRef_t nr; + CHECK(kvspaceShmResolveRef(kv, "/n", &nr) == 0); + for (int i = 0; i < 5; i++) { + char k[8]; + snprintf(k, sizeof k, "/n%d", i); + CHECK(set_i64(kv, k, i) == 0); + } + d = kvspaceShmGetByRef(kv, &nr, "/n", &len); + CHECK(get_i64(d, len) == 7); + + kvspaceShmDel(kv, "/a"); + len = 0; + d = kvspaceShmGetByRef(kv, &ref, NULL, &len); + CHECK(d == NULL); + CHECK(set_i64(kv, "/a", 9) == 0); + d = kvspaceShmGetByRef(kv, &ref, "/a", &len); + CHECK(get_i64(d, len) == 9); + + CHECK(set_i64(kv, "/p", 100) == 0); + kvspaceRef_t pr; + CHECK(kvspaceShmResolveRef(kv, "/p", &pr) == 0); + d = kvspaceShmGet(kv, "/p", 0, &len); + CHECK(d && len > 8); + xvalue_head_t hh = kvspaceXvalueDecodeHead(d, len); + int64_t nv = 200; + uint8_t raw[8]; + for (int i = 0; i < 8; i++) + raw[i] = (uint8_t)((uint64_t)nv >> (8 * i)); + CHECK(kvspaceShmSetPartByRef(kv, &pr, "/p", + (uint32_t)kvspaceXvalueHeadLen(&hh), raw, + 8) == 0); + d = kvspaceShmGetByRef(kv, &pr, "/p", &len); + CHECK(get_i64(d, len) == 200); + + const int N = 200000; + CHECK(set_i64(kv, "/hot", 1) == 0); + kvspaceRef_t hr; + CHECK(kvspaceShmResolveRef(kv, "/hot", &hr) == 0); + uint64_t t0 = nsec(); + for (int i = 0; i < N; i++) { + int32_t l = 0; + kvspaceShmGet(kv, "/hot", 0, &l); + } + uint64_t t1 = nsec(); + for (int i = 0; i < N; i++) { + int32_t l = 0; + kvspaceShmGetByRef(kv, &hr, "/hot", &l); + } + uint64_t t2 = nsec(); + double ns_get = (double)(t1 - t0) / N; + double ns_ref = (double)(t2 - t1) / N; + printf("Get %.1f ns/op GetByRef %.1f ns/op ratio %.2f\n", ns_get, ns_ref, + ns_ref > 0 ? ns_get / ns_ref : 0); + + kvspaceShmClose(kv); + return failures ? 1 : 0; +} From 8c80ffc83971598f4a06a619ae9ff276389a6446 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 12:32:37 +0800 Subject: [PATCH 02/10] =?UTF-8?q?perf:=20ART=20=E7=BC=93=E5=AD=98=E5=8F=B6?= =?UTF-8?q?=E5=AD=90=E7=88=B6=E8=8A=82=E7=82=B9=EF=BC=8CResolveRef/GetByRe?= =?UTF-8?q?f=20=E4=BB=8E=20parent=20=E5=8F=96=E5=85=84=E5=BC=9F=EF=BC=88#2?= =?UTF-8?q?68=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 112 ++++++++++++++++++++++++++++++++------ tests/test_handle_cache.c | 53 ++++++++++++++++++ 2 files changed, 149 insertions(+), 16 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index 960cde7..30e0c12 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -19,6 +19,7 @@ #define KVS_MAGIC "kvspace-c.v2" #define ART_PREFIX_MAX 10 +#define ART_HINT_MAX 256 #define ART_NODE_MAX_SZ 2112 #define ART_SLAB_INIT (256UL * 1024 * 1024) /* blocks_init picks 30-bit ids only if the initial pool holds > 8191 blocks; @@ -135,6 +136,9 @@ struct kvspace { uint8_t *sbo_data; watch_t watches[WATCH_TABLE_SZ]; pthread_mutex_t wlock; + int32_t art_hint; + int art_hint_d; + uint8_t art_hint_pfx[ART_HINT_MAX]; }; /* ---- shm region ---- */ @@ -383,12 +387,10 @@ static int32_t art_child(kvspace_t *kv, void *n, uint8_t b) { return -1; } -/* ---- art_search ---- */ -static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key, - int klen) { - if (nid < 0 || !key) - return -1; - int d = 0; +static int32_t art_follow(kvspace_t *kv, int32_t id); + +static int32_t art_walk(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen, + int d, int32_t *par, int *pard) { while (nid >= 0) { art_hdr_t *h = art_hdr(kv, nid); if (!h || h->type == ART_MOVED) @@ -407,11 +409,64 @@ static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key, } if (d == klen) return h->has_value ? nid : -1; + if (par) + *par = nid; + if (pard) + *pard = d; nid = art_child(kv, h, key[d]); d++; } return -1; } + +/* ---- art_search ---- */ +static int32_t art_find2(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen, + int32_t *par, int *pard) { + if (nid < 0 || !key) + return -1; + int32_t origin = nid; + int d = 0; + int32_t parent = -1; + int parent_d = 0; + int hinted = 0; + if (kv->hdr && nid == kv->hdr->art_root && kv->art_hint >= 0 && + kv->art_hint_d > 0 && kv->art_hint_d < klen && + kv->art_hint_d <= ART_HINT_MAX && + memcmp(key, kv->art_hint_pfx, (size_t)kv->art_hint_d) == 0) { + int32_t hid = art_follow(kv, kv->art_hint); + art_hdr_t *hh = hid >= 0 ? art_hdr(kv, hid) : NULL; + if (hh && hh->type != ART_MOVED) { + int32_t cid = art_child(kv, hh, key[kv->art_hint_d]); + if (cid >= 0) { + parent = hid; + parent_d = kv->art_hint_d; + nid = cid; + d = kv->art_hint_d + 1; + hinted = 1; + } + } + } + int32_t id = art_walk(kv, nid, key, klen, d, &parent, &parent_d); + if (id < 0 && hinted) { + parent = -1; + parent_d = 0; + id = art_walk(kv, origin, key, klen, 0, &parent, &parent_d); + } + if (id >= 0 && parent >= 0 && parent_d > 0 && parent_d <= ART_HINT_MAX) { + kv->art_hint = parent; + kv->art_hint_d = parent_d; + memcpy(kv->art_hint_pfx, key, (size_t)parent_d); + } + if (par) + *par = parent; + if (pard) + *pard = parent_d; + return id; +} +static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key, + int klen) { + return art_find2(kv, nid, key, klen, NULL, NULL); +} static art_hdr_t *art_search(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen) { int32_t id = art_find(kv, nid, key, klen); @@ -993,6 +1048,7 @@ kvspace_t *kvspaceShmOpen(const char *path, size_t data_size) { if (!kv) return NULL; kv->r_art.fd = kv->r_head.fd = kv->r_data.fd = -1; + kv->art_hint = -1; /* validate before O_EXCL create: no empty file left behind */ bool created = false; @@ -1312,11 +1368,27 @@ uint8_t *kvspaceShmGet(kvspace_t *kv, const char *key, int resolve, return raw; } -static int32_t ref_live(kvspace_t *kv, kvspaceRef_t *ref) { +/* gen>0: block_id is parent at depth gen; key must share that prefix. */ +static int32_t ref_leaf(kvspace_t *kv, kvspaceRef_t *ref, const char *key) { int32_t id = art_follow(kv, (int32_t)ref->block_id); - if (id >= 0) - ref->block_id = (uint32_t)id; - return id; + if (id < 0) + return -1; + ref->block_id = (uint32_t)id; + if (ref->gen == 0) + return id; + if (!key) + return -1; + int klen = (int)strlen(key); + int d = (int)ref->gen; + if (d <= 0 || d >= klen) + return -1; + art_hdr_t *h = art_hdr(kv, id); + if (!h || h->type == ART_MOVED) + return -1; + int32_t cid = art_child(kv, h, (uint8_t)key[d]); + if (cid < 0) + return -1; + return art_walk(kv, cid, (const uint8_t *)key, klen, d + 1, NULL, NULL); } int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { @@ -1324,12 +1396,20 @@ int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { return -1; if (kv_sync(kv) != 0) return -1; - int32_t id = - art_find(kv, kv->hdr->art_root, (const uint8_t *)key, (int)strlen(key)); + int32_t parent = -1; + int parent_d = 0; + int klen = (int)strlen(key); + int32_t id = art_find2(kv, kv->hdr->art_root, (const uint8_t *)key, klen, + &parent, &parent_d); if (id < 0) return -1; - ref->block_id = (uint32_t)id; - ref->gen = 0; + if (parent >= 0 && parent_d > 0) { + ref->block_id = (uint32_t)parent; + ref->gen = (uint32_t)parent_d; + } else { + ref->block_id = (uint32_t)id; + ref->gen = 0; + } return 0; } @@ -1340,7 +1420,7 @@ uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref, *ol = 0; if (kv_sync(kv) != 0) return NULL; - int32_t id = ref_live(kv, ref); + int32_t id = ref_leaf(kv, ref, key_fallback); art_hdr_t *h = id >= 0 ? art_hdr(kv, id) : NULL; if (h && h->has_value) { uint8_t *raw; @@ -1365,7 +1445,7 @@ int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref, return -1; if (kv_sync(kv) != 0) return -1; - int32_t id = ref_live(kv, ref); + int32_t id = ref_leaf(kv, ref, key_fallback); art_hdr_t *h = id >= 0 ? art_hdr(kv, id) : NULL; if (!h || !h->has_value) { if (!key_fallback) diff --git a/tests/test_handle_cache.c b/tests/test_handle_cache.c index e7c3e65..0804fb3 100644 --- a/tests/test_handle_cache.c +++ b/tests/test_handle_cache.c @@ -101,6 +101,38 @@ int main(void) { d = kvspaceShmGetByRef(kv, &pr, "/p", &len); CHECK(get_i64(d, len) == 200); + for (int i = 0; i < 64; i++) { + char k[16]; + snprintf(k, sizeof k, "/sib/%02d", i); + CHECK(set_i64(kv, k, i + 100) == 0); + } + for (int i = 0; i < 64; i++) { + char k[16]; + snprintf(k, sizeof k, "/sib/%02d", i); + int32_t l = 0; + d = kvspaceShmGet(kv, k, 0, &l); + CHECK(get_i64(d, l) == i + 100); + } + CHECK(set_i64(kv, "/other", 1) == 0); + { + int32_t l = 0; + d = kvspaceShmGet(kv, "/sib/00", 0, &l); + CHECK(get_i64(d, l) == 100); + d = kvspaceShmGet(kv, "/other", 0, &l); + CHECK(get_i64(d, l) == 1); + kvspaceRef_t sr; + CHECK(set_i64(kv, "/frm/a", 1) == 0); + CHECK(set_i64(kv, "/frm/i", 2) == 0); + CHECK(set_i64(kv, "/frm/n", 3) == 0); + CHECK(kvspaceShmResolveRef(kv, "/frm/a", &sr) == 0); + d = kvspaceShmGetByRef(kv, &sr, "/frm/i", &l); + CHECK(get_i64(d, l) == 2); + d = kvspaceShmGetByRef(kv, &sr, "/frm/n", &l); + CHECK(get_i64(d, l) == 3); + d = kvspaceShmGetByRef(kv, &sr, "/frm/a", &l); + CHECK(get_i64(d, l) == 1); + } + const int N = 200000; CHECK(set_i64(kv, "/hot", 1) == 0); kvspaceRef_t hr; @@ -121,6 +153,27 @@ int main(void) { printf("Get %.1f ns/op GetByRef %.1f ns/op ratio %.2f\n", ns_get, ns_ref, ns_ref > 0 ? ns_get / ns_ref : 0); + const char *fk[] = {"/frm/a", "/frm/i", "/frm/n"}; + const int NF = 3; + const int NR = 50000; + uint64_t t3 = nsec(); + for (int r = 0; r < NR; r++) { + int32_t l = 0; + kvspaceShmGet(kv, fk[r % NF], 0, &l); + } + uint64_t t4 = nsec(); + kvspaceRef_t sr; + CHECK(kvspaceShmResolveRef(kv, fk[0], &sr) == 0); + uint64_t t5 = nsec(); + for (int r = 0; r < NR; r++) { + int32_t l = 0; + kvspaceShmGetByRef(kv, &sr, fk[r % NF], &l); + } + uint64_t t6 = nsec(); + printf("Get sibling %.1f ns/op GetByRef sibling %.1f ns/op ratio %.2f\n", + (double)(t4 - t3) / NR, (double)(t6 - t5) / NR, + (t6 - t5) > 0 ? (double)(t4 - t3) / (double)(t6 - t5) : 0); + kvspaceShmClose(kv); return failures ? 1 : 0; } From d9a0de7ab203f3bd7cda099d5f14ceca8d8f5bdb Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 12:51:46 +0800 Subject: [PATCH 03/10] =?UTF-8?q?perf:=20ResolveRef=20=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=8F=B6=E5=AD=90=E4=B8=8E=E7=88=B6=E8=8A=82?= =?UTF-8?q?=E7=82=B9=EF=BC=8CGetByRef=20=E6=8C=89=20gen=20=E9=80=89?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 47 ++++++--------------------------------- src/kvspace_shm.h | 2 ++ tests/test_handle_cache.c | 23 ++++++++++++++----- 3 files changed, 26 insertions(+), 46 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index 30e0c12..916816b 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -19,7 +19,6 @@ #define KVS_MAGIC "kvspace-c.v2" #define ART_PREFIX_MAX 10 -#define ART_HINT_MAX 256 #define ART_NODE_MAX_SZ 2112 #define ART_SLAB_INIT (256UL * 1024 * 1024) /* blocks_init picks 30-bit ids only if the initial pool holds > 8191 blocks; @@ -136,9 +135,6 @@ struct kvspace { uint8_t *sbo_data; watch_t watches[WATCH_TABLE_SZ]; pthread_mutex_t wlock; - int32_t art_hint; - int art_hint_d; - uint8_t art_hint_pfx[ART_HINT_MAX]; }; /* ---- shm region ---- */ @@ -424,39 +420,9 @@ static int32_t art_find2(kvspace_t *kv, int32_t nid, const uint8_t *key, int kle int32_t *par, int *pard) { if (nid < 0 || !key) return -1; - int32_t origin = nid; - int d = 0; int32_t parent = -1; int parent_d = 0; - int hinted = 0; - if (kv->hdr && nid == kv->hdr->art_root && kv->art_hint >= 0 && - kv->art_hint_d > 0 && kv->art_hint_d < klen && - kv->art_hint_d <= ART_HINT_MAX && - memcmp(key, kv->art_hint_pfx, (size_t)kv->art_hint_d) == 0) { - int32_t hid = art_follow(kv, kv->art_hint); - art_hdr_t *hh = hid >= 0 ? art_hdr(kv, hid) : NULL; - if (hh && hh->type != ART_MOVED) { - int32_t cid = art_child(kv, hh, key[kv->art_hint_d]); - if (cid >= 0) { - parent = hid; - parent_d = kv->art_hint_d; - nid = cid; - d = kv->art_hint_d + 1; - hinted = 1; - } - } - } - int32_t id = art_walk(kv, nid, key, klen, d, &parent, &parent_d); - if (id < 0 && hinted) { - parent = -1; - parent_d = 0; - id = art_walk(kv, origin, key, klen, 0, &parent, &parent_d); - } - if (id >= 0 && parent >= 0 && parent_d > 0 && parent_d <= ART_HINT_MAX) { - kv->art_hint = parent; - kv->art_hint_d = parent_d; - memcpy(kv->art_hint_pfx, key, (size_t)parent_d); - } + int32_t id = art_walk(kv, nid, key, klen, 0, &parent, &parent_d); if (par) *par = parent; if (pard) @@ -1048,7 +1014,6 @@ kvspace_t *kvspaceShmOpen(const char *path, size_t data_size) { if (!kv) return NULL; kv->r_art.fd = kv->r_head.fd = kv->r_data.fd = -1; - kv->art_hint = -1; /* validate before O_EXCL create: no empty file left behind */ bool created = false; @@ -1403,12 +1368,14 @@ int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { &parent, &parent_d); if (id < 0) return -1; + ref->block_id = (uint32_t)id; + ref->gen = 0; if (parent >= 0 && parent_d > 0) { - ref->block_id = (uint32_t)parent; - ref->gen = (uint32_t)parent_d; + ref->parent_id = (uint32_t)parent; + ref->depth = (uint32_t)parent_d; } else { - ref->block_id = (uint32_t)id; - ref->gen = 0; + ref->parent_id = 0; + ref->depth = 0; } return 0; } diff --git a/src/kvspace_shm.h b/src/kvspace_shm.h index daf5329..05081aa 100644 --- a/src/kvspace_shm.h +++ b/src/kvspace_shm.h @@ -18,6 +18,8 @@ typedef struct kvspace kvspace_t; typedef struct { uint32_t block_id; uint32_t gen; + uint32_t parent_id; + uint32_t depth; } kvspaceRef_t; /* ================================================================ diff --git a/tests/test_handle_cache.c b/tests/test_handle_cache.c index 0804fb3..f43be25 100644 --- a/tests/test_handle_cache.c +++ b/tests/test_handle_cache.c @@ -125,12 +125,18 @@ int main(void) { CHECK(set_i64(kv, "/frm/i", 2) == 0); CHECK(set_i64(kv, "/frm/n", 3) == 0); CHECK(kvspaceShmResolveRef(kv, "/frm/a", &sr) == 0); - d = kvspaceShmGetByRef(kv, &sr, "/frm/i", &l); - CHECK(get_i64(d, l) == 2); - d = kvspaceShmGetByRef(kv, &sr, "/frm/n", &l); - CHECK(get_i64(d, l) == 3); + CHECK(sr.depth > 0 && sr.parent_id != 0); d = kvspaceShmGetByRef(kv, &sr, "/frm/a", &l); CHECK(get_i64(d, l) == 1); + { + kvspaceRef_t pr = { sr.parent_id, sr.depth, 0, 0 }; + d = kvspaceShmGetByRef(kv, &pr, "/frm/i", &l); + CHECK(get_i64(d, l) == 2); + CHECK(get_i64(d, l) != 1); + d = kvspaceShmGetByRef(kv, &pr, "/frm/n", &l); + CHECK(get_i64(d, l) == 3); + CHECK(get_i64(d, l) != 1); + } } const int N = 200000; @@ -162,12 +168,17 @@ int main(void) { kvspaceShmGet(kv, fk[r % NF], 0, &l); } uint64_t t4 = nsec(); - kvspaceRef_t sr; + kvspaceRef_t sr, pref; CHECK(kvspaceShmResolveRef(kv, fk[0], &sr) == 0); + CHECK(sr.depth > 0); + pref.block_id = sr.parent_id; + pref.gen = sr.depth; + pref.parent_id = 0; + pref.depth = 0; uint64_t t5 = nsec(); for (int r = 0; r < NR; r++) { int32_t l = 0; - kvspaceShmGetByRef(kv, &sr, fk[r % NF], &l); + kvspaceShmGetByRef(kv, &pref, fk[r % NF], &l); } uint64_t t6 = nsec(); printf("Get sibling %.1f ns/op GetByRef sibling %.1f ns/op ratio %.2f\n", From 025a43fc51160fa6ef591b8ee5950b59b92ebef4 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 14:08:55 +0800 Subject: [PATCH 04/10] =?UTF-8?q?perf:=20ART=20=E7=9B=AE=E5=BD=95=E7=A5=96?= =?UTF-8?q?=E5=85=88=E4=BE=9B=20GetByRef=20=E5=8F=96=E5=85=84=E5=BC=9F?= =?UTF-8?q?=E5=8F=B6=E5=AD=90=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 54 +++++++++++++++++++++++++++++++++++++-- tests/test_handle_cache.c | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index 916816b..f40dc02 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -433,6 +433,42 @@ static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen) { return art_find2(kv, nid, key, klen, NULL, NULL); } + +/* Ancestor covering last '/'. depth is after this node's prefix, ready for + * art_child(key[depth]) — siblings in the same dir share that node. */ +static int32_t art_dir_ancestor(kvspace_t *kv, int32_t nid, const uint8_t *key, + int klen, int last_slash, int *out_d) { + int d = 0; + if (last_slash <= 0 || nid < 0) + return -1; + while (nid >= 0) { + art_hdr_t *h = art_hdr(kv, nid); + if (!h || h->type == ART_MOVED) + return -1; + int entry_d = d; + if (h->prefix_len) { + int s = pfx_shared(h->prefix, h->prefix_len, key + d, klen - d); + if (s != h->prefix_len) + return -1; + d += h->prefix_len; + if (d > klen) + return -1; + } + if (entry_d <= last_slash && last_slash < d) { + *out_d = d; + return nid; + } + if (d == last_slash + 1) { + *out_d = d; + return nid; + } + if (d > last_slash || d >= klen) + return -1; + nid = art_child(kv, h, key[d]); + d++; + } + return -1; +} static art_hdr_t *art_search(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen) { int32_t id = art_find(kv, nid, key, klen); @@ -1345,7 +1381,7 @@ static int32_t ref_leaf(kvspace_t *kv, kvspaceRef_t *ref, const char *key) { return -1; int klen = (int)strlen(key); int d = (int)ref->gen; - if (d <= 0 || d >= klen) + if (d < 0 || d >= klen) return -1; art_hdr_t *h = art_hdr(kv, id); if (!h || h->type == ART_MOVED) @@ -1370,7 +1406,16 @@ int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { return -1; ref->block_id = (uint32_t)id; ref->gen = 0; - if (parent >= 0 && parent_d > 0) { + int slash = klen - 1; + while (slash > 0 && key[slash] != '/') + slash--; + int dir_d = 0; + int32_t dn = art_dir_ancestor(kv, kv->hdr->art_root, (const uint8_t *)key, + klen, slash, &dir_d); + if (dn >= 0) { + ref->parent_id = (uint32_t)dn; + ref->depth = (uint32_t)dir_d; + } else if (parent >= 0 && parent_d > 0) { ref->parent_id = (uint32_t)parent; ref->depth = (uint32_t)parent_d; } else { @@ -1397,6 +1442,9 @@ uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref, return raw; } } + /* gen>0: parent walk miss — do not full-Get; caller falls back. */ + if (ref->gen != 0) + return NULL; if (!key_fallback) return NULL; uint8_t *raw = kvspaceShmGet(kv, key_fallback, 0, ol); @@ -1415,6 +1463,8 @@ int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref, int32_t id = ref_leaf(kv, ref, key_fallback); art_hdr_t *h = id >= 0 ? art_hdr(kv, id) : NULL; if (!h || !h->has_value) { + if (ref->gen != 0) + return -1; if (!key_fallback) return -1; int32_t rl = 0; diff --git a/tests/test_handle_cache.c b/tests/test_handle_cache.c index f43be25..5c40a71 100644 --- a/tests/test_handle_cache.c +++ b/tests/test_handle_cache.c @@ -137,6 +137,58 @@ int main(void) { CHECK(get_i64(d, l) == 3); CHECK(get_i64(d, l) != 1); } + CHECK(set_i64(kv, "/map/[1]", 11) == 0); + CHECK(set_i64(kv, "/map/[2]", 22) == 0); + CHECK(set_i64(kv, "/map/[3]", 33) == 0); + { + kvspaceRef_t mr; + CHECK(kvspaceShmResolveRef(kv, "/map/[1]", &mr) == 0); + CHECK(mr.depth > 0 && mr.parent_id != 0); + kvspaceRef_t mp = { mr.parent_id, mr.depth, 0, 0 }; + d = kvspaceShmGetByRef(kv, &mp, "/map/[2]", &l); + CHECK(get_i64(d, l) == 22); + CHECK(get_i64(d, l) != 11); + d = kvspaceShmGetByRef(kv, &mp, "/map/[3]", &l); + CHECK(get_i64(d, l) == 33); + CHECK(get_i64(d, l) != 11); + } + { + kvspaceRef_t sr0; + CHECK(kvspaceShmResolveRef(kv, "/sib/00", &sr0) == 0); + CHECK(sr0.depth > 0); + kvspaceRef_t sp = { sr0.parent_id, sr0.depth, 0, 0 }; + for (int i = 0; i < 64; i++) { + char k[16]; + snprintf(k, sizeof k, "/sib/%02d", i); + int32_t l2 = 0; + d = kvspaceShmGetByRef(kv, &sp, k, &l2); + CHECK(get_i64(d, l2) == i + 100); + } + } + { + const int N = 200; + for (int i = 0; i < N; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]/h/[%d]", + (int)((unsigned)i * 2654435761u % 100003)); + CHECK(set_i64(kv, k, i) == 0); + } + char k0[128]; + snprintf(k0, sizeof k0, "/vthread/vt0/[0]/h/[%d]", 0); + kvspaceRef_t hr; + CHECK(kvspaceShmResolveRef(kv, k0, &hr) == 0); + CHECK(hr.depth > 0 && hr.parent_id != 0); + kvspaceRef_t hp = { hr.parent_id, hr.depth, 0, 0 }; + for (int i = 0; i < N; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]/h/[%d]", + (int)((unsigned)i * 2654435761u % 100003)); + int32_t l2 = 0; + d = kvspaceShmGetByRef(kv, &hp, k, &l2); + CHECK(get_i64(d, l2) == i); + CHECK(get_i64(d, l2) != i + 1); + } + } } const int N = 200000; From 64a1a5e89f6bd030acde9ac8b6ed9c920443d82d Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 15:30:34 +0800 Subject: [PATCH 05/10] =?UTF-8?q?perf:=20ResolveRef=20=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=20walk=20=E5=90=8C=E6=97=B6=E8=AE=B0=E4=B8=8B=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E7=A5=96=E5=85=88=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index f40dc02..6f194f8 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -434,12 +434,13 @@ static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key, return art_find2(kv, nid, key, klen, NULL, NULL); } -/* Ancestor covering last '/'. depth is after this node's prefix, ready for - * art_child(key[depth]) — siblings in the same dir share that node. */ -static int32_t art_dir_ancestor(kvspace_t *kv, int32_t nid, const uint8_t *key, - int klen, int last_slash, int *out_d) { +/* One walk: leaf plus the ancestor covering last '/' (after that node's prefix). */ +static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, + int klen, int last_slash, int32_t *dirn, int *dird) { int d = 0; - if (last_slash <= 0 || nid < 0) + int32_t dir = -1; + int dd = 0; + if (nid < 0 || !key) return -1; while (nid >= 0) { art_hdr_t *h = art_hdr(kv, nid); @@ -454,16 +455,18 @@ static int32_t art_dir_ancestor(kvspace_t *kv, int32_t nid, const uint8_t *key, if (d > klen) return -1; } - if (entry_d <= last_slash && last_slash < d) { - *out_d = d; - return nid; + if (last_slash > 0 && + ((entry_d <= last_slash && last_slash < d) || d == last_slash + 1)) { + dir = nid; + dd = d; } - if (d == last_slash + 1) { - *out_d = d; - return nid; + if (d == klen) { + if (dirn) + *dirn = dir; + if (dird) + *dird = dd; + return h->has_value ? nid : -1; } - if (d > last_slash || d >= klen) - return -1; nid = art_child(kv, h, key[d]); d++; } @@ -1397,27 +1400,21 @@ int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { return -1; if (kv_sync(kv) != 0) return -1; - int32_t parent = -1; - int parent_d = 0; int klen = (int)strlen(key); - int32_t id = art_find2(kv, kv->hdr->art_root, (const uint8_t *)key, klen, - &parent, &parent_d); - if (id < 0) - return -1; - ref->block_id = (uint32_t)id; - ref->gen = 0; int slash = klen - 1; while (slash > 0 && key[slash] != '/') slash--; int dir_d = 0; - int32_t dn = art_dir_ancestor(kv, kv->hdr->art_root, (const uint8_t *)key, - klen, slash, &dir_d); - if (dn >= 0) { + int32_t dn = -1; + int32_t id = art_find_dir(kv, kv->hdr->art_root, (const uint8_t *)key, klen, + slash, &dn, &dir_d); + if (id < 0) + return -1; + ref->block_id = (uint32_t)id; + ref->gen = 0; + if (dn >= 0 && dir_d > 0) { ref->parent_id = (uint32_t)dn; ref->depth = (uint32_t)dir_d; - } else if (parent >= 0 && parent_d > 0) { - ref->parent_id = (uint32_t)parent; - ref->depth = (uint32_t)parent_d; } else { ref->parent_id = 0; ref->depth = 0; From 8e1d01780955a2230dc61f48db3058ef46cd28c7 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 15:52:05 +0800 Subject: [PATCH 06/10] =?UTF-8?q?perf:=20ResolveRef=20=E6=8C=89=E6=88=90?= =?UTF-8?q?=E5=91=98=20=C2=B7=20=E4=B8=8E=E8=B7=AF=E5=BE=84=20/=20?= =?UTF-8?q?=E5=8F=96=E7=9B=AE=E5=BD=95=E7=A5=96=E5=85=88=EF=BC=88#268?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 40 +++++++++++++++++----- tests/test_handle_cache.c | 71 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index 6f194f8..3a1a9c8 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -434,9 +434,33 @@ static int32_t art_find(kvspace_t *kv, int32_t nid, const uint8_t *key, return art_find2(kv, nid, key, klen, NULL, NULL); } -/* One walk: leaf plus the ancestor covering last '/' (after that node's prefix). */ +/* Last path '/' or kvlang member '·' (U+00B7, utf-8 C2 B7). */ +static int last_key_sep(const uint8_t *key, int klen, int *seplen) { + int slash = -1, mid = -1; + for (int i = 0; i < klen; i++) { + if (key[i] == '/') + slash = i; + if (i + 1 < klen && key[i] == 0xC2 && key[i + 1] == 0xB7) + mid = i; + } + if (mid > slash) { + *seplen = 2; + return mid; + } + if (slash > 0) { + *seplen = 1; + return slash; + } + *seplen = 0; + return -1; +} + +/* One walk: leaf plus the ancestor covering last '/' or '·' (after that + * node's prefix). First match: node at the separator, not a deeper unique + * prefix that swallowed it. */ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, - int klen, int last_slash, int32_t *dirn, int *dird) { + int klen, int last_sep, int seplen, int32_t *dirn, + int *dird) { int d = 0; int32_t dir = -1; int dd = 0; @@ -455,8 +479,9 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, if (d > klen) return -1; } - if (last_slash > 0 && - ((entry_d <= last_slash && last_slash < d) || d == last_slash + 1)) { + if (dir < 0 && last_sep > 0 && seplen > 0 && + ((entry_d <= last_sep && last_sep < d) || + d == last_sep + seplen)) { dir = nid; dd = d; } @@ -1401,13 +1426,12 @@ int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { if (kv_sync(kv) != 0) return -1; int klen = (int)strlen(key); - int slash = klen - 1; - while (slash > 0 && key[slash] != '/') - slash--; + int seplen = 0; + int sep = last_key_sep((const uint8_t *)key, klen, &seplen); int dir_d = 0; int32_t dn = -1; int32_t id = art_find_dir(kv, kv->hdr->art_root, (const uint8_t *)key, klen, - slash, &dn, &dir_d); + sep, seplen, &dn, &dir_d); if (id < 0) return -1; ref->block_id = (uint32_t)id; diff --git a/tests/test_handle_cache.c b/tests/test_handle_cache.c index 5c40a71..310d614 100644 --- a/tests/test_handle_cache.c +++ b/tests/test_handle_cache.c @@ -189,6 +189,77 @@ int main(void) { CHECK(get_i64(d, l2) != i + 1); } } + /* kvlang map slots use member '·', not '/'. */ + { + const int N = 200; +#define MSEP "\xC2\xB7" + for (int i = 0; i < N; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]" MSEP "h" MSEP "[%d]", + (int)((unsigned)i * 2654435761u % 100003)); + CHECK(set_i64(kv, k, i) == 0); + } + char k0[128]; + snprintf(k0, sizeof k0, "/vthread/vt0/[0]" MSEP "h" MSEP "[0]"); + kvspaceRef_t hr; + CHECK(kvspaceShmResolveRef(kv, k0, &hr) == 0); + CHECK(hr.depth > 0 && hr.parent_id != 0); + kvspaceRef_t hp = { hr.parent_id, hr.depth, 0, 0 }; + int hits = 0; + for (int i = 0; i < N; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]" MSEP "h" MSEP "[%d]", + (int)((unsigned)i * 2654435761u % 100003)); + int32_t l2 = 0; + d = kvspaceShmGetByRef(kv, &hp, k, &l2); + if (get_i64(d, l2) == i) + hits++; + CHECK(get_i64(d, l2) == i); + CHECK(get_i64(d, l2) != i + 1); + } + CHECK(hits == N); + for (int i = 0; i < 32; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]" MSEP "arr" MSEP "[%d]", i); + CHECK(set_i64(kv, k, i + 50) == 0); + } + snprintf(k0, sizeof k0, "/vthread/vt0/[0]" MSEP "arr" MSEP "[0]"); + CHECK(kvspaceShmResolveRef(kv, k0, &hr) == 0); + hp.block_id = hr.parent_id; + hp.gen = hr.depth; + for (int i = 0; i < 32; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]" MSEP "arr" MSEP "[%d]", i); + int32_t l2 = 0; + d = kvspaceShmGetByRef(kv, &hp, k, &l2); + CHECK(get_i64(d, l2) == i + 50); + } + /* kv.get/kv.set map slots are unbracketed: base·42 */ + for (int i = 0; i < N; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]" MSEP "m" MSEP "%d", + (int)((unsigned)i * 2654435761u % 100003)); + CHECK(set_i64(kv, k, i + 1000) == 0); + } + snprintf(k0, sizeof k0, "/vthread/vt0/[0]" MSEP "m" MSEP "0"); + CHECK(kvspaceShmResolveRef(kv, k0, &hr) == 0); + CHECK(hr.depth > 0 && hr.parent_id != 0); + hp.block_id = hr.parent_id; + hp.gen = hr.depth; + hits = 0; + for (int i = 0; i < N; i++) { + char k[128]; + snprintf(k, sizeof k, "/vthread/vt0/[0]" MSEP "m" MSEP "%d", + (int)((unsigned)i * 2654435761u % 100003)); + int32_t l2 = 0; + d = kvspaceShmGetByRef(kv, &hp, k, &l2); + if (get_i64(d, l2) == i + 1000) + hits++; + CHECK(get_i64(d, l2) == i + 1000); + } + CHECK(hits == N); +#undef MSEP + } } const int N = 200000; From 6b28fe8c75d729f6c263dea8c29956e2c459a282 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 17:38:02 +0800 Subject: [PATCH 07/10] =?UTF-8?q?perf:=20=E7=9B=AE=E5=BD=95=E7=A5=96?= =?UTF-8?q?=E5=85=88=E5=8F=AA=E8=AE=B0=E5=88=86=E9=9A=94=E7=AC=A6=E5=90=8E?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E5=AD=97=E8=8A=82=E6=B7=B1=E5=BA=A6=EF=BC=88?= =?UTF-8?q?#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index 3a1a9c8..95d3652 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -470,7 +470,6 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, art_hdr_t *h = art_hdr(kv, nid); if (!h || h->type == ART_MOVED) return -1; - int entry_d = d; if (h->prefix_len) { int s = pfx_shared(h->prefix, h->prefix_len, key + d, klen - d); if (s != h->prefix_len) @@ -479,9 +478,10 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, if (d > klen) return -1; } + /* Exact depth of first byte after the separator — not a node + * whose prefix already ate unique last-component bytes. */ if (dir < 0 && last_sep > 0 && seplen > 0 && - ((entry_d <= last_sep && last_sep < d) || - d == last_sep + seplen)) { + d == last_sep + seplen) { dir = nid; dd = d; } From 5367e416895d7057f3c7b2496a70b53d88b379f5 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 17:38:40 +0800 Subject: [PATCH 08/10] =?UTF-8?q?perf:=20=E5=89=8D=E7=BC=80=E8=B6=8A?= =?UTF-8?q?=E8=BF=87=E5=88=86=E9=9A=94=E7=AC=A6=E6=97=B6=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=A5=96=E5=85=88=E5=81=9C=E5=9C=A8=E4=B8=8A=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E7=82=B9=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index 95d3652..565e8e6 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -462,14 +462,15 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen, int last_sep, int seplen, int32_t *dirn, int *dird) { int d = 0; - int32_t dir = -1; - int dd = 0; + int32_t dir = -1, prev = -1; + int dd = 0, prev_d = 0; if (nid < 0 || !key) return -1; while (nid >= 0) { art_hdr_t *h = art_hdr(kv, nid); if (!h || h->type == ART_MOVED) return -1; + int entry_d = d; if (h->prefix_len) { int s = pfx_shared(h->prefix, h->prefix_len, key + d, klen - d); if (s != h->prefix_len) @@ -478,12 +479,16 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, if (d > klen) return -1; } - /* Exact depth of first byte after the separator — not a node - * whose prefix already ate unique last-component bytes. */ - if (dir < 0 && last_sep > 0 && seplen > 0 && - d == last_sep + seplen) { - dir = nid; - dd = d; + if (dir < 0 && last_sep > 0 && seplen > 0) { + int want = last_sep + seplen; + if (d == want) { + dir = nid; + dd = d; + } else if (entry_d < want && want < d && prev >= 0) { + /* Prefix ate past the last component: stay on the parent. */ + dir = prev; + dd = prev_d; + } } if (d == klen) { if (dirn) @@ -492,6 +497,8 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, *dird = dd; return h->has_value ? nid : -1; } + prev = nid; + prev_d = d; nid = art_child(kv, h, key[d]); d++; } From 3d3e044133043fbce1ef8fe6106da67db70d8488 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sat, 12 Sep 2026 17:45:04 +0800 Subject: [PATCH 09/10] =?UTF-8?q?revert:=20=E6=81=A2=E5=A4=8D=E5=88=86?= =?UTF-8?q?=E9=9A=94=E7=AC=A6=E5=A4=84=E8=AE=B0=E5=BD=95=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=A5=96=E5=85=88=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/kvspace.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/kvspace.c b/src/kvspace.c index 565e8e6..3a1a9c8 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -462,8 +462,8 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, int klen, int last_sep, int seplen, int32_t *dirn, int *dird) { int d = 0; - int32_t dir = -1, prev = -1; - int dd = 0, prev_d = 0; + int32_t dir = -1; + int dd = 0; if (nid < 0 || !key) return -1; while (nid >= 0) { @@ -479,16 +479,11 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, if (d > klen) return -1; } - if (dir < 0 && last_sep > 0 && seplen > 0) { - int want = last_sep + seplen; - if (d == want) { - dir = nid; - dd = d; - } else if (entry_d < want && want < d && prev >= 0) { - /* Prefix ate past the last component: stay on the parent. */ - dir = prev; - dd = prev_d; - } + if (dir < 0 && last_sep > 0 && seplen > 0 && + ((entry_d <= last_sep && last_sep < d) || + d == last_sep + seplen)) { + dir = nid; + dd = d; } if (d == klen) { if (dirn) @@ -497,8 +492,6 @@ static int32_t art_find_dir(kvspace_t *kv, int32_t nid, const uint8_t *key, *dird = dd; return h->has_value ? nid : -1; } - prev = nid; - prev_d = d; nid = art_child(kv, h, key[d]); d++; } From 125f16e65415f7336b034d622b2ea19b9e26b75b Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sun, 13 Sep 2026 21:40:12 +0800 Subject: [PATCH 10/10] =?UTF-8?q?shm:=20ResolveRef=20=E5=85=88=20memset=20?= =?UTF-8?q?ref=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 未写字段保持 0,避免 runtime 把栈垃圾当 parent_id。 --- src/kvspace.c | 1 + src/kvspace_shm.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/kvspace.c b/src/kvspace.c index 71d3573..6b9446e 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -1426,6 +1426,7 @@ static int32_t ref_leaf(kvspace_t *kv, kvspaceRef_t *ref, const char *key) { int kvspaceShmResolveRef(kvspace_t *kv, const char *key, kvspaceRef_t *ref) { if (!kv || !key || !ref) return -1; + memset(ref, 0, sizeof(*ref)); if (kv_sync(kv) != 0) return -1; int klen = (int)strlen(key); diff --git a/src/kvspace_shm.h b/src/kvspace_shm.h index 05081aa..987262b 100644 --- a/src/kvspace_shm.h +++ b/src/kvspace_shm.h @@ -15,6 +15,7 @@ typedef struct kvspace kvspace_t; +/* 对齐 kvspace/include/kvspace/kvspace.h:叶子 + 目录祖先。 */ typedef struct { uint32_t block_id; uint32_t gen;