【免费下载链接】scalene
Scalene: a high-performance, high-precision CPU, GPU, and memory profiler for Python with AI-powered optimization proposals
本文围绕当前仓库 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+ / TLC | formal/tla/ | 交错安全性、死锁自由、活性(liveness) | 穷举模型检验 |
| Lean 4 | formal/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 个真实缺陷,均已修复:
- 泄漏速度报告中的无保护除零(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。 - 未校验环境变量导致的零/负内存采样窗口(src/include/sampleheap.hpp 中
atol(getenv("SCALENE_ALLOCATION_SAMPLING_WINDOW")))。atol对"0"或任何不可解析字符串返回 0;0 间隔使采样器在每次分配都触发——并违反MemorySampler.lean证明为必要的interval > 0前提。修复:≤0 时钳制回默认值。 - 逐栈 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。 - 第 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_unbiased | N 样本无偏(头条):对每个样本预算 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 过滤与 NEWLINEcontinue忠实丢弃不得计入的记录。
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 获取等设备管道、输出渲染与浮点。
【免费下载链接】scalene
Scalene: a high-performance, high-precision CPU, GPU, and memory profiler for Python with AI-powered optimization proposals
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



