perf(全仓库/正则): 字面量正则全部改模块级预编译常量(15 文件 / 115 个调用点)

背景(prof 基线 PROF/20260930_224857,主进程 433.69s):正则这一族合计 ≈45s ——
`re.match`(未预编译入口)被调 1970 万次 / 自身 10.70s、它内部 `Pattern.match` 占 8.06s,
`re._compile`(缓存查找)1975 万次 / 7.19s,`re._make`(真正编译)274 万次 / 7.62s
(说明 re 的 512 条缓存已被打穿,每 7 次调用就有 1 次重新编译)。
每次 `re.match(字面量, s)` 都要做"模式串哈希 → 缓存查找";预编译后这层全省掉。

改动:把 15 个文件里 97 处"字面量当模式"的 `re.match/search/sub/subn/fullmatch/
findall/finditer/split(...)` 提升为模块级 `_RE_*: re.Pattern[str] = re.compile(...)`,
共 115 个调用点(含同字面量跨处共用)。模式串与 flags 一字未改(flags 从调用处
搬进 re.compile,含原先以**位置参数**传入的 flags)。4 处拼接型动态模式按设计不动
(FuncGen 2 处、HandlesImports 2 处)。

核验(不靠肉眼):写了 AST 指纹对拍脚本,对每个文件取 HEAD 版与新工作区版,把
"旧 re.<m>(字面量, 其余实参, 关键字) + flags" 与 "新 _RE_X.<m>(实参, 关键字) +
常量 (模式, flags)" 做多重集比对 —— 15 个文件、115 个调用点全部逐一等价(0 失败),
另查无重名常量、无遗留字面量调用、GetDiagnostics 全空。
对拍过程中查出一处子代理抄写错误:`_RE_GLOBAL_DSOLOCAL_MULTILINE` 丢了行首锚 `^`
(原文注释明确"使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare"),已修正。

同轮经查证后**未做改动**的两项(避免把假热点当真事修):
- VLogger 的 `re.sub` 6.43s:profile 当时的 VLogger 版本里根本没有 `import re`
  (`git show 3658ab06:lib/core/VLogger.py` 已确认),属 C 帧错挂,不做。
- tokenize 274 万次:那是 `_generate_tokens_from_c_tokenizer` 的 **token 产出次数**
  (≈350 个整文件的词法量),SymbolUtils 的 4 个 tokenize 调用点 nc 仅数百,且已有
  `_EXPAND_CACHE` 去重;再加一层 token 缓存无收益,不做。
This commit is contained in:
TermiNexus committed 2026-09-30 23:47:36 +08:00
1 parent 4641777db1
commit b18d57fa38
15 files changed
+318 -96

No files matched your search

+44 -15
View File
@@ -49,6 +49,35 @@ _LLVM_PRIMITIVE_TYPE_MAP: dict[str, ir.Type] = {
}
# 字面量正则的模块级预编译(性能:这些调用点做逐行扫描/逐条解析,命中次数极高;
# 每次 `re.match(字面量, s)` 都要做"模式串哈希 → re 缓存查找"。预编译成模块级常量
# 后省掉该查找,且匹配路径更短。模式串一字未改。
# 解析异常报文里兜底取 `at line N`
_RE_AT_LINE: re.Pattern[str] = re.compile(r'at line (\d+)')
# stub 里 `%<sha1>.Name = type <...>` 结构体定义行
_RE_STRUCT_TYPE_DEF: re.Pattern[str] = re.compile(r'%"?((?:[a-f0-9]+\.)?([\w\[\]]+))"?\s*=\s*type\s*(.*)')
# `[N x <T>]` 数组类型
_RE_ARRAY_TYPE: re.Pattern[str] = re.compile(r'\[(\d+)\s*x\s+(.+)\]')
# declare 行(函数名限定 `[\w.]+` 字符集)
_RE_DECLARE_SIMPLE: re.Pattern[str] = re.compile(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$')
# declare 行(函数名允许任意非括号字符)
_RE_DECLARE_GENERIC: re.Pattern[str] = re.compile(r'declare\s+(.+?)\s+@("?[^()]+"?)\s*\((.*)\)$')
# 剥离 LLVM IR 参数名:`\s+%name` 或 `\s+%"quoted.name"` 结尾
_RE_PARAM_NAME_TRAILING: re.Pattern[str] = re.compile(r'\s+%("[^"]*"|[\w.]+)\s*$')
# 剥离 LLVM IR 参数名:`\s+%name`(无引号形式)
_RE_PARAM_NAME_PLAIN: re.Pattern[str] = re.compile(r'\s+%[\w.]+\s*$')
# stub 里引用到的 sha1 前缀 `%<16hex>.`
_RE_SHA1_PREFIX_REF: re.Pattern[str] = re.compile(r'%\"?([a-f0-9]{16})\.')
# `@name = external global <...>` 全局变量定义行
_RE_EXTERNAL_GLOBAL_DEF: re.Pattern[str] = re.compile(r'@("?)(?:([a-f0-9]{16})\.)?([\w.\[\]]+)\1\s*=\s*external\s+global\s+(\S.*)')
# 去掉全局类型尾部 zeroinitializer
_RE_STRIP_ZEROINIT: re.Pattern[str] = re.compile(r'\s+zeroinitializer.*$')
# 去掉全局类型尾部 `, align N`
_RE_STRIP_ALIGN: re.Pattern[str] = re.compile(r'\s*,\s*align\s+\d+.*$')
# struct 定义名(不强制 `= type ...` 后缀)
_RE_STRUCT_NAME: re.Pattern[str] = re.compile(r'%"?((?:[a-f0-9]+\.)?[\w.\[\]*]+)"?')
def _HasRealParentClass(Node: ast.ClassDef) -> bool:
"""类是否声明了**真实父类**(排除 Object/CVTable/NoVTable/CEnum/... 标记基类)。"""
for base in getattr(Node, 'bases', None) or []:
@@ -106,7 +135,7 @@ def _ParseFailureSite(Exc: BaseException) -> str:
Line: int = int(getattr(Exc, 'lineno', 0) or 0)
Col: int = int(getattr(Exc, 'offset', 0) or 0)
if not Line:
Matched: re.Match | None = re.search(r'at line (\d+)', str(Exc))
Matched: re.Match | None = _RE_AT_LINE.search(str(Exc))
if Matched:
Line = int(Matched.group(1))
if not Line:
@@ -2921,7 +2950,7 @@ class ImportHandle(BaseHandle):
content = f.read()
for line in content.splitlines():
stripped = line.strip()
match = re.match(r'%"?((?:[a-f0-9]+\.)?([\w\[\]]+))"?\s*=\s*type\s*(.*)', stripped)
match = _RE_STRUCT_TYPE_DEF.match(stripped)
if match:
full_name = match.group(1)
short_name = match.group(2)
@@ -3593,7 +3622,7 @@ class ImportHandle(BaseHandle):
stub_func_name = name_match[1].split('(', 1)[0].strip().strip('"')
match = re.match(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$', stripped)
match = _RE_DECLARE_SIMPLE.match(stripped)
if match:
stub_ret = match.group(1).strip()
stub_params = match.group(3).strip()
@@ -3646,7 +3675,7 @@ class ImportHandle(BaseHandle):
"""剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分"""
param_str = param_str.strip()
# 支持 %name 和 %"quoted.name" 两种参数名形式
result = re.sub(r'\s+%("[^"]*"|[\w.]+)\s*$', '', param_str)
result = _RE_PARAM_NAME_TRAILING.sub('', param_str)
return result.strip()
def _parse_simple_llvm_type(self, type_str: str) -> ir.Type | None:
@@ -3676,7 +3705,7 @@ class ImportHandle(BaseHandle):
return ir.PointerType(ir.IntType(8))
if type_str.startswith('[') and ']' in type_str:
arr_match = re.match(r'\[(\d+)\s*x\s+(.+)\]', type_str)
arr_match = _RE_ARRAY_TYPE.match(type_str)
if arr_match:
size = int(arr_match.group(1))
elem_type = self._parse_simple_llvm_type(arr_match.group(2).strip())
@@ -3727,10 +3756,10 @@ class ImportHandle(BaseHandle):
for line in content.splitlines():
stripped = line.strip()
if stripped.startswith('%') and '=' in stripped and 'type' in stripped:
for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped):
for m in _RE_SHA1_PREFIX_REF.finditer(stripped):
needed_sha1s.add(m.group(1))
elif stripped.startswith('declare '):
for m in re.finditer(r'%\"?([a-f0-9]{16})\.', stripped):
for m in _RE_SHA1_PREFIX_REF.finditer(stripped):
needed_sha1s.add(m.group(1))
except Exception as _e:
if _config_mode == "strict":
@@ -3757,7 +3786,7 @@ class ImportHandle(BaseHandle):
stripped = line.strip()
if stripped.startswith('%') and '=' in stripped and 'type' in stripped:
struct_def_match = re.match(r'%"?((?:[a-f0-9]+\.)?([\w\[\]]+))"?\s*=\s*type\s*(.*)', stripped)
struct_def_match = _RE_STRUCT_TYPE_DEF.match(stripped)
if struct_def_match:
raw_name = struct_def_match.group(1)
# 治本修复:从 raw_name 提取类型定义的实际 sha1 前缀,
@@ -3807,14 +3836,14 @@ class ImportHandle(BaseHandle):
# Gen.module.globals → _lookup_module_global 恒 None → store/load
# 全部丢失 → memset(null) 崩溃。此处兼容裸名 + sha1 前缀两种格式,
# 名称与定义一致(temp/*.text.ll 均为裸名)。
_gv_match = re.match(r'@("?)(?:([a-f0-9]{16})\.)?([\w.\[\]]+)\1\s*=\s*external\s+global\s+(\S.*)', stripped)
_gv_match = _RE_EXTERNAL_GLOBAL_DEF.match(stripped)
if _gv_match:
_gv_name: str = _gv_match.group(3)
_gv_sha1: str | None = _gv_match.group(2)
_gv_type_str: str = _gv_match.group(4).strip()
# 去掉尾部修饰(zeroinitializer / , align N),只保留类型
_gv_type_str = re.sub(r'\s+zeroinitializer.*$', '', _gv_type_str)
_gv_type_str = re.sub(r'\s*,\s*align\s+\d+.*$', '', _gv_type_str)
_gv_type_str = _RE_STRIP_ZEROINIT.sub('', _gv_type_str)
_gv_type_str = _RE_STRIP_ALIGN.sub('', _gv_type_str)
if _gv_name not in Gen.module.globals:
_gv_ty: ir.Type | None = self._parse_llvm_type(_gv_type_str, Gen, source_sha1=source_sha1)
if _gv_ty is not None:
@@ -4002,7 +4031,7 @@ class ImportHandle(BaseHandle):
def _parse_llvm_declare(self, declare_line: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
"""解析 LLVM declare 语句"""
match = re.match(r'declare\s+(.+?)\s+@("?[^()]+"?)\s*\((.*)\)$', declare_line)
match = _RE_DECLARE_GENERIC.match(declare_line)
if not match:
return None
@@ -4146,7 +4175,7 @@ class ImportHandle(BaseHandle):
return ir.PointerType(ir.IntType(8))
if type_str.startswith('[') and ']' in type_str:
arr_match = re.match(r'\[(\d+)\s*x\s+(.+)\]', type_str)
arr_match = _RE_ARRAY_TYPE.match(type_str)
if arr_match:
size = int(arr_match.group(1))
elem_type = self._parse_llvm_type(arr_match.group(2).strip(), Gen, source_sha1=source_sha1, create_structs=create_structs)
@@ -4160,7 +4189,7 @@ class ImportHandle(BaseHandle):
# list[<sha1>.AST](少一层指针)并 declare_only 特化出无方法体的
# 假类型 → 调用点引用未定义的 <模sha1>.list[<sha1>.AST].__len__
# (链接期 undefined symbol 根因)。
struct_match = re.match(r'%"?((?:[a-f0-9]+\.)?[\w.\[\]*]+)"?', type_str)
struct_match = _RE_STRUCT_NAME.match(type_str)
if struct_match:
raw_name = struct_match.group(1)
actual_source_sha1 = source_sha1
@@ -4240,5 +4269,5 @@ class ImportHandle(BaseHandle):
'i32' -> 'i32'
"""
param_str = param_str.strip()
stripped = re.sub(r'\s+%[\w.]+\s*$', '', param_str)
stripped = _RE_PARAM_NAME_PLAIN.sub('', param_str)
return stripped.strip()