perf(Phase2Translator/IRMetadataFix): 削逐行热点——跳过无谓 strip、预编译逐行正则、C 级数字扫描

prof 基线(PROF/20260930_224857/main_time.txt=438.40s,7.28 亿次调用)显示全流程最大的 Python 侧热点是逐行扫描产生的内置字符串/正则调用:str.startswith 2.372 亿次(32.1s)、str.strip 1.791 亿次(22.8s)、re.Pattern.match 3130 万次(11.4s)、re.match 模块级 1971 万次(10.7s)+re._compile 1975 万次(7.2s)。四处等价改动:
1) _split_ll 第二遍:in_define/in_global_def 续行(.ll 里占比最高)不再 str.strip——这两支只用行内容与花括号计数,前导/尾随空白不含花括号,_count_braces_outside_strings 结果逐字不变;
2) _split_ll 第一遍:加 "define" not in line 的 C 级快路径,绝大多数行连 strip 都不做;
3) Phase2Translator 逐行正则全部预编译为模块级 _RE_* 常量(11 处 re.match + 4 处 re.sub,模式串一字未改)——消掉每次调用的模式串哈希+缓存查找;
4) IRMetadataFix:_RewritePlain 的前导数字扫描由逐字符 Python 循环改为 str.lstrip 的 C 级扫描(该函数 prof 5.8M 次调用,是项目内 self time 最大者);SanitizeIRMetadata 的逐行 startswith("!") 改为 [:1] 索引切片。
离线自测:数字扫描与旧实现逐例一致;SanitizeIRMetadata 幂等性保持;_split_ll 冒烟输出与预期语义一致(global/多行初始化器/define→declare/constant/metadata 分流正确)。
This commit is contained in:
TermiNexus committed 2026-09-30 23:04:25 +08:00
1 parent e2c84daa65
commit 3658ab0637
2 files changed
+68 -32

No files matched your search

+56 -25
View File
@@ -153,7 +153,29 @@ _DWARF_TAG_ANY_RE = re.compile(r'DW_TAG_\w+')
_DWARF_TAG_SUBPROGRAM_RE = re.compile(r'DW_TAG_subprogram\b')
_DWARF_LOWPC_RE = re.compile(r'DW_AT_low_pc\s*\(\s*(0x[0-9A-Fa-f]+)\s*\)')
_DWARF_HIGHPC_RE = re.compile(r'DW_AT_high_pc\s*\(\s*(0x[0-9A-Fa-f]+)\s*\)')
_DWARF_ATNAME_RE = re.compile(r'DW_AT_name\s*\("((?:[^"\\]|\\.)*)"\)')
_DWARF_ATNAME_RE = re.compile(r'DW_AT_name\s*\("((?:[^"\\]|\\.)*)"\)')
# ---------------------------------------------------------------
# `_split_ll` 的逐行正则**预编译**(性能:该函数对合并后的 .ll 逐行跑两遍,
# prof 实测 43.9万行级输入下 re 模块级函数的缓存查找(re._compile)达 1970 万次
# ≈ 7s,另有 3100 万次 re.Pattern.match;每次 `re.match(字面量, ...)` 都要做
# "模式串哈希 → 缓存查找"。预编译成模块级常量后省掉这层查找,且匹配路径更短。
# 语义与逐字写法完全一致(模式串一字未改)。
# ---------------------------------------------------------------
_RE_DEFINE_SHA1: re.Pattern[str] = re.compile(r'define\s+[^@]*@"?([a-f0-9]+)\.')
_RE_DEFINE_NAME: re.Pattern[str] = re.compile(r'define\s+[^@]*@"?([^"\s(]+)"?')
_RE_DECLARE_NAME: re.Pattern[str] = re.compile(r'declare\s+[^@]*@"?([^"\s(]+)"?')
_RE_LLVM_INTRINSIC: re.Pattern[str] = re.compile(r'@("?)llvm\.')
_RE_AT_NAME: re.Pattern[str] = re.compile(r'(@\S+)')
_RE_SHA1_16_DOT: re.Pattern[str] = re.compile(r'^[a-f0-9]{16}\.')
_RE_DEFINE_PREFIX: re.Pattern[str] = re.compile(r'^define\s+')
_RE_DEFINE_LINKAGE: re.Pattern[str] = re.compile(
r'\b(linkonce_odr|weak_odr|linkonce|weak|common|appending|internal|private)\s+')
_RE_ALWAYSINLINE: re.Pattern[str] = re.compile(r'\balwaysinline\s+')
# `_compile_one` 的逐行符号扫描/全局去重里用到的两条(同一预编译理由)
_RE_AT_EXTERNAL: re.Pattern[str] = re.compile(r'@\S+\s*=\s*external\s+')
_RE_DECLARE_FULL: re.Pattern[str] = re.compile(
r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(')
def _ParseDwarfLineRows(dump_text: str) -> list[tuple[int, str, int]]:
@@ -1614,13 +1636,19 @@ class Phase2Translator:
defined_names = set()
for line in ll_content.splitlines(True):
# 第一遍只关心 `define ...` 行,而合并后的 .ll 里这类行占比极低。
# `'define' not in line` 是 C 级子串扫描 ⇒ 绝大多数行连 strip 都不做
# (prof 实测 str.strip 1.79 亿次 / str.startswith 2.37 亿次是全流程
# 最大的 Python 侧热点,本函数两遍逐行扫描是其主要来源)。
if 'define' not in line:
continue
stripped = line.strip()
if stripped.startswith('define '):
dm = re.match(r'define\s+[^@]*@"?([a-f0-9]+)\.', stripped)
dm = _RE_DEFINE_SHA1.match(stripped)
if dm and module_sha1 is None:
module_sha1 = dm.group(1)
dm2 = re.match(r'define\s+[^@]*@"?([^"\s(]+)"?', stripped)
dm2 = _RE_DEFINE_NAME.match(stripped)
if dm2:
defined_names.add(dm2.group(1))
@@ -1630,12 +1658,14 @@ class Phase2Translator:
global_brace_depth = 0
for line in ll_content.splitlines(True):
stripped = line.strip()
# 续行(函数体指令 / 全局初始化器)在合并 .ll 里占绝大多数,而这两支
# 只需要"行内容 + 花括号计数" ⇒ 不进 strip(前导/尾随空白不含花括号,
# _count_braces_outside_strings 的结果逐字不变)。prof 实测 str.strip
# 1.79 亿次为最大 Python 侧热点,逐行 strip 是其主要来源。
if in_global_def:
text_lines.append(line)
global_type_parts.append(stripped)
global_brace_depth += _count_braces_outside_strings(stripped)
global_type_parts.append(line.strip())
global_brace_depth += _count_braces_outside_strings(line)
if global_brace_depth <= 0:
full_type = ' '.join(global_type_parts).strip()
depth = 0
@@ -1659,18 +1689,19 @@ class Phase2Translator:
if in_define:
text_lines.append(line)
brace_depth += _count_braces_outside_strings(stripped)
brace_depth += _count_braces_outside_strings(line)
if brace_depth <= 0:
in_define = False
continue
stripped = line.strip()
if stripped.startswith('define '):
text_lines.append(line)
in_define = True
brace_depth = _count_braces_outside_strings(stripped)
decl_line = re.sub(r'^define\s+', 'declare ', stripped)
decl_line = re.sub(r'\b(linkonce_odr|weak_odr|linkonce|weak|common|appending|internal|private)\s+', '', decl_line)
decl_line = re.sub(r'\balwaysinline\s+', '', decl_line)
decl_line = _RE_DEFINE_PREFIX.sub('declare ', stripped)
decl_line = _RE_DEFINE_LINKAGE.sub('', decl_line)
decl_line = _RE_ALWAYSINLINE.sub('', decl_line)
paren_pos = decl_line.rfind(')')
if paren_pos >= 0:
decl_line = decl_line[:paren_pos + 1]
@@ -1698,10 +1729,10 @@ class Phase2Translator:
# LLVM 保留全局(如 --hotspot-sites 的 @llvm.global_ctors;名字含点故
# 最终 IR 保留引号)是 appending 定义,禁止改写为 "external global"
# 存根(否则 .ll 重组后 llc 报错)
if re.match(r'@("?)llvm\.', stripped):
if _RE_LLVM_INTRINSIC.match(stripped):
continue
if ' internal ' not in stripped and ' private ' not in stripped:
name_match = re.match(r'(@\S+)', stripped)
name_match = _RE_AT_NAME.match(stripped)
if name_match:
var_name = name_match.group(1)
global_pos = stripped.find(' global ')
@@ -1736,7 +1767,7 @@ class Phase2Translator:
continue
if stripped.startswith('declare'):
dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped)
dm = _RE_DECLARE_NAME.match(stripped)
if dm:
fname = dm.group(1)
if fname in defined_names:
@@ -1747,7 +1778,7 @@ class Phase2Translator:
fname_sha1 = fname[:dot_pos]
if module_sha1 and fname_sha1 != module_sha1 and len(fname_sha1) >= 16:
continue
if re.match(r'^[a-f0-9]{16}\.', fname):
if _RE_SHA1_16_DOT.match(fname):
continue
stub_lines.append(line)
continue
@@ -2475,10 +2506,10 @@ class Phase2Translator:
for i, line in enumerate(ll_lines_for_dedup):
stripped = line.strip()
if stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped):
gm = re.match(r'(@\S+)', stripped)
gm = _RE_AT_NAME.match(stripped)
if gm:
gname = gm.group(1)
is_external = re.match(r'@\S+\s*=\s*external\s+', stripped) is not None
is_external = _RE_AT_EXTERNAL.match(stripped) is not None
if is_external:
external_globals[gname] = i
else:
@@ -2507,7 +2538,7 @@ class Phase2Translator:
else:
existing_symbols[name] = 'full'
elif stripped.startswith('declare'):
dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped)
dm = _RE_DECLARE_NAME.match(stripped)
if dm:
fname = dm.group(1)
existing_declares.add(fname)
@@ -2525,11 +2556,11 @@ class Phase2Translator:
for line in ll_content.splitlines():
stripped = line.strip()
if stripped.startswith('define'):
dm = re.match(r'define\s+[^@]*@"?([^"\s(]+)"?', stripped)
dm = _RE_DEFINE_NAME.match(stripped)
if dm:
existing_defines.add(dm.group(1))
elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped):
gm = re.match(r'(@\S+)', stripped)
gm = _RE_AT_NAME.match(stripped)
if gm:
existing_global_defs.add(gm.group(1))
for dep_name, dep_sha1 in deps.items():
@@ -2547,7 +2578,7 @@ class Phase2Translator:
if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped:
continue
if stripped.startswith('declare'):
dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped)
dm = _RE_DECLARE_NAME.match(stripped)
if dm:
fname = dm.group(1)
if 'zdef_alloc' in fname:
@@ -2562,7 +2593,7 @@ class Phase2Translator:
if has_struct and not existing_has_struct:
for i, l in enumerate(all_stub_lines):
if l is not None and l.strip().startswith('declare'):
em = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', l.strip())
em = _RE_DECLARE_NAME.match(l.strip())
if em and em.group(1) == fname:
all_stub_lines[i] = line
break
@@ -2606,7 +2637,7 @@ class Phase2Translator:
else:
all_stub_lines.append(line)
elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped):
gm = re.match(r'(@\S+)', stripped)
gm = _RE_AT_NAME.match(stripped)
if gm and (gm.group(1) in existing_global_defs or gm.group(1) in stub_global_defs):
continue
if gm:
@@ -2670,7 +2701,7 @@ class Phase2Translator:
lines: list[str] = ll_content.splitlines(True)
kept_lines: list[str] = []
for line in lines:
m = re.match(r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', line)
m = _RE_DECLARE_FULL.match(line)
if m and m.group(1) in defined_funcs:
continue
kept_lines.append(line)
@@ -2680,7 +2711,7 @@ class Phase2Translator:
dedup_lines: list[str] = ll_content.splitlines(True)
dedup_kept: list[str] = []
for line in dedup_lines:
dm = re.match(r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', line)
dm = _RE_DECLARE_FULL.match(line)
if dm:
fname = dm.group(1)
if fname in seen_declares:
+12 -7
View File
@@ -27,8 +27,9 @@ _NamedDefinition = re.compile(r'^!([A-Za-z][\w.]*)\s*=\s*(.*)$')
_ListForm = re.compile(r'^!\{(.*)\}$', re.DOTALL)
# 纯 id 引用 `!N`
_PureId = re.compile(r'^!(\d+)$')
# Decimal digit set for the no-regex fast rewrite path.
_DIGITS: frozenset[str] = frozenset('0123456789')
# 前导数字字符集(str 形式):供 str.lstrip 的 **C 级**扫描用
# (_RewritePlain 在合并 .ll 上是百万级调用,原逐字符 Python 循环是 self time 主源)。
_DIGITS_STR: str = '0123456789'
def _SplitTopLevel(Content: str) -> list[str]:
@@ -92,10 +93,11 @@ def _RewritePlain(Line: str, StrMap: dict[str, str]) -> str:
Ap = Out.append
Get = StrMap.get
for Part in Parts[1:]:
d: int = 0
n: int = len(Part)
while d < n and Part[d] in _DIGITS:
d += 1
# 前导数字个数改由 str.lstrip 的 **C 级**扫描给出(原逐字符 Python 循环
# `while d < n and Part[d] in _DIGITS`;本函数 prof 实测 5.8M 次调用,
# 是 self time 最大的项目内函数)。语义与循环逐字等价:都是"前导数字个数"。
d: int = n - len(Part.lstrip(_DIGITS_STR))
if not d:
Ap('!')
Ap(Part)
@@ -160,9 +162,12 @@ def SanitizeIRMetadata(text: str) -> str:
Blocks: list[tuple[int, int]] = [] # [Start, End)
i: int = 0
while i < LineCount:
if Segments[i].startswith('!'):
# 单个字符的判定用索引切片(seg[:1])替代 str.startswith:本循环对合并 .ll
# 的**每一行**各跑一次(prof 实测 str.startswith 237M 次为全流程最大热点),
# 索引切片无方法查找开销、语义与 startswith('!') 等价(空行得 ''≠'!')。
if Segments[i][:1] == '!':
j: int = i + 1
while j < LineCount and Segments[j].startswith('!'):
while j < LineCount and Segments[j][:1] == '!':
j += 1
Blocks.append((i, j))
i = j