perf(tpc): _LookupStubFuncType 的目录计数按目录 mtime 缓存,去掉每次调用的全量 listdir

类别:纯提速(判定结果不变)。原实现每次调用都要

  sum(1 for f in os.listdir(output_dir) if f.endswith('.stub.ll'))
  sum(1 for f in os.listdir(temp_dir)   if f.endswith('.stub.ll'))

只为构造一个"stub 是否新增"的签名;本函数位于每次跨模块函数/方法查找的热路径
(20 个调用点),开销 = O(目录文件数) × 调用次数,与编译工作量无关,工程越大越亏。

改为 ImportHandle._CountStubFilesInDir(DirPath):同一「目录 + 目录 mtime_ns」
只 listdir 一次,mtime 命中即复用上次计数(每目录一次 os.stat 代替一次
os.listdir + 全量 endswith 判定)。
判据:stub 目录只增不改、且由主进程在批次之间写入 ⇒ 只有目录条目增删才会改变
计数,而 Windows/NTFS 在条目增/删/改名时会更新目录 mtime。
取不到 stat(目录不存在/权限)时不写缓存、退化为每次实算。
计数仍是权威签名,缓存只决定"要不要重新 listdir",不改变任何判定结果
(增量重扫、output 覆盖 temp 的语义逐字不变)。
This commit is contained in:
TermiNexus committed 2026-09-29 14:32:39 +08:00
1 parent f4c8c48748
commit f1e7fd6c28
1 file changed
+34 -2
+34 -2
View File
@@ -161,6 +161,9 @@ class ImportHandle(BaseHandle):
_stub_func_cache: dict[str, tuple[str, str, bool, str]] = {}
_stub_func_cache_signature: tuple[int, int] | None = None
_stub_func_scanned: set[str] = set()
# 目录 → (listdir 时的目录 mtime_ns, 该次统计出的 *.stub.ll 数量)。
# 键为目录绝对路径:temp/ 与 output/ 分别计缓存。见 _CountStubFilesInDir。
_stub_dir_count_cache: dict[str, tuple[int, int]] = {}
def __init__(self, translator: "Translator") -> None:
super().__init__(translator)
@@ -3085,6 +3088,33 @@ class ImportHandle(BaseHandle):
"""Build a scalar constant value from AST node, delegated to unified method."""
return self.Trans._BuildScalarConstantUnified(value_node, target_type, Gen)
@staticmethod
def _CountStubFilesInDir(DirPath: str) -> int:
"""目录下 `*.stub.ll` 的数量(同一「目录 + 目录 mtime」只 listdir 一次)。
为什么可缓存:stub 目录**只增不改**,且由主进程在批次之间写入 ⇒ 只有目录
条目增删才会改变计数,而 Windows/NTFS 在条目增/删/改名时会更新目录 mtime。
命中缓存即复用上次计数,省掉 `os.listdir` + 全量 `endswith('.stub.ll')`
判定 —— 这两个统计只为在 `_LookupStubFuncType` 里构造"是否新增 stub"的
签名,而该函数位于每次跨模块函数/方法查找的热路径上(20 个调用点),
工程越大(stub 越多)这份与编译工作量无关的开销越高。
取不到 stat(目录不存在 / 权限)时**不写缓存**,退化为每次实算:
计数仍是权威判据,缓存只决定"要不要重新 listdir",不改变任何判定结果。
"""
if not os.path.isdir(DirPath):
return 0
try:
_Mtime: int = os.stat(DirPath).st_mtime_ns
except OSError:
return sum(1 for _f in os.listdir(DirPath) if _f.endswith('.stub.ll'))
_Hit: tuple[int, int] | None = ImportHandle._stub_dir_count_cache.get(DirPath)
if _Hit is not None and _Hit[0] == _Mtime:
return _Hit[1]
_Count: int = sum(1 for _f in os.listdir(DirPath) if _f.endswith('.stub.ll'))
ImportHandle._stub_dir_count_cache[DirPath] = (_Mtime, _Count)
return _Count
def _LookupStubFuncType(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None:
"""从 stub.ll 文件中查找指定函数的 LLVM 类型
@@ -3106,8 +3136,10 @@ class ImportHandle(BaseHandle):
# 只增量解析新增 stub 文件(编译进程内 stub 只增不改,旧文件不重扫,
# 省略每文件 os.path.isfile ——Windows stat ~0.5ms/次,原 5840 次 ≈ 6.5s)。
# 覆盖语义不变:先扫 temp/ 后扫 output/,output 同名声明覆盖 temp。
output_count = sum(1 for f in os.listdir(output_dir) if f.endswith('.stub.ll')) if os.path.isdir(output_dir) else 0
temp_count = sum(1 for f in os.listdir(temp_dir) if f.endswith('.stub.ll')) if os.path.isdir(temp_dir) else 0
# 计数本身也不重复 listdir:走 _CountStubFilesInDir 的「目录 mtime 命中即复用」
# 缓存(原实现每次调用都 listdir + 全量 endswith 判定,纯浪费)。
output_count = self._CountStubFilesInDir(output_dir)
temp_count = self._CountStubFilesInDir(temp_dir)
signature = (output_count, temp_count)
if ImportHandle._stub_func_cache_signature != signature:
ImportHandle._stub_func_cache_signature = signature