Skip to content

Commit e4e4fc3

Browse files
authored
Merge pull request #312 from array2d/feat/issue-309-runtime-hotpath
runtime: 执行循环热路径免 malloc + vthread 状态写入收敛
2 parents 947b99b + 4a99942 commit e4e4fc3

10 files changed

Lines changed: 299 additions & 94 deletions

File tree

runtime/src/keytree.c

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ char *kvlangKeytreeStack(const char *root) {
2020
return r;
2121
}
2222

23+
/* 栈缓冲版:把 root(去尾 /)拷进 buf 并补 '/',返回写入长度(含尾 /);cap 不足返回 0。
24+
* 读参热路径复用调用方缓冲,免每操作一次 malloc。 */
25+
size_t kvlangKeytreeStackBuf(const char *root, char *buf, size_t cap) {
26+
size_t n = strlen(root);
27+
while (n > 0 && root[n - 1] == '/') n--;
28+
if (n + 2 > cap) return 0;
29+
memcpy(buf, root, n);
30+
buf[n] = '/';
31+
buf[n + 1] = 0;
32+
return n + 1;
33+
}
34+
2335
char *kvlangKeytreeFrameRoot(const char *pc) {
2436
const char *last = NULL;
2537
for (const char *p = pc; (p = strstr(p, "/[")) != NULL; p += 2) last = p;
@@ -30,6 +42,13 @@ char *kvlangKeytreeFrameRoot(const char *pc) {
3042
return r;
3143
}
3244

45+
/* 帧根长度(末个 "/[" 之前),无分配:循环每步据之判断帧是否变化,免一次 malloc。 */
46+
size_t kvlangKeytreeFrameRootLen(const char *pc) {
47+
const char *last = NULL;
48+
for (const char *p = pc; (p = strstr(p, "/[")) != NULL; p += 2) last = p;
49+
return last ? (size_t)(last - pc) : 0;
50+
}
51+
3352
static char *trim_right_join(const char *root, const char *suffix) {
3453
size_t n = strlen(root);
3554
while (n > 0 && root[n - 1] == '/') n--;
@@ -59,14 +78,15 @@ char *kvlangKeytreeFrameAt(const char *vtid, int depth) {
5978
}
6079

6180
int kvlangKeytreeFrameNum(const char *path) {
62-
kvlangStrbuf_t vtid_b; kvlangStrbufInit(&vtid_b);
63-
const char *vtid = kvlangKeytreeVtidFromPc(path, &vtid_b);
64-
if (!vtid[0]) keytree_die("FrameNum: not a vthread path: %s", path);
65-
kvlangStrbuf_t pfx; kvlangStrbufInit(&pfx);
66-
kvlangKeytreeVthread(vtid, &pfx);
67-
kvlangStrbufPutc(&pfx, '/');
68-
if (strncmp(path, pfx.p, pfx.len) != 0) keytree_die("FrameNum: not a vthread path: %s", path);
69-
const char *rest = path + pfx.len;
81+
/* 免分配:/vthread/<vtid>/[d][/...] —— 直接定位 vtid 后的帧坐标,不构造前缀串。 */
82+
const size_t rl = sizeof(VTHREAD_ROOT) - 1;
83+
if (strncmp(path, VTHREAD_ROOT PATH_SEP, rl + 1) != 0)
84+
keytree_die("FrameNum: not a vthread path: %s", path);
85+
const char *vtid = path + rl + 1;
86+
const char *slash = strchr(vtid, '/');
87+
if (!slash || slash == vtid)
88+
keytree_die("FrameNum: not a vthread path: %s", path);
89+
const char *rest = slash + 1;
7090
if (rest[0] != '[') keytree_die("FrameNum: no frame coord in %s", path);
7191
const char *end = strchr(rest, ']');
7292
if (!end) keytree_die("FrameNum: unterminated frame coord in %s", path);
@@ -90,7 +110,6 @@ int kvlangKeytreeFrameNum(const char *path) {
90110
} else {
91111
keytree_die("FrameNum: expected /[irseq,j] after [d] in %s", path);
92112
}
93-
kvlangStrbufFree(&vtid_b); kvlangStrbufFree(&pfx);
94113
return (int)n;
95114
}
96115

runtime/src/kvcpu.c

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -121,23 +121,25 @@ static int irseq_of(const kvlangParam_t *p) {
121121
}
122122

123123
/* 函数内跳转:只改 PC 的 [irseq],帧不变。目标非法 → RuntimeError,返回 -1。 */
124-
static int jump_to(kvlangKv_t *kv, const char *vtid, const char *pc,
125-
const kvlangParam_t *target, const char *op) {
124+
static int jump_to(kvlangFrame_t *f, const kvlangParam_t *target, const char *op) {
126125
int irseq = irseq_of(target);
127126
if (irseq < 0) {
128127
char msg[256];
129128
snprintf(msg, sizeof msg,
130129
"RuntimeError: %s target is not an int64 irseq: %s (kind=%s)",
131130
op, target->name ? target->name : "",
132131
kvlangXvalueKind(&target->val));
133-
kvlangVthreadSetError(kv, vtid, pc, msg);
132+
kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg);
134133
return -1;
135134
}
136-
char *fr = kvlangKeytreeFrameRoot(pc);
135+
/* 帧根优先用主循环缓存的(br/goto 帧不变,借它免每次 malloc)。 */
136+
char *owned = NULL;
137+
const char *fr = f->frame_root;
138+
if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; }
137139
char *np = kvlangKeytreeIrseqPc(fr, irseq);
138-
free(fr);
139-
kvlangVthreadSet(kv, vtid, np, "running");
140-
kvlangLogDebug("[%s] %s → %s", vtid, op, np);
140+
free(owned);
141+
kvlangVthreadAdvance(f, np, "running");
142+
kvlangLogDebug("[%s] %s → %s", f->vtid, op, np);
141143
free(np);
142144
return 0;
143145
}
@@ -789,7 +791,7 @@ int kvlangCtlCall(kvlangFrame_t *f) {
789791
char *sub = handle_call(f->kv, f->pc, f->inst);
790792
if (!sub)
791793
return -1;
792-
kvlangVthreadSet(f->kv, f->vtid, sub, "running");
794+
kvlangVthreadAdvance(f, sub, "running");
793795
free(sub);
794796
return 0;
795797
}
@@ -802,7 +804,7 @@ int kvlangCtlReturn(kvlangFrame_t *f) {
802804
kvlangVthreadSetDone(f->kv, f->vtid, "ok");
803805
return 0;
804806
}
805-
kvlangVthreadSet(f->kv, f->vtid, parent, "running");
807+
kvlangVthreadAdvance(f, parent, "running");
806808
free(parent);
807809
return 0;
808810
}
@@ -816,7 +818,7 @@ int kvlangCtlGoto(kvlangFrame_t *f) {
816818
kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg);
817819
return -1;
818820
}
819-
return jump_to(f->kv, f->vtid, f->pc, &inst->reads[0], OP_GOTO);
821+
return jump_to(f, &inst->reads[0], OP_GOTO);
820822
}
821823

822824
int kvlangCtlBr(kvlangFrame_t *f) {
@@ -852,7 +854,7 @@ int kvlangCtlBr(kvlangFrame_t *f) {
852854
}
853855
bool taken = kvlangScalarI64(kvlangXvalueScalar(&cond)) != 0;
854856
kvlangXvalueFree(&cond);
855-
return jump_to(f->kv, f->vtid, f->pc, &inst->reads[taken ? 1 : 2], OP_BR);
857+
return jump_to(f, &inst->reads[taken ? 1 : 2], OP_BR);
856858
}
857859

858860
/* 动态调用:以运行时得到的 funckey 在当前 vthread 造一次 OP_CALL(不新开 vid),
@@ -1027,28 +1029,40 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
10271029
char *cur = strdup(pc);
10281030
char *cur_frame = NULL,
10291031
*cur_funcdir = NULL; /* 帧不变时 funcdir 只读一次,供缓存键 */
1032+
int cur_depth = 0; /* 帧深度,随 cur_frame 一起缓存 */
10301033
int rc = 0;
1031-
/* status 跨轮携带:尾部 VthreadGet 已连 pc 一并取出,下轮直接复用,省掉背靠背重读
1032-
* (vthread 记录在两次 get 之间不被改写;status 串由 ValueString 自持,跨 ReadReset 存活)。 */
1034+
/* status:每步从 kvspace 回读的**源值**,不当缓存用(下轮开头即重读)。
1035+
* 同时借给本步的 kvlangFrame_t.status_known,供 Advance 判断是否真要写 status。 */
10331036
char *status = NULL;
1034-
{
1035-
char *pcv = NULL;
1036-
kvlangVthreadGet(kv, vtid, &pcv, &status);
1037-
free(pcv);
1038-
}
10391037
for (;;) {
10401038
/* 指令边界:回收上条指令执行期借出的读池(cache 指令的读参已 Materialize 自持,不受影响)。
10411039
* durable 惰性写不再清池,全靠此处回收;shm 常驻映射侧为 no-op。 */
10421040
kvlangKvReadReset(kv);
1041+
/* 状态门(spec [[执行模型]] 步骤 1):每指令边界从 kvspace 回读 ‥status,只读 1 个 key
1042+
* (走 vtid key 缓存)。不得改用进程内副本——那是状态的第二份事实源:外部把 ‥status
1043+
* 置 paused/error(暂停、接管、终止)必须在下一步就被看见,且 kvspace 始终等于真实状态。 */
1044+
free(status);
1045+
status = NULL;
1046+
kvlangVthreadStatusGet(kv, vtid, &status);
10431047
if (!status ||
10441048
(strcmp(status, "init") != 0 && strcmp(status, "running") != 0 &&
10451049
strcmp(status, "wait") != 0)) {
10461050
break;
10471051
}
1048-
free(status);
1049-
status = NULL;
10501052

1051-
int depth = kvlangKeytreeFrameNum(cur);
1053+
/* 帧变(call/return/goto 跨帧)才重算根/深度/funcdir;帧内每步只比较前缀,免分配。 */
1054+
size_t frlen = kvlangKeytreeFrameRootLen(cur);
1055+
if (!cur_frame || strlen(cur_frame) != frlen ||
1056+
strncmp(cur_frame, cur, frlen) != 0) {
1057+
free(cur_frame);
1058+
cur_frame = malloc(frlen + 1);
1059+
memcpy(cur_frame, cur, frlen);
1060+
cur_frame[frlen] = 0;
1061+
free(cur_funcdir);
1062+
cur_funcdir = read_seglib(kv, cur_frame);
1063+
cur_depth = kvlangKeytreeFrameNum(cur);
1064+
}
1065+
int depth = cur_depth;
10521066
if (depth > MAX_STACK_DEPTH) {
10531067
char msg[256];
10541068
snprintf(msg, sizeof msg,
@@ -1059,13 +1073,7 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
10591073
break;
10601074
}
10611075

1062-
char *fr = kvlangKeytreeFrameRoot(cur);
1063-
if (!cur_frame || strcmp(cur_frame, fr) != 0) {
1064-
free(cur_frame);
1065-
cur_frame = strdup(fr);
1066-
free(cur_funcdir);
1067-
cur_funcdir = read_seglib(kv, fr);
1068-
}
1076+
const char *fr = cur_frame; /* 借用(随 cur_frame 缓存同步),循环内无需 free */
10691077
const char *lastc = NULL;
10701078
for (const char *p = cur; (p = strstr(p, "/[")) != NULL; p += 2)
10711079
lastc = p;
@@ -1086,7 +1094,6 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
10861094
snprintf(msg, sizeof msg, "decode: %s", err);
10871095
kvlangVthreadSetError(kv, vtid, cur, msg);
10881096
free(link_base);
1089-
free(fr);
10901097
rc = -1;
10911098
break;
10921099
}
@@ -1114,7 +1121,6 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
11141121
snprintf(msg, sizeof msg, "RuntimeError: no instruction at %s",
11151122
cur);
11161123
kvlangVthreadSetError(kv, vtid, cur, msg);
1117-
free(fr);
11181124
if (tmp_owned)
11191125
kvlangRwirInstFree(&tmp);
11201126
rc = -1;
@@ -1123,9 +1129,12 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
11231129

11241130
int exec_err = 0;
11251131
char *yield = NULL;
1132+
char *fb_pc = NULL; /* 本指令的 PC 回传槽(见 kvlangFrame_t),免写后回读 */
11261133
if (inst->op_id >= 0) {
11271134
/* 单表派发:native 算子与 control/copy 同居 myrwircaps,op_id 直查一跳到底。 */
1128-
kvlangFrame_t f = {kv, vtid, cur, inst, &yield};
1135+
kvlangFrame_t f = {kv, vtid, cur, inst, &yield, &fb_pc};
1136+
f.frame_root = cur_frame; /* 复用循环缓存的帧根,免每步重算 */
1137+
f.status_known = status; /* 本步开始前读到的 ‥status(源值,借用) */
11291138
exec_err = kvlangBuiltinNative(&f);
11301139
if (exec_err == 0 && yield) {
11311140
/* native(vthread·run return 模式)冒泡一个子 vthread 的 rwir pc 给上层驱动。
@@ -1134,9 +1143,9 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
11341143
*out_pc = yield;
11351144
else
11361145
free(yield);
1137-
free(fr);
11381146
if (tmp_owned)
11391147
kvlangRwirInstFree(&tmp);
1148+
free(fb_pc);
11401149
free(cur);
11411150
free(cur_frame);
11421151
free(cur_funcdir);
@@ -1153,9 +1162,9 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
11531162
if (exec_err == 0 && mode == KVMODE_RETURN) {
11541163
if (out_pc)
11551164
*out_pc = strdup(cur);
1156-
free(fr);
11571165
if (tmp_owned)
11581166
kvlangRwirInstFree(&tmp);
1167+
free(fb_pc);
11591168
free(cur);
11601169
free(cur_frame);
11611170
free(cur_funcdir);
@@ -1181,24 +1190,31 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode,
11811190
}
11821191
ci.writes = inst->writes;
11831192
kvlangFrame_t cf = {kv, vtid, cur, &ci, NULL};
1193+
cf.status_known = status;
11841194
exec_err = kvlangCtlCall(&cf);
11851195
free(ci.opcode);
11861196
free(ci.reads[0].name);
11871197
free(ci.reads);
11881198
}
11891199

11901200
if (exec_err != 0) {
1191-
free(fr);
11921201
if (tmp_owned)
11931202
kvlangRwirInstFree(&tmp);
1203+
free(fb_pc);
11941204
rc = -1;
11951205
break;
11961206
}
11971207

11981208
char *newpc = NULL;
1199-
kvlangVthreadGet(kv, vtid, &newpc,
1200-
&status); /* status 连 pc 一并取出,供下轮直接复用 */
1201-
free(fr);
1209+
if (fb_pc) {
1210+
/* 本指令经帧回传了新 PC:直接用(它就是本指令刚写进 kvspace 的值)。
1211+
* status 不回传——下一轮状态门照旧从 kvspace 回读。 */
1212+
newpc = fb_pc;
1213+
fb_pc = NULL;
1214+
} else {
1215+
/* 无回传(ext rwir / 用户 call / done 等):从 kvspace 回读 PC。 */
1216+
kvlangVthreadPcGet(kv, vtid, &newpc);
1217+
}
12021218
if (tmp_owned)
12031219
kvlangRwirInstFree(&tmp);
12041220
if (!newpc || !newpc[0]) {

runtime/src/kvspace.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv
4545
if (!name || !name[0])
4646
return 0;
4747
size_t dl = strlen(dir), nl = strlen(name);
48-
char *key = malloc(dl + nl + 1);
48+
/* 短 key 走栈缓冲,免读参热路径每步一次 malloc。 */
49+
char stackbuf[512];
50+
char *key = stackbuf;
51+
if (dl + nl + 1 > sizeof stackbuf)
52+
key = malloc(dl + nl + 1);
4953
memcpy(key, dir, dl);
5054
memcpy(key + dl, name, nl);
5155
key[dl + nl] = 0;
@@ -56,7 +60,8 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv
5660
out->len = len;
5761
out->borrowed = 1;
5862
}
59-
free(key);
63+
if (key != stackbuf)
64+
free(key);
6065
return 0;
6166
}
6267

@@ -123,6 +128,24 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui
123128
return rc;
124129
}
125130

131+
/* 直写 char/utf8 值:跳过「TLV 编码 → KvSet 再解 head」的往返,直接向后端要 body 指针填字节。
132+
* 供 PC 推进热路径(每指令一次)用;语义等价于 NewCharUtf8 + KvSet。 */
133+
int kvlangKvSetChar(kvlangKv_t *k, const char *key, const char *s) {
134+
uint32_t len = (uint32_t)strlen(s);
135+
uint8_t *dst = NULL;
136+
char err[256];
137+
if (kvspaceWriteInPlace(k->h, key, 0, len, &dst, err, sizeof err) != 0) {
138+
if (kvspaceWriteNewPlace(k->h, key, KVSPACE_REF_INLINE,
139+
KVSPACE_STORETYPE_ATOM, 0, 0,
140+
KVSPACE_KIND_CHAR_UTF8, len, &dst, err,
141+
sizeof err) != 0)
142+
return -1;
143+
}
144+
if (len && dst)
145+
memcpy(dst, s, len);
146+
return 0;
147+
}
148+
126149
int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap) {
127150
const char *keys[1] = {key};
128151
return kvspaceDel(k->h, keys, 1, err, err_cap);

runtime/src/myrwircaps.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ int kvlangDefRwir(void *kvspace, const char *opcode, const char *const *rp,
9696

9797
/* 精度前缀(int64·add / float32·add …)保留:CapIndex 两级查表——先按完整 opcode 命中特化,
9898
* 未命中且前缀是 C native 数字 kind 时才剥前缀归到裸 op(如 add),kvlangBuiltin* 按操作数 kind 归约。 */
99-
static int kvlangCtlCopy(kvlangFrame_t *f) { return kvlangBuiltinExecuteCopy(f->kv, f->vtid, f->pc, f->inst); }
99+
static int kvlangCtlCopy(kvlangFrame_t *f) { return kvlangBuiltinExecuteCopy(f); }
100100

101101
static const struct { const char *op; kvlangBuiltinFn fn; } myrwircaps[] = {
102102
/* control / copy:与 native 算子同表,op_id 单跳派发 */

0 commit comments

Comments
 (0)