perf(gen): 结构体/CTypedef 查名加索引,热点线性查名补首字符预筛

StructGenMixin.find_struct:索引可信且查询名非空时,未命中即判定确无同名条目,跳过 O(n) 精确扫描(此前索引只短路命中、未命中仍全表扫描,实测 527 万次逐条 strcmp 即源于此)。

TypeGenCType:新增 CTypedef 名索引(开放寻址、槽存 index+1、Built==len 才可信、不可信或未命中回退线性扫描、clear_ctypedefs 同步复位),消除 lookup_ctypedef 约 445 万次 strcmp。

补首字符预筛:StubMergerIR._FindSha1ByModName、CallGenBase._ensure_method_declare、VarGenMixin.find_var、CallGenLeafKit.find_func_entry_in_table(_owned)(首字符不等即判不等,匹配结果与顺序不变)。
This commit is contained in:
TermiNexus committed 2026-09-14 04:15:36 +08:00
1 parent e8db102b9e
commit cb430bf00a
7 files changed
+155 -11

No files matched your search

+4 -2
View File
@@ -364,7 +364,9 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
# 第一遍:精确匹配
for i in range(count):
idx: sizet = sizet(i) * 64
if string.strcmp(mod_arr + idx, mod_name) == 0:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
# (热点:本函数全表逐条 strcmp 查名)。
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
sidx: sizet = sizet(i) * 17
return sha1_arr + sidx
# 第二遍:包名回退("ast" → "ast.__init__")
@@ -374,7 +376,7 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
for i in range(count):
idx2: sizet = sizet(i) * 64
if string.strcmp(mod_arr + idx2, init_buf) == 0:
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
sidx2: sizet = sizet(i) * 17
stdlib.free(init_buf)
return sha1_arr + sidx2
+4 -1
View File
@@ -645,7 +645,10 @@ class CallGenBase:
stdio.printf("[CALLGEN] 链表异常: module funcs(call_name=%s,遍历超限,终止)\n", call_name)
stdlib.exit(1)
if existing.Name is not None:
if string.strcmp(existing.Name, call_name) == 0:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
# (热点:本函数对模块函数链逐节点命名比对)。
if existing.Name[0] == call_name[0] \
and string.strcmp(existing.Name, call_name) == 0:
# 已存在:仅当是 declare 且参数数量不足时追加缺失参数
if existing.IsDeclared == 1:
# 变长函数(*args,IsVarArg=1)不追加参数:多余实参由
+5 -2
View File
@@ -1498,7 +1498,8 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
entry_addr: uint64 = uint64(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
if entry.Name[0] == name[0] and string.strcmp(entry.Name, name) == 0:
return entry
return None
@@ -1524,7 +1525,9 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry is None or entry.Name is None:
continue
if string.strcmp(entry.Name, name) != 0:
# 快速路径:strcmp 判等要求首字符相等,首字符不同者跳过 strcmp
# (热点:本函数对函数表逐条按裸名 + 定义模块 SHA1 比对)。
if entry.Name[0] != name[0] or string.strcmp(entry.Name, name) != 0:
continue
if owner_sha1 is None:
return entry
+16 -5
View File
@@ -791,11 +791,22 @@ class StructGenMixin:
fi: int = self._struct_name_idx_find(name)
if fi >= 0:
return self._get_struct_entry(fi)
for i in range(self.StructCount):
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
# 名索引可信(Built==Count | 索引存在 | 查询名非空)时,索引未命中即
# "确无同名条目":条目在注册时即命名并同步入索引,之后名至多被中和为 ""
# (ClassGenDef/ClassGenEnum 置 Name[0]=0),故任何非空查询名的命中项必在
# 索引探测链上 ⇒ 可跳过下方 O(n) 精确扫描(热点:本扫描占 find_struct 的
# 全部 strcmp)。索引不可信 / 无索引 / 空名查询时仍回退原线性扫描
# (正确性保证,非兜底)。
name_idx_trusted: int = 0
if self.StructNameIdx is not None and self.StructIdxCap > 0 \
and self.StructIdxBuilt == self.StructCount:
name_idx_trusted = 1
if name_idx_trusted == 0 or name[0] == '\0':
for i in range(self.StructCount):
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
# 模块限定名(module.Class):模块可解析时按模块归属精确匹配,
# 防止裸名回退抓跨模块同名类。模块未命中时若裸名类唯一(无冲突)
# 仍回退,避免误杀未登记模块的既有解析。
+10
View File
@@ -79,6 +79,13 @@ class TypeGenBase:
CTypedefNames: list[str] | t.CPtr
CTypedefTypeStrs: list[str] | t.CPtr
CTypedefFound: int
# CTypedef 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽)。
# 与 CTypedefNames 同池分配、同 clear_ctypedefs 复位;CTypedefNames 只追加
# 不重排(同名注册原地更新类型串)⇒ 存槽下标稳定;Built == len(CTypedefNames)
# 才可信,不可信/未命中一律回退线性扫描(正确性保证)。
CTypedefNameIdx: sizet | t.CPtr
CTypedefNameIdxCap: int
CTypedefNameIdxBuilt: int
CTypedefResolveDepth: int
CTypedefVisiting: list[str] | t.CPtr
# builtins 源扫描标记:注解解析"最后匹配"兜底(_lookup_builtins_typedef)
@@ -151,6 +158,9 @@ class TypeGenBase:
self.CTypedefNames = None
self.CTypedefTypeStrs = None
self.CTypedefFound = 0
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
self.CTypedefResolveDepth = 0
self.CTypedefVisiting = None
self.BuiltinsTypedefScanned = 0
+114
View File
@@ -16,6 +16,33 @@ import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeSpec as TSpec
# ============================================================
# CTypedef 名索引(名 → 槽下标,开放寻址 | 线性探测)
#
# 槽数 = 4096(2 的幂;负载 < 0.5 上限 2048 条,超限标记不可信回退线性扫描)。
# 槽存槽下标 + 1(0=空槽)。CTypedefNames 只追加不重排(同名注册原地更新类型串)
# ⇒ 槽下标稳定;Built == len(CTypedefNames) 才可信。
# ============================================================
CTYPEDEF_IDX_CAP: t.CDefine = 4096
# 索引哈希常量(32 位 FNV-1a,与 StructGenMixin 范式一致)
CTYPEDEF_IDX_FNV_OFFSET: t.CDefine = 2166136261
CTYPEDEF_IDX_FNV_PRIME: t.CDefine = 16777619
CTYPEDEF_IDX_FNV_MASK: t.CDefine = 4294967295
def _ctypedef_idx_hash(name: str) -> uint64:
"""CTypedef 名 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束)"""
h: uint64 = uint64(CTYPEDEF_IDX_FNV_OFFSET)
if name is None:
return h
i: sizet = 0
while name[i] != '\0':
h = (h ^ uint64(int(name[i]))) * uint64(CTYPEDEF_IDX_FNV_PRIME)
h = h & uint64(CTYPEDEF_IDX_FNV_MASK)
i += 1
return h
# ============================================================
# TypeGenCType - TypeGenMixin 拆分(阶段 D):CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)
#
@@ -27,6 +54,74 @@ import lib.core.Gen.Type.TypeSpec as TSpec
class TypeGenCType(TypeGenBase.TypeGenBase):
"""CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr)"""
# ============================================================
# CTypedef 名索引(开放寻址;槽存 index+1,0=空槽)
# ============================================================
def _ctypedef_idx_slot(self, si: sizet) -> sizet | t.CPtr:
"""取索引第 si 个槽(8 字节,存槽下标+1;0=空槽)"""
return (sizet | t.CPtr)(t.CVoid(uint64(self.CTypedefNameIdx) + uint64(si) * 8, t.CPtr))
def _ctypedef_idx_init(self, pool: rowptr[memhub.MemBuddy]) -> None:
"""创建/清零名索引(与 CTypedefNames 同池);失败则标记不可信"""
idx: sizet | t.CPtr = pool.alloc(sizet(CTYPEDEF_IDX_CAP) * 8)
if idx is None:
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
return
string.memset(idx, 0, sizet(CTYPEDEF_IDX_CAP) * 8)
self.CTypedefNameIdx = idx
self.CTypedefNameIdxCap = CTYPEDEF_IDX_CAP
self.CTypedefNameIdxBuilt = 0
def _ctypedef_idx_find(self, name: str) -> int:
"""在名索引中探测 name,返回命中槽下标(>=0);未命中/不可用返回 -1"""
if self.CTypedefNameIdx is None or self.CTypedefNameIdxCap <= 0 or name is None:
return -1
if self.CTypedefNameIdxBuilt != int(len(self.CTypedefNames)):
return -1
h: uint64 = _ctypedef_idx_hash(name)
mask: sizet = sizet(self.CTypedefNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CTypedefNameIdxCap:
slot: sizet | t.CPtr = self._ctypedef_idx_slot(si)
sv: sizet = slot[0]
if sv == 0:
return -1
se: int = int(sv) - 1
nm: str = self.CTypedefNames.get(sizet(se))
if nm is not None and string.strcmp(nm, name) == 0:
return se
si = (si + 1) & mask
i += 1
return -1
def _ctypedef_idx_insert(self, slot_index: int, name: str) -> int:
"""把 (name → slot_index) 记入索引;0=成功 / 1=失败"""
if self.CTypedefNameIdx is None or self.CTypedefNameIdxCap <= 0 or name is None:
return 1
h: uint64 = _ctypedef_idx_hash(name)
mask: sizet = sizet(self.CTypedefNameIdxCap - 1)
si: sizet = sizet(h) & mask
i: int = 0
while i < self.CTypedefNameIdxCap:
slot: sizet | t.CPtr = self._ctypedef_idx_slot(si)
if slot[0] == 0:
slot[0] = sizet(slot_index + 1)
return 0
si = (si + 1) & mask
i += 1
return 1
def _ctypedef_idx_sync_append(self, name: str) -> None:
"""CTypedefNames.append 后同步索引(槽下标 = len-1);不可信则保持不可信"""
n: int = int(len(self.CTypedefNames))
if self.CTypedefNameIdxBuilt == n - 1 and self._ctypedef_idx_insert(n - 1, name) == 0:
self.CTypedefNameIdxBuilt = n
else:
self.CTypedefNameIdxBuilt = -1
def register_ctypedef(self, pool: rowptr[memhub.MemBuddy],
name: str, type_str: str) -> None:
"""注册 CTypedef 到本地表
@@ -60,6 +155,8 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
if self.CTypedefNames is None:
self.CTypedefNames = list[str](pool, 64)
self.CTypedefTypeStrs = list[str](pool, 64)
# 名索引随表创建(同池;分配失败则标记不可信,查找回退线性扫描)
self._ctypedef_idx_init(pool)
# 去重:若已存在同名 CTypedef,更新其类型字符串
if self.CTypedefNames is not None:
exist_n: sizet = len(self.CTypedefNames)
@@ -72,6 +169,8 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
return
self.CTypedefNames.append(name_copy)
self.CTypedefTypeStrs.append(type_copy)
# 追加后同步名索引(槽下标 = 追加前 len = 新 len-1)
self._ctypedef_idx_sync_append(name_copy)
def store_builtins_diag(self, which: int, text: str) -> str:
"""把 builtins 诊断串复制进自有缓冲并返回该缓冲(which: 0=Name, 1=Result)
@@ -120,6 +219,17 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
return None
n: sizet = len(self.CTypedefNames)
i: sizet
# 名索引快速路径:可信(Built==n)时直接定位唯一候选槽下标。CTypedef 表
# 注册时按名去重(register_ctypedef 已存在则原地更新类型串)⇒ 名唯一,
# 索引未命中 ⇒ 确实不存在,直接返回;索引不可用/不可信 → 回退原全表扫描。
if self.CTypedefNameIdxBuilt == int(n) \
and self.CTypedefNameIdx is not None and self.CTypedefNameIdxCap > 0:
ti: int = self._ctypedef_idx_find(name)
if ti < 0:
return None
ts_hit: str = self.CTypedefTypeStrs.get(sizet(ti))
self.CTypedefFound = 1
return ts_hit
for i in range(n):
nm: str = self.CTypedefNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
@@ -284,6 +394,10 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
"""清空 CTypedef 表(模块切换时调用)"""
self.CTypedefNames = None
self.CTypedefTypeStrs = None
# 名索引随表复位(下次 register 重建);不同步会使 Built 与空表错配
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
self.CTypedefResolveDepth = 0
self.CTypedefVisiting = None
# builtins 源扫描标记随表清空复位:每文件最多读一次 includes/builtins.vp
+2 -1
View File
@@ -75,7 +75,8 @@ class VarGenMixin:
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None and entry.Used != 0:
if string.strcmp(entry.Name, name) == 0:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
if entry.Name[0] == name[0] and string.strcmp(entry.Name, name) == 0:
return entry.Alloca
return None