model 设计

设计目标

其他每个包都通过 model 交换数据。该包确定了一套词汇:带版本、对失败显式表达,并且足够完整,使结果可以追溯到其协议、输入和环境。除身份函数和访问器外,它不拥有任何行为,因此每一层都可以依赖它而不会形成循环。

数学背景

作为等价关系的环境兼容性

把环境快照写作 e=(σ,π,ρ)e = (\sigma, \pi, \rho),其中 σ\sigma 为语义部分,π\pi 为性能部分,ρ\rho 为来源信息。environment_compatible 为

e∼e′  ⟺  (σ,π)=(σ′,π′),e \sim e' \iff (\sigma, \pi) = (\sigma', \pi'),

逐字段比较。这是投影 p(e)=(σ,π)p(e) = (\sigma, \pi) 的核:e∼e′  ⟺  p(e)=p(e′)e \sim e' \iff p(e) = p(e')。任何映射的核都是等价关系,因为相等是自反、对称且传递的。因此,一组运行的快照会划分为若干类,比较恰好在同一类内才有意义。传递性在实践中很重要:如果运行 A 可以与 B 比较,B 可以与 C 比较,那么 A 就可以与 C 比较,因此一次存储的基线可以服务于同一类中之后的任何运行。

带容差的关系(例如“CPU 频率相差在 5 % 以内”)不是传递的,无法把运行划分为类;这就是检查采用声明字符串完全相等的原因。

作为和类型的结果

一次操作要么产生一个值,要么以几种值得区别对待的方式之一失败。把结果建模为一个和类型,

Outcome(V)=V+V×Flags+Trap×Flags+3⋅Reason+Exit×Stderr+Ms×Reason,\text{Outcome}(V) = V + V \times \text{Flags} + \text{Trap} \times \text{Flags} + 3 \cdot \text{Reason} + \text{Exit} \times \text{Stderr} + \text{Ms} \times \text{Reason},

使各种情形在类型、事件和报告中保持区分。value_option 是到前两个分量上的投影;其他每个分量都没有值,包含它的计时批次是无效的。

设计决策

带版本的标识符

问题。 旧 JSONL 的读取器不能悄无声息地误读新字段。选择。 三个版本枚举产生标识符 mmkp_1(协议词汇)、mmka_1(事件产物)和 mmks_1(Plot IR)。每个事件都携带 artifact_version;读取器拒绝未知版本。理由。 增量式的变化保持版本不变;破坏性变化则增加 V2 和一个生命周期条目,旧的读取器会明确失败,而不是去猜测。

三部分的环境快照

问题。 有些差异会使正确性失效(目标、标志、ABI),有些会使计时失效(CPU、GC、时钟、频率策略),还有些只是簿记信息(主机名、时间、修订版本)。选择。 SemanticEnvironment、PerformanceEnvironment 和 ProvenanceEnvironment,兼容性取决于前两者。理由。 在不同主机上、声明的硬件相同的两次运行仍然可以比较;标志不同的运行则不行。快照记录的是你声明的内容;mare_mark 不会探测机器。

显式的协议

问题。 “跑了基准测试”掩盖了预热、批次大小、顺序、样本数和决策阈值。选择。 RunProtocol 为所有这些命名,protocol_identity 把预热次数、验证性样本数和实际阈值折叠成一个短键。理由。 只有协议可复现,结果才可复现。该键有意保持简短,并不覆盖每个字段;请在将要比较的结果旁存储完整的协议。

失败种类,而非字符串

ExecutionOutcome 把工作进程崩溃(Aborted)与超时、解码问题、不受支持的输入和被接受的偏差区分开来,ValidationStatus 在判定器一侧与之对应。报告在能力矩阵中分别统计每一种,因此“不支持”永远不会被读成“错误”,超时也永远不会被读成一次慢的测量。

带构造函数的只读记录

记录是 pub struct:字段在任何地方都可读,值通过 new 构建。用户需要构建的枚举是 pub(all)。IntervalMode 和 CrossoverResult 通过函数构建,使其构造始终只有一条有文档记录的路径。

决策与部署类型

ScaleBoundary、Region、ParetoPoint、DeploymentPolicy 和 CrossoverResult 描述测得的偏好如何成为部署规则。它们位于 model 中,使 experiment、tune 和应用无需相互依赖即可对它们达成一致。

正确性与不变量

  • environment_compatible 是一个等价关系(推导见上文)。
  • 对每个版本枚举,X::identifier() 都是 implementation() + "_" + version(),且 V1 值都是 Supported。
  • ExecutionOutcome::value_option(o) 恰好对 Value 和 RaisedFlags 为 Some;flags(o) 只对 RaisedFlags 和 Trapped 非空。
  • OperationResult::completed(v, c) 等于输出为空且没有退出码的 OperationResult::new(Value(v), Some(c))。
  • 构造函数不复制任何东西,也不验证任何东西:验证属于 runner.validate_protocol 和 BenchSpec::compile。

被否决的方案

  • 探测环境。 读取 CPU 型号、调速器和 GC 设置需要针对各平台的代码和权限;声明的值则是显式且可测试的。
  • 带容差的兼容性。 不具有传递性(见上文)。
  • 自由格式的结果字符串。 会丢失失败种类之间的区分。
  • 单个扁平的协议字符串。 不可读且未经验证。

边界

  • 不验证值;没有 IO;没有序列化(JSON 位于 event、report 和 tune_gemm 中)。
  • protocol_identity 只覆盖三个字段。
  • ExperimentDesign、ValidationCoverage、OutlierPolicy 和 WorkspaceScope 是记录下来的意图;运行器不解释它们。
  • ProtocolVersion、ArtifactVersion 和 SchemaVersion 各只有一个版本;没有迁移代码。