Scalene 形式化验证全解:Lean 4 定理证明 + TLA+ 模型检验,把 Python 分析器的正确性写成机器可检查的定理

  • 开发工具
  • 性能测试
  • AI 应用

【免费下载链接】scalene

Scalene: a high-performance, high-precision CPU, GPU, and memory profiler for Python with AI-powered optimization proposals

项目地址: https://gitcode.com/gh_mirrors/sc/scalene
点击查看 免费下载

本文围绕当前仓库 formal/ 目录下的形式化验证工程展开,完整梳理 Scalene(Python CPU/GPU/内存分析器)运行时正确性保证的机器可检查建模:14 个子系统、17 个 Lean 4 模块(142 个定理、无 sorry、仅依赖标准公理)与 2 个经 TLC 穷举模型检验的 TLA+ 规格,并说明这套 proof→production(证明到生产)流水线如何在真实代码中发现了 4 个已修复的生产缺陷。读完本文,你将掌握该工程的验证范围、双引擎分工原理、从 Lean 证明到 Python 代码的提取流程,以及如何在本地复现全部证明与模型检验。


1. 工程全景:把"分析器正确"变成可检查的定理

formal/README.md 开宗明义:本目录包含 Scalene 运行时的机器可检查形式化模型,以及一条"证明→生产"流水线——把已被证明的算法提取为 Python 代码,并与真实分析器做差分测试。

截至当前仓库快照,整个正确性工程的规模为:

  • Lean 4:17 个模块、142 个定理,无任何 sorry/admit,证明只依赖 Lean 的三个标准公理(propext、Classical.choice、Quot.sound);
  • TLA+/TLC:2 个规格,经 TLC 穷举模型检验,结论分别标记为 ✅ Proven(已证明)、⚠️ Partial(部分证明)、❌ Unproven(未证明);
  • 每个子系统的详细状态表见 formal/STATUS.md,按方面的结论陈述与源码映射见 formal/README.md 的 §1–§17。

明确未建模(Not modeled) 的部分包括:输出渲染(三个渲染器,由测试而非证明守护——下面会看到,4 个除零 bug 正是在这个区域被发现的)、CLI/参数解析、replacement_* 模块、浮点舍入(证明使用精确有理数 ℚ),以及 Jupyter/AI-provider GUI。

2. 双引擎分工:为什么同时需要 Lean 与 TLA+/TLC

工具目录验证什么方法
TLA+ / TLCformal/tla/交错安全性、死锁自由、活性(liveness)穷举模型检验
Lean 4formal/lean/守恒/界的不等式算术、快照代数机器检查证明

两者互补而非冗余(README "Why both?" 一节):

  • 竞态与死锁是"交错"性质的。TLC 穷举探索交错并产出具体的反例轨迹,还能在公平性(fairness)假设下检查活性(进展、无饥饿)——这是 Lean 难以自然表述的;
  • 守恒与界是"对所有输入成立的算术"。Lean 对无界量做证明(例如 estimator_unbiased 对任意样本预算 N 成立),这是模型检验器无法表达的(TLC 无法表述"对所有 N")。

两引擎有一处刻意且有诚意的重叠:LeakTrackerConcurrency.lean 在 Lean 中证明了交错安全性质,但把"步原子性"作为公理假设(由 RLock + 线程 join 论证合理性);从 sig-queue 的操作语义推导该原子性是自然的下一项 TLA+ 工作(详见 formal/HANDOFF.md)。

3. 证明总览(Proof Roundup):按子系统的结论

以下是 README 的结论骨架,逐一列出核心定理与源码映射。

3.1 CPU 分析——头条结论

报告的行级 profile 是真相的无偏、一致估计量:

  • ✅ estimator_unbiased——对任意样本预算 N,期望值恰为真实值;
  • ✅ jointVariance_eq——方差 = p(1−p)/N → 0,随样本累积收敛于真相;
  • ✅ 采样器是 Poisson(ExponentialSampler,逆 CDF + 无记忆性);
  • ✅ PASTA 现已被证明把 Poisson 时刻与时间占比落点联系起来(PoissonArrivals.uniform_realizes_trueFraction,§12);
  • ✅ Python/native 分类器在每个分支守恒样本的 CPU 预算(PythonNativeClassifier.charge_total,§15),并在两条代码路径(主线程混合路径与工作线程字节码分类器)上精确(ClassifierAccuracy,§17,在显式 CPython 信号投递假设下,且共享域内两者一致);
  • ⚠️ 残留项:C++ 打点(stamping)是否确立忠实落点、以及 SigDeliverySound 本身(atCall 是否反映真实执行状态)——两者都是 CPython 运行时/工程性质,作为假设陈述而非证明。

3.2 内存采样

  • ✅ 默认 ThresholdSampler 精确守恒真实净分配,且有界残差(threshold_conserves、threshold_residual_bounded);
  • ✅ 与字面双计数器 C++ 双模拟(step_bisim);
  • ✅ Poisson 采样器无偏;
  • ✅ 行级字节占比忠实(PerLineAttribution);
  • ✅ 行级 reader 簿记守恒(Σ 行级字节 = 总字节)、python 份额 ≤ 总字节、峰值单调不降(PerLineMallocAttribution,§16)。

3.3 内存泄漏检测——完全闭环(含并发)

  • ✅ 泄漏得分是 [0,1] 的 Rule-of-Succession 概率,带单调性与精确判定规则(MetricCorrectness);
  • ✅ 无保护的分母安全(LeakTrackerAudit,frees ≤ allocs);
  • ✅ 该安全性在 sig-queue/主线程交错与 fork 下依然成立,且序列化被证明是必要的(LeakTrackerConcurrency)。此审计找到了生产 bug(见下)。

3.4 跨 C++/Python 边界的端到端指标

  • ✅ 拷贝量(CopyVolumeWiring,§13)与 ✅ malloc 足迹/峰值内存(MallocFootprintWiring,§14)——报告值等于 native interposer 观测值,误差不超过有界采样残差。足迹模型诚实地处理 free 侧 max(0,·) 钳制:非负域内精确,域外钳制只会高报(clamp_only_raises),绝不静默少报;
  • ✅ GPU/copy/python 拆分算术有界;❌ GPU/加速器设备获取路径。

3.5 并发与信号安全(TLA+)

  • ✅ combined_stacks 竞态在 bug 配置中可达(具体 4 状态反例),在修复配置中不可能(SignalSafety.tla);
  • ✅ 无死锁、handler 从不阻塞在锁上、输出在公平性下取得进展(Deadlock.tla);
  • ✅ 支撑修复的快照代数(SignalSafety.lean);
  • ⚠️ TLC 仅在给定界内穷举(N=3、MaxHandler=2)。

3.6 有界数据结构

  • ✅ Space-Saving combined_stacks 表永不超容量,且驱逐最小计数(SpaceSaving);
  • ✅ 已接入生产:证明过的 Lean 定义经 LeanToPython 提取为 Python,并与真实 _space_saving_increment 做差分测试(tests/test_verified_space_saving.py)。

4. 形式化发现的生产 Bug:假设审计的回报

形式化迫使每一个隐式假设被命名,从而暴露"代码并未执行证明所需前提"的位置。README 记录了对模型假设的对抗式审计(参见 formal/lean/Scalene/LeakTrackerAudit.lean)发现的 4 个真实缺陷,均已修复:

  1. 泄漏速度报告中的无保护除零(scalene/scalene_json.py 中 velocity_mb_s: leak_velocity / stats.elapsed_time 站点)。compute_leaks 按分配增长率而非墙钟时间把关,因此一次足够短的运行仍可能报出泄漏,此时 elapsed_time 仍为 0.0 → ZeroDivisionError。回归测试:tests/test_leak_velocity_zero_elapsed.py。
  2. 未校验环境变量导致的零/负内存采样窗口(src/include/sampleheap.hpp 中 atol(getenv("SCALENE_ALLOCATION_SAMPLING_WINDOW")))。atol 对 "0" 或任何不可解析字符串返回 0;0 间隔使采样器在每次分配都触发——并违反 MemorySampler.lean 证明为必要的 interval > 0 前提。修复:≤0 时钳制回默认值。
  3. 逐栈 CPU 归一化中的无保护除零(scalene/scalene_json.py 中按 stats.cpu_stats.total_cpu_samples 归一化的 stats.stacks 循环)。total_cpu_samples 可为 0.0 而 stats.stacks 非空——--stacks 的纯内存运行会记录栈条目却从不产生 CPU 样本,而把关的是内存活动而非 CPU。回归测试:tests/test_stacks_zero_cpu_samples.py。
  4. 第 1 项的 CLI 渲染器孪生。Scalene 有三个独立输出渲染器;第 1 项修复只触及 JSON 渲染器,scalene/scalene_output.py(scalene view --cli 路径)在泄漏报告中携带同样的无保护 leak[2] / stats.elapsed_time。回归测试:tests/test_cli_leak_velocity_zero_elapsed.py。

另外,LeakTrackerAudit.lean 卸下了一项隐式安全契约:泄漏公式 1 − (frees+1)/(allocs−frees+2)(scalene/scalene_leak_analysis.py 第 31 行)对分母没有保护,完全依赖不变量 frees ≤ allocs;由于 allocs/frees 在两个独立代码站点递增,模型化原始双计数器递增纪律并证明 frees ≤ allocs 成立,从而 allocs−frees+2 ≥ 2 > 0。(非 bug,但该安全性此前是隐式且未经验证的。)

方法论要点(HANDOFF.md 强调的教训):如果证明的假设照搬代码的隐式假设,验证的是模型而非代码。早期模型把 allocs = unfreed + frees 硬编码进 ℕ 类型,等于预先写死 frees ≤ allocs,因此永远不可能抓住那个除零 bug。规则是:把每个建模假设(0 < total、frees ≤ allocs、interval > 0、每个分母)当作待对实现核验的声明——代码不执行它,就是发现,而不是可以假设掉的东西。

5. 按模块深入:Lean 证明与 TLA+ 规格

5.1 信号/迭代安全——tla/SignalSafety.tla

建模 CPU 采样信号 handler 与 profile 输出迭代器在 stats.combined_stacks 上的竞态。源码映射:

模型元素Scalene 源码含义
HandlerFire(k)scalene/scalene_cpu_profiler.py 的 add_combined_stack(由 cpu_signal_handler 到达)信号 handler 在信号上下文中同步追加栈键
IterStepLive(bug)修复前 scalene_json.py 的 for … in stats.combined_stacks.items()迭代活字典
IterStepSnapshot(修复)scalene/scalene_json.py 的 for stk, hits in list(stats.combined_stacks.items())迭代快照
outPC = "fault"CPython RuntimeError: dictionary changed size during iteration本模型模拟的崩溃

结果:SignalSafety_Bug.cfg(UseSnapshot = FALSE)下 TLC 报告 Invariant NoIterationFault is violated,给出 4 状态反例 OutputStart → HandlerFire(k1) → IterStepLive → fault,精确复现 PR #1067 修复的 bug;SignalSafety_Fix.cfg(UseSnapshot = TRUE)下无错误,99 个不同状态,NoIterationFault、SnapshotSound、SnapshotComplete 全部成立。

快照代数侧(formal/lean/Scalene/SignalSafety.lean)证明 list(...) 快照与后续插入值解耦:snapshot_stable/snapshot_length_fixed(迭代器界在进入时固定,故"迭代期间大小改变"不可能出现)、snapshot_sound/fresh_key_deferred(只访问进入时的键,新键推迟)、insert_preserves_old(被捕获键永不丢失)。snapshot_sound 不依赖任何公理。

5.2 死锁自由与信号安全——tla/Deadlock.tla

建模锁/队列拓扑:N = 3 个后台 ScaleneSigQueue 线程(alloc / memcpy / async),各自只持自己的 RLock;主输出线程按固定全局顺序获取全部锁;信号 handler 只做无锁 queue.put。

源码映射:owner[i] = "worker" ↔ scalene/scalene_sigqueue.py 的 self.lock = RLock() / with self.lock:;OutputAcquire(固定顺序 1..N)↔ scalene/scalene_profiler.py 输出路径在 flush 前按列表顺序取全部 sigqueue 锁;HandlerFire(恒可用、无锁)↔ malloc/free/memcpy handler → 经 queue.SimpleQueue 的 sigq.put(...)。

结果(Deadlock.cfg,N = 3):无错误,72 个不同状态——No deadlock、MutualExclusion、HandlerNeverBlocks(handler 步骤的可用性从不依赖锁状态,故打断持锁者的 handler 仍能进展)、OutputMakesProgress(时序性质,弱/强公平性下输出线程最终进入临界区,无饥饿)。

5.3 归因正确性——Attribution.lean

在 ℚ(精确有理数——这些不变量本意就是精确的,浮点只是近似)上证明守恒与界。核心映射:totalTime_eq_split/totalTime_nonneg ↔ scalene/scalene_cpu_profiler.py 的 c_time = max(elapsed − python_time, 0)、total = python + c;cpu_distribution_conserved ↔ 按帧分发 total_time / total_frames 且 total_cpu_samples += total_time;pythonFraction_nonneg/pythonFraction_le_one ↔ src/include/sampleheap.hpp 的 _pythonCount/_cCount 与 0/0 防护的 python_fraction;pythonBytes_le_count ↔ scalene/scalene_memory_profiler.py 的 memory_python_samples/memory_malloc_samples;footprint_conserved ↔ malloc +count/free −count 的 after = before + (Σmalloc − Σfree)。

5.4 有界重击者记账——SpaceSaving.lean

把 scalene/scalene_utility.py 的 _space_saving_increment(有界 combined_stacks 表,上限 _COMBINED_STACKS_MAX_KEYS = 10_000)建模为纯函数 step : Table → Key → Table(关联列表),匹配三个 Python 分支:已存在→自增、有空间→插入、满→驱逐最小。证明:

  • step_withinCap / fold_withinCap——对任意插入序列表永不超容量(整个设计要保证的界);
  • minCount_le——驱逐总针对最小计数项(Metwally 规则),故重击者存活;
  • present_keeps_size / insert_grows_by_one / evict_keeps_size——各分支的大小行为。

5.5 分析器正确性——ProfilerCorrectness.lean(用户关心的性质)

采样分析器在单次运行上不可能完全精确(它观测执行的随机子集),因此"正确归因"必然是统计性陈述。两半都证明了:

Lean 定理陈述
expect_indicator单样本无偏:一个忠实放置的样本以概率恰为 trueFraction ℓ 归因到行 ℓ
estimator_unbiasedN 样本无偏(头条):对每个样本预算 N ≥ 1,行 ℓ 的报告占比期望等于其真实占比——任意 N 下平均而言正确
variance_indicator单样本方差为 Bernoulli p(1−p)
jointExpect_pair独立性因子分解:不同样本独立(E[XᵢXⱼ] = E[Xᵢ]E[Xⱼ],i≠j)
jointVariance_eq一致性(头条):N 样本估计量方差恰为 p(1−p)/N → 0,报告数值随样本累积收敛于真相

一切建立在一个显式假设(Truth 结构中的采样分布)上:每个定时器 tick 以正比于该行真实运行时间的概率归因于当时真正执行的那一行。对基于信号的 Python 分析器这并非自动——异步信号可能在实际触发事件的几个字节码之后才投递,把样本涂抹到错误行上。这正是 Scalene 工程手段闭合的缝隙:

  • 采样时刻同步(C++)打点记录执行的 (file, line)(whereInPython/whereInPythonWithStack,src/source/pywhere.cpp);
  • scalene/scalene_memory_profiler.py 中的涂抹修正,把 arena/GC 字节从纯算术叶行重新归因;
  • §3 的守恒与界(Attribution.lean)保证无偏性所累加的单样本归因自身良构(分数在 [0,1]、总量保持)。

链条是:忠实的单样本归因(工程 + §3)⇒ 无偏、一致的报告 profile(§6);faithful 假设是二者之间的形式化契约。尚未证明的包括:C++ 打点确实确立忠实采样(需要建模信号投递 + CPython 解释器循环)、以及连续时间序统计版本的 PASTA 定理。

5.6 采样器是 Poisson——ExponentialSampler.lean

scalene/scalene_profiler.py 第 1108 行以 -scale · log(1 − u)(u ~ Uniform[0,1))抽取每个采样间隔——指数分布的逆 CDF 变换。定理:sample_le_iff(逆 CDF 正确性:P(sample ≤ t) = 1 − exp(−t/scale))、sample_nonneg、survival_memoryless(S(s+t) = S(s)·S(t)——指数无记忆性,下一采样时刻与过去独立)、expCDF_zero/expCDF_lt_one。意义:指数间隔 ⇒ 采样时刻构成 Poisson 过程;无记忆性给出 §6 假设的独立性;由 PASTA("Poisson 到达看到时间平均"),Poisson 样本以等于其真实时间占比的概率落在某行——即 trueFraction 分布。固定速率采样不会有此性质(可能与周期程序行为混叠),指数抽取正是让 §6 假设成立的刻意设计。

5.7 另外四个指标——MetricCorrectness.lean

两种证明形状(并非所有指标都是平均):加权平均归因——GPU 利用率(gpuFraction_bounds:gpu_samples/n_gpu_samples 为非负时间积分之比,故为 [0,1] 分数)、memcpy 拷贝量(指数字节钟采样,行级计数对真实拷贝量无偏)、Python-vs-native 拆分(python_c_fraction_sums_one:报告占比划分每个样本且和为 1)。内存泄漏检测——贝叶斯假设检验(不同的一类,scalene/scalene_leak_analysis.py 第 31 行):报告得分是 Laplace Rule of Succession leakScore = 1 − (frees+1)/(unfreed+2)。定理:leakScore_eq、leakScore_nonneg/leakScore_le_one([0,1] 概率)、leakScore_mono_unfreed/leakScore_anti_frees(单调性)、reportsLeak_iff(精确判定规则:当且仅当回收质量 (frees+1)/(unfreed+2) ≤ threshold 时报泄漏)、no_leak_without_evidence/no_leak_when_all_freed(假阳性防护:无证据时得分是先验 ½,低于任何 < ½ 阈值;全释放的行永不被标记)。

5.8 内存采样器——MemorySampler.lean

Scalene 出厂两个分配采样器(src/include/sampleheap.hpp):ThresholdSampler(默认,确定性)——净分配/释放字节跨过 _sampleInterval 时触发,报告精确超出量并重置;threshold_conserves(报告净额 + 亚阈值残差 = 真实净分配,精确无发明/丢失)、threshold_residual_bounded(残差恒 < interval,报告足迹距真相一个间隔内)。PoissonSampler(实验性,#if 0)——每个字节以 1/window 概率采样并按 window 缩放;poisson_unbiased/poisson_unbiased_sum 无偏。

忠实于字面双计数器 C++:模型把 C++ 的两个 uint64_t 计数器(_increments、_decrements)折叠为差 bal;step_bisim/run_bisim 现在证明字面双计数器机器(独立 ℕ 计数器、触发条件 incr ≥ decr + I、双双重置)在 abs (incr, decr) = incr − decr 下与单计数器模型双模拟,threshold2_conserves 把精确守恒转移到按原样书写的双计数器机器——"忠实于 C++"由此是定理而非假设。

5.9 采样下的行级归因——PerLineAttribution.lean

分析器报告 recorded[ℓ] / Σ recorded——两个无偏估计量之比,不严格无偏(E[X/Y] ≠ E[X]/E[Y])。证明实际成立且用户依赖的性质:fraction_of_expectations(由期望记录计数构成的分数等于真实行级字节占比:采样窗口上下相消,无系统性尺度偏差);recorded_fraction_exact(行级计数与真实字节成正比时——确定性阈值极限/大样本 Poisson 极限——报告分数精确等于真实分数,与采样率无关);trueFraction_nonneg/trueFraction_sum_one(行级内存拆分是行上的概率分布,与 CPU 情形完全一致)。

5.10 并发与 fork 下的泄漏跟踪器——LeakTrackerConcurrency.lean

LeakTrackerAudit.lean 对单一顺序事件流证明 frees ≤ allocs;但代码中两个递增站点运行于后台 sig-queue 线程、关闭排空运行于主线程、且整个跟踪器状态跨 fork 复制。本节闭合该缝隙,并证明代码依赖的纪律是必要的:

  • interleave_preserves_inv / sigqueue_then_drain_safe——交错安全:在 RLock 保证每次调用原子后,sig-queue 线程步骤与主线程排空的任意洗牌都保持 frees ≤ allocs——调度顺序无关紧要,这正是锁买来的东西;
  • torn_free_breaks_inv——原子性必要:丢失解除武装的竞态(一次没有对应 disarm 的 free)会使两次撕裂 free 双倍贷记一个武装触发器,得到 frees = allocs + 1——不变量真正依赖 RLock,它是承重的;
  • fork_reset_inv——子进程从初始状态安全重启;
  • partial_fork_reset_breaks_inv——两个字段必须一起重置:清零计数器却保留武装触发器(清了 leak_score 没清 last_malloc_triggered)会立即破坏不变量——这正是 scalene/scalene_statistics.py 在重置 leak_score 时一并重置 last_malloc_triggered 的原因。

5.11 PASTA:采样器→正确性的链接——PoissonArrivals.lean

把视界建模为 M 个等长时间槽,Poisson 到达在给定计数条件下出现在均匀随机时刻(序统计性质)——此处为均匀槽。定理:uniform_landing_eq_timeFraction(PASTA 恒等式:均匀到达落在 ℓ 上的期望指示 = ℓ 的时间占比 timeCount ℓ / M);sum_timeFraction(时间占比构成概率分布);uniform_realizes_trueFraction(卸除:构建由时间线诱导的 ProfilerCorrectness.Truth,其 trueFraction 同时等于时间占比与 Truth.expect (indicator ℓ)——喂养 estimator_unbiased/jointVariance_eq 的假设由采样器机制产生,而非设定)。边界:这是离散时间类比(槽上均匀);连续时间 Poisson 过程的序统计定理未形式化——对 tick 采样的分析器,离散形式即操作性内容。

5.12 跨 C++↔Python 的拷贝量端到端——CopyVolumeWiring.lean

其他 Lean 模块都停在 native/Python 分界线一侧;本模块横跨两侧,同时建模 C++ MemcpySampler 累加器/flush 状态机(src/include/memcpysampler.hpp)与 Python 读取器(process_memcpy_samples,scalene/scalene_memory_profiler.py)。定理:flushed_add_residual(C++ 守恒:写入记录 + 未 flush 累加器 = 观测总字节,无发明无丢失);python_total_eq_flushed(mapfile 传输 + pid 过滤既不丢也不重计进程内字节);roundtrip_conservation(头条:Python 报告的拷贝量 = C++ 观测字节 − 在途残差——scalene view 的拷贝量列忠实反映观测 memcpy 流量);foreign_pid_dropped(子进程记录不污染本进程总量);residual_zero_after_flush(残差以一次采样间隔为界,往返偏差至多为采样粒度)。边界:建模发射器/读取器状态机与字节记账,不建模 mapfile 的低层字节格式解析或部分读处理。

5.13 跨 C++↔Python 的 malloc 足迹端到端——MallocFootprintWiring.lean

更难的路径:当前足迹/峰值内存数字,横跨 C++ SampleHeap 发射器(src/include/sampleheap.hpp)与 Python 读取器 process_malloc_free_samples(scalene/scalene_memory_profiler.py)。建模而非假设的微妙处:Python free 路径在每次 free 时把运行足迹钳到 max(0, current − count);该钳制破坏纯守恒——若 free 把足迹压到 0 以下(启动缺失,见代码注释),它静默加回字节。忽略钳制的朴素模型会虚假地"证明"守恒,因此证明的是诚实的条件陈述:

  • emit_records_sum——C++ ThresholdSampler 发射的记录(带符号)之和等于其 reported 净额(桥接 MemorySampler.threshold_conserves);
  • clamp_is_identity_of_safe——足迹保持 ≥ 0 时钳制是 no-op,Python fold 精确可加;
  • roundtrip_conservation_of_safe——头条:该域内报告足迹增量 = (真实净额 − 采样器残差) / BYTES_PER_MB,端到端成立;
  • clamp_only_raises——无非负假设时,钳制只会把足迹推高:报告足迹恒 ≥ 可加值,故误差单侧(高报存活内存),绝无静默少报——诚实的无条件界;
  • foreign_pid_dropped、newline_marker_skipped——pid 过滤与 NEWLINE continue 忠实丢弃不得计入的记录。

5.14 Python/native 分类器——PythonNativeClassifier.lean 与 ClassifierAccuracy.lean

把每个 CPU 样本的时间在 Python 与 native 间拆分的启发式(scalene/scalene_cpu_profiler.py),按字节码位置四选一:A) 在 CALL → 该行全 native;B) c_time > 0 且前一 CALL 在异行 → c_time native 归彼处、python_time Python 归此处;C) 同行/未找到 → 合并;D) 否则按计算。

  • 定理侧(PythonNativeClassifier.lean):charge_total/classified_conserves——守恒:每个分支中所有 (line, python|native) 桶的总计时等于样本的 cpu = python_time + c_time,分类只移动固定预算;split_atCall/split_together/split_splitToCall——各分支决定的拆分;charge_nonneg——桶计费 ≥ 0(代码经 c_time = max(elapsed − python, 0) 保证);classify_total——分支选择器是观测事实的全函数,任何输入恰好一个分支触发。
  • 精确性侧(ClassifierAccuracy.lean):准确性不能是裸定理——哪个分支正确取决于计时器触发时程序真正在哪里,只能通过 CPython 的一个操作事实观测。该事实被陈述为显式假设 SigDeliverySound:CPython 只在字节码边界运行 Python 级信号 handler,故在 C 调用内到期的计时器被推迟到调用返回,f_lasti 停在 CALL 操作码上——于是 atCall = true ↔ 计时器真正在 C 调用内触发。在此假设下证明:worker_classifier_correct(工作线程分类器精确计费 native——workerNative = trueNative、workerPython = truePython,假设下精确而非近似)、main_branchA_correct(主路径分支 A 在同样假设下精确)、main_worker_agree/main_worker_both_exact(对比:共享域内两条路径做逐字节相同的决定——Scalene 在那里从一个调用点运行一个分类器)、deferral_route_iff(路径仅在虚拟时间、非线程采样模式下分歧,该模式下仅主线程有虚拟计时器可测推迟)、misclassified_when_unsound/missed_when_unsound(若 SigDeliverySound 失败,字节码分类器双侧出错:假阳性把 Python 计为 native,假阴性反之——量化假设买到的确切内容)。

SigDeliverySound 自身(CPython 运行时 + 同步打点属性)保持为假设;tests/test_classifier_soundness.py 通过 sys.monitoring 对正向方向做了确定性差分检验(每个真实 C 调用都从 f_lasti 位于 CALL 操作码的帧发出,5 种工作负载形状下 100%),并记录了反向方向无法从 Python 可靠测试的原因。

6. 证明→生产:提取已验证的 Python

上面证明的计算核心经 LeanToPython 提取为 Python,接入 Scalene 作为已验证预言机(verified oracle):

formal/lean/Scalene/{Attribution,SpaceSaving}.lean   ← 证明(Lean 4.31 + Mathlib)
        │  (同一算法,提取友好片段)
formal/extract/ScaleneExtract.lean                   ← Nat/List 定义,无 Mathlib
        │  lake env lean ScaleneExtract.lean  (LeanToPython,Lean 4.12)
formal/extract/scalene_verified_core.py              ← 生成的 Python(已提交)
        │  作为参考预言机被导入
tests/test_verified_space_saving.py                  ← 与真实 _space_saving_increment 差分测试
  • formal/lean/Scalene/ExtractMirror.lean 是机器检查的完整性桥梁:重新陈述提取定义并证明其等于/满足已证明的定义(minCountX_eq、totalTimeNs_eq_elapsed、pythonFractionPpm_le)。若 ScaleneExtract.lean 偏离已证明模型,该文件编译失败——提取的 Python 不可能悄悄背离被证明的内容。
  • tests/test_verified_space_saving.py 在相同随机键流上运行生产 _space_saving_increment 与提取预言机,断言真实代码上已证明的容量界成立、计数多重集与预言机一致(受害者身份可不同:生产按 dict 顺序、预言机按列表顺序打破最小计数平局——平局打破不在被证明规格内)。

为此上游修复的 LeanToPython 缺陷:提取 Scalene 定义暴露了两个转译器 bug,均已在 upstream 修复(emeryberger/LeanToPython#1):① Bool 型参数分支反转——if isMalloc then a else b 被降级为判别名启发式不认识的 Decidable cases,交换了分支(顺带修复了此前损坏的 mod_pow 语料用例);② 二元 min/max 操作数丢失——Nat.min a b 提取成 min(b),在 stdlibFnToPython? 的包装发射器修复。

7. 模型边界:诚实地陈述假设

  • 原子步骤:每个 TLA+ 动作(一次 handler 追加、一次迭代步、一次锁操作)原子。对 CPython 忠实:Python 级信号 handler 运行于字节码之间,不能撕裂单次 dict 变更——但可以在迭代步之间交错,这正是所建模的。
  • ℚ 而非 float:Lean 证明用精确有理数,确立的是本意守恒/界;真实代码中的浮点舍入是未建模的独立(数值)关注点。
  • TLC 有界常量:Keys = {k1,k2,k3}、N = 3、MaxHandler = 2——使检查穷举且有限;Lean 快照引理把安全论证推广到无界输入。
  • PASTA 以离散时间形式证明(PoissonArrivals.lean),采样器→正确性链接不再是单纯引用;连续时间序统计证明仍未形式化。
  • 拷贝量现已跨 C++/Python 边界端到端建模(CopyVolumeWiring.lean)——首个跨 native 边界证明的指标。
  • 分类器准确性在 SigDeliverySound 条件下建模(§17);该假设本身(运行时 + 同步打点)陈述而非证明。
  • 尚未建模(out of scope):C++ 分配器内部线程局部 _pythonCount/_cCount 记账;mapfile IPC 字节格式解析(拷贝量模型抽象记录而非磁盘编码);fork() 超出 stop/join 纪律的锁状态隐患;GPU/加速器设备路径。

8. 复现:从干净检出重跑全部证明与模型检验

8.1 Lean(证明)——本地运行

cd formal/lean
lake exe cache get      # 首次拉取预构建 Mathlib oleans
lake build              # 构建 Scalene.Attribution + Scalene.SignalSafety;0 sorry

验证证明只依赖标准公理:

echo 'import Scalene
open Scalene
#print axioms cpu_distribution_conserved
#print axioms footprint_conserved
#print axioms pythonFraction_le_one' > /tmp/ax.lean
lake env lean /tmp/ax.lean
# => 每个仅依赖 [propext, Classical.choice, Quot.sound]

工具链版本见 formal/lean/lean-toolchain(leanprover/lean4:v4.31.0),依赖见 formal/lean/lakefile.toml。

8.2 TLA+(模型检验)——用 TLC 运行

需要 Java + tla2tools.jar:

cd formal/tla
J=path/to/tla2tools.jar
java -cp $J tlc2.TLC -config SignalSafety_Fix.cfg SignalSafety.tla   # 无错误,99 状态
java -cp $J tlc2.TLC -config SignalSafety_Bug.cfg SignalSafety.tla   # 违反 NoIterationFault(反例)
java -XX:+UseParallelGC -cp $J tlc2.TLC -config Deadlock.cfg Deadlock.tla  # 无错误,72 状态

8.3 重新生成提取的 Python

需要 LeanToPython 检出(Lean 4.12)。把 formal/extract/ScaleneExtract.lean 复制进去并执行:

lake env lean ScaleneExtract.lean > scalene_verified_core.py

然后重跑 tests/test_verified_space_saving.py。(注意:该测试在 Python < 3.10 时跳过,因为生成代码使用 PEP 604 X | Y 联合类型。)

9. 目录布局速查

formal/
  README.md                     # 本文档:§1–§17、源码映射、边界、复现
  STATUS.md                     # 子系统状态映射(proven / partial / unproven)
  HANDOFF.md                    # 交接/后续笔记(方法、已合并、开放项)
  tla/
    SignalSafety.tla            # handler-vs-iterator 竞态
    SignalSafety_Fix.cfg        # 快照修复:全部不变量成立
    SignalSafety_Bug.cfg        # 活迭代:NoIterationFault 被违反
    Deadlock.tla                # 锁/队列拓扑
    Deadlock.cfg                # 无死锁 + 信号安全 + 活性
  lean/
    lakefile.toml               # Mathlib 依赖
    lean-toolchain              # leanprover/lean4:v4.31.0
    Scalene.lean                # 顶层导入(17 个模块全量)
    Scalene/
      Attribution.lean          # CPU/内存守恒 + 分数界
      SignalSafety.lean         # 快照迭代代数
      SpaceSaving.lean          # 有界 combined_stacks 容量证明
      ExtractMirror.lean        # 已证明 == 已提取的完整性桥梁
      ProfilerCorrectness.lean  # 无偏 + 一致归因(用户向规格)
      ExponentialSampler.lean   # 采样器是 Poisson:逆 CDF + 无记忆性(§7)
      MetricCorrectness.lean    # GPU / 拷贝量 / python 拆分 / 泄漏检测(§8)
      MemorySampler.lean        # 阈值 + Poisson 采样器;双计数器双模拟(§9)
      PerLineAttribution.lean   # 采样下的行级字节分数(§10)
      LeakTrackerAudit.lean     # 泄漏分母安全(frees ≤ allocs)
      LeakTrackerConcurrency.lean # 交错 + fork 下不变量;序列化必要性(§11)
      PoissonArrivals.lean      # PASTA 离散形式(§12)
      CopyVolumeWiring.lean     # 拷贝量端到端 C++↔Python(§13)
      MallocFootprintWiring.lean # malloc 足迹端到端;钳制诚实建模(§14)
      PythonNativeClassifier.lean # 分类器守恒与分支特征(§15)
      PerLineMallocAttribution.lean # 行级 malloc 记账守恒(§16)
      ClassifierAccuracy.lean   # 分类器精确性:双路径 + SigDeliverySound(§17)
  extract/
    ScaleneExtract.lean         # 提取友好定义(Lean 4.12,LeanToPython)
    scalene_verified_core.py    # 生成的 Python 预言机(已提交)

把预言机与生产代码绑定的差分测试位于 tests/test_verified_space_saving.py;状态总览请查阅 formal/STATUS.md,方法要点与后续工作清单在 formal/HANDOFF.md。

10. 一句话总结

已证明:统计核心(CPU 无偏 + 一致,且 PASTA 链接已闭合;内存采样;含并发的泄漏检测)、守恒律(含 Python/native 分类器的预算守恒与行级 malloc 记账)——两项跨 C++/Python 边界的端到端指标(拷贝量与 malloc 足迹,后者诚实建模 free 侧钳制)、有界结构容量,以及信号/死锁安全拓扑;分类器在两条代码路径上的单样本精确性,在显式 CPython 信号投递假设下证明为精确。未证明:SigDeliverySound 本身、GPU 获取等设备管道、输出渲染与浮点。

  • 开发工具
  • 性能测试
  • AI 应用

【免费下载链接】scalene

Scalene: a high-performance, high-precision CPU, GPU, and memory profiler for Python with AI-powered optimization proposals

项目地址: https://gitcode.com/gh_mirrors/sc/scalene
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值