floating_vs_decmial_x API

包 Luna-Flow/diff_bench/floating_vs_decmial_x 在相同的十进制输入上比较 moonbitlang/x/decimal(X)与 Luna-Flow/floating/decimal_gda@0.7.1(GDA),用 BigInt 预言机检验两者,并用 Mare Mark 测量。它是保存在 GitHub 仓库中用于复现的基准框架,不是其他项目的运行时依赖。函数在输入超出约定时中止。

包名保留了历史拼写 decmial。教程演示条目的用法,设计页推导精度约定。源码:src/floating_vs_decmial_x/。

示例以 @floating_vs_decmial_x 和 moonbitlang/core/bigint 的形式导入。

中立的十进制模型

这些条目与 dzmingli_vs_floating 中的定义相同;两个包各自保留一份副本,使每个基准都能独立构建。

DecimalValue

DecimalValue 是与实现无关的十进制数 (c,s)(c, s),其值为 c⋅10−sc \cdot 10^{-s}。

pub(all) struct DecimalValue {
  coefficient : @bigint.BigInt
  scale : Int
}

normalize

normalize 在标度保持非负的前提下去掉系数末尾的零;零变为 (0,0)(0, 0)。

pub fn normalize(DecimalValue) -> DecimalValue

canonical_string

canonical_string 以不带指数的位置记数法输出规范化后的值;相等的数给出相等的字符串。

pub fn canonical_string(DecimalValue) -> String

parse_decimal_value

parse_decimal_value 把带可选符号、小数点和指数的有限十进制字符串读入为规范化的值。遇到其他内容时中止。

pub fn parse_decimal_value(String) -> DecimalValue

OracleResult

OracleResult 保存一个规范化值及其规范字符串。

pub struct OracleResult {
  value : DecimalValue
  canonical : String
}

to_oracle_result

to_oracle_result 规范化一个值,并将其与规范字符串配对。

pub fn to_oracle_result(DecimalValue) -> OracleResult
test "neutral decimal model" {
  let v = @floating_vs_decmial_x.parse_decimal_value("0.00120")
  inspect(v.coefficient, content="12")
  inspect(v.scale, content="4")
  inspect(@floating_vs_decmial_x.canonical_string(v), content="0.0012")
  let r = @floating_vs_decmial_x.to_oracle_result({ coefficient: -1200N, scale: 2 })
  inspect(r.canonical, content="-12")
}

精确算术

add

add 在把两个操作数对齐到较大标度后返回精确和。

pub fn add(DecimalValue, DecimalValue) -> DecimalValue

subtract

subtract 在同样对齐后返回精确差。

pub fn subtract(DecimalValue, DecimalValue) -> DecimalValue

multiply

multiply 返回精确积。

pub fn multiply(DecimalValue, DecimalValue) -> DecimalValue

compare

compare 返回 -1、0 或 1。

pub fn compare(DecimalValue, DecimalValue) -> Int
test "exact arithmetic" {
  let a = @floating_vs_decmial_x.parse_decimal_value("123.45")
  let b = @floating_vs_decmial_x.parse_decimal_value("-4.5")
  let show = @floating_vs_decmial_x.canonical_string
  inspect(show(@floating_vs_decmial_x.add(a, b)), content="118.95")
  inspect(show(@floating_vs_decmial_x.subtract(a, b)), content="127.95")
  inspect(show(@floating_vs_decmial_x.multiply(a, b)), content="-555.525")
  inspect(@floating_vs_decmial_x.compare(b, a), content="-1")
}

参考预言机

预言机模拟 X 的结果策略:至多 28 位小数,多出的位向零截断。

oracle_multiply

oracle_multiply 返回精确积;当其标度超过 28 时向零截断到 28 位小数。

pub fn oracle_multiply(DecimalValue, DecimalValue) -> DecimalValue

oracle_divide

oracle_divide 返回向零截断到 28 位小数的商,只用整数除法计算。

pub fn oracle_divide(DecimalValue, DecimalValue) -> DecimalValue

记 k=28+sr−sℓk = 28 + s_r - s_\ell,当 k≥0k \ge 0 时结果系数为 trunc⁡(cℓ⋅10k/cr)\operatorname{trunc}(c_\ell \cdot 10^{k} / c_r),否则为 trunc⁡(trunc⁡(cℓ/10−k)/cr)\operatorname{trunc}(\operatorname{trunc}(c_\ell / 10^{-k}) / c_r),标度为 28。除数为零时中止。与 dzmingli_vs_floating 不同,这里循环小数的商没有问题。

oracle_operation

oracle_operation 按上述规则计算 Operation:精确的 Add、Subtract 和 Compare,截断的 Multiply 和 Divide,以及 Parse 和 Format 的恒等映射。

pub fn oracle_operation(Operation, DecimalValue, DecimalValue) -> OracleResult

预言机不依赖 DecimalSemantics;设计页说明了为什么一个预言机能同时服务两个组。

test "reference oracle" {
  let one = @floating_vs_decmial_x.parse_decimal_value("1")
  let three = @floating_vs_decmial_x.parse_decimal_value("3")
  inspect(
    @floating_vs_decmial_x.oracle_operation(Divide, one, three).canonical,
    content="0.3333333333333333333333333333",
  )
  let tiny : @floating_vs_decmial_x.DecimalValue = { coefficient: 1N, scale: 20 }
  let wide : @floating_vs_decmial_x.DecimalValue = { coefficient: 123456789N, scale: 10 }
  inspect(
    @floating_vs_decmial_x.canonical_string(@floating_vs_decmial_x.oracle_multiply(tiny, wide)),
    content="0.0000000000000000000001234567",
  )
}

运算、语义与套件

Operation

Operation 列出本基准的运算族。

pub(all) enum Operation {
  Add
  Subtract
  Multiply
  Divide
  Compare
  Parse
  Format
}

Parse 与 Format 在两边都返回预先准备好的左操作数,可执行程序不运行它们。

operation_name

operation_name 返回记录中使用的蛇形命名,例如 "divide"。

pub fn operation_name(Operation) -> String

DecimalSemantics

DecimalSemantics 选择两个实现都必须满足的语义约定。

pub(all) enum DecimalSemantics {
  ExactOverlap
  XCompatible
}

ExactOverlap 使用两个库都能表示其精确结果的输入,因此两边都不发生舍入。XCompatible 在 GDA 中通过在 multiply 和 divide 之后执行一次 quantize 来重现 X 的 28 位小数截断,该步骤位于计时路径之内。

decimal_semantics_name

decimal_semantics_name 返回 "exact_overlap" 或 "x_compatible"。

pub fn decimal_semantics_name(DecimalSemantics) -> String

OperandShape

OperandShape 为生成的用例打标签;该标签不影响操作数。

pub(all) enum OperandShape {
  Small
  Large
  Boundary
  Cancellation
  Repeating
}

TimingScope

TimingScope 为套件元数据命名一次计时调用所包含的内容。

pub(all) enum TimingScope {
  ConstructionAndOperation
  OperationOnly
}

Mare Mark 运行器不读取它。比较记录改为携带字符串形式的范围:XCompatible 下的 Multiply 与 Divide 为 "semantic_equivalent_pipeline",其余为 "arithmetic_only"。

Suite

Suite 是基准套件的描述性元数据。

pub struct Suite {
  name : String
  operations : Array[Operation]
  scales : Array[Int]
  timing_scope : TimingScope
  warmup : Int
  samples : Int
}

default_suite

default_suite 返回套件 "decimal-x-floating-gda":全部七种运算、标度 [0, 2, 6, 18, 28]、OperationOnly、5 次预热和 20 个样本。

pub fn default_suite() -> Suite

suite_case_count

suite_case_count 返回 运算数 × 标度数 × 每种运算的用例数。

pub fn suite_case_count(Suite, Int) -> Int
test "operations, semantics and suites" {
  inspect(@floating_vs_decmial_x.operation_name(Divide), content="divide")
  inspect(@floating_vs_decmial_x.decimal_semantics_name(XCompatible), content="x_compatible")
  let suite = @floating_vs_decmial_x.default_suite()
  inspect(@floating_vs_decmial_x.suite_case_count(suite, 2), content="70")
}

确定性用例

generate_decimal

generate_decimal(seed, digits, scale, negative) 构造一个系数恰有 digits 位(每位在 1..91..9 中)的十进制字符串,其中 scale 位是小数。若 digits <= 0 或 scale < 0 则中止。

pub fn generate_decimal(Int, Int, Int, Bool) -> String

BenchmarkCase

BenchmarkCase 是一个生成的用例;其字符串就是序列化边界。

pub struct BenchmarkCase {
  id : String
  operation : Operation
  shape : OperandShape
  left : String
  right : String
  scale : Int
}

generate_cases

generate_cases(seed, count, operation) 返回 count 个可复现的用例,位数为 1..241..24、标度为 0..80..8,不使用时钟或全局随机状态。

pub fn generate_cases(Int, Int, Operation) -> Array[BenchmarkCase]

serialize_case

serialize_case 用换行连接 decimal-neutral-v1 与用例各字段。

pub fn serialize_case(BenchmarkCase) -> String

fingerprint_case

fingerprint_case 用 Mare Mark 的 stable_fingerprint 对 serialize_case 求哈希。

pub fn fingerprint_case(BenchmarkCase) -> String

expand_digit_scales

expand_digit_scales(sizes, n) 把每个系数规模重复 n 次,每次重复对应一个 Mare Mark 数据集。

pub fn expand_digit_scales(Array[Int], Int) -> Array[Int]
test "deterministic cases" {
  inspect(@floating_vs_decmial_x.generate_decimal(0, 2, 4, true), content="-0.0018")
  let first = @floating_vs_decmial_x.generate_cases(17, 4, Multiply)
  let again = @floating_vs_decmial_x.generate_cases(17, 4, Multiply)
  inspect(first[3].right == again[3].right, content="true")
  assert_eq(@floating_vs_decmial_x.expand_digit_scales([1, 4], 3), [1, 1, 1, 4, 4, 4])
}

夹具与适配器

working_precision

working_precision(operation, left, right, semantics?) 返回夹具的 GDA 上下文精度。semantics 默认为 XCompatible。

pub fn working_precision(Operation, DecimalValue, DecimalValue, semantics? : DecimalSemantics) -> Int

记 dℓ,drd_\ell, d_r 为系数位数,sℓ,srs_\ell, s_r 为标度:

运算精度
Add, Subtractmax⁡(dℓ,dr)+∣sℓ−sr∣+2\max(d_\ell, d_r) + \lvert s_\ell - s_r \rvert + 2
Multiplydℓ+dr+1d_\ell + d_r + 1
Divide, ExactOverlapdℓ+dr+2d_\ell + d_r + 2
Divide, XCompatiblemax⁡(1, dℓ−sℓ−dr+sr+1)+30\max(1,\ d_\ell - s_\ell - d_r + s_r + 1) + 30
Compare, Parse, Formatmax⁡(dℓ,dr)+1\max(d_\ell, d_r) + 1

设计页推导了每一行及其覆盖的操作数类别。

DecimalFixture

DecimalFixture 保存两个实现在计时前构造好的操作数。

pub struct DecimalFixture {
  neutral_left : DecimalValue
  neutral_right : DecimalValue
  operation : Operation
  semantics : DecimalSemantics
  x_left : @decimal.Decimal
  x_right : @decimal.Decimal
  gda_left : @decimal_gda.Decimal
  gda_right : @decimal_gda.Decimal
  gda_quantum_28 : @decimal_gda.Decimal
  gda_context : @decimal_gda.GdaContext
}

@decimal 指 moonbitlang/x/decimal。gda_quantum_28 为 10−2810^{-28},即 XCompatible 后处理所用的量子。

prepare_fixture

prepare_fixture(operation, left, right, semantics?) 计算精度 pp,为 X 和 GDA 分别转换两个操作数,并构造精度为 pp、向零舍入的 GDA 上下文。

pub fn prepare_fixture(Operation, DecimalValue, DecimalValue, semantics? : DecimalSemantics) -> DecimalFixture

semantics 默认为 XCompatible。GDA 操作数以精度 pp 和半偶舍入解析,因此有效位数超过 pp 的操作数会被舍入;参见已知限制。

x_from_neutral

x_from_neutral 用 Decimal::new 转换值;当标度超出 X 的范围 0..280..28 时中止。

pub fn x_from_neutral(DecimalValue) -> @decimal.Decimal

gda_from_neutral

gda_from_neutral(value, precision) 用 Decimal::from_string(precision~) 解析规范字符串。

pub fn gda_from_neutral(DecimalValue, Int) -> @decimal_gda.Decimal

DecimalObservation

DecimalObservation 是一次适配器调用的原始结果。

pub(all) enum DecimalObservation {
  X(@decimal.Decimal)
  XCompare(Int)
  Gda(@decimal_gda.GdaOutcome[@decimal_gda.Decimal])
}

XCompare 保存已被归约为 -1、0 或 1 的 X 比较结果。

run_x

run_x 用 X 的运算符 +、-、*、/ 或 Compare::compare 执行夹具的运算。

pub fn run_x(DecimalFixture) -> DecimalObservation

run_gda

run_gda 用 floating GDA 执行夹具的运算;在 XCompatible 下,它还把标度超过 28 的积以及所有商量化到 10−2810^{-28}。

pub fn run_gda(DecimalFixture) -> DecimalObservation

canonical_x

canonical_x 根据 X 十进制数的系数和标度把它转换为规范化的 DecimalValue。

pub fn canonical_x(@decimal.Decimal) -> DecimalValue

canonical_observation

canonical_observation 把任意观测值转换为规范化的 DecimalValue;GDA 结果经由 to_string 和 parse_decimal_value。

pub fn canonical_observation(DecimalObservation) -> DecimalValue
test "fixtures and adapters" {
  let one = @floating_vs_decmial_x.parse_decimal_value("1")
  let three = @floating_vs_decmial_x.parse_decimal_value("3")
  inspect(@floating_vs_decmial_x.working_precision(Divide, one, three), content="31")
  let fixture = @floating_vs_decmial_x.prepare_fixture(Divide, one, three)
  let show = (o : @floating_vs_decmial_x.DecimalObservation) => {
    @floating_vs_decmial_x.canonical_string(
      @floating_vs_decmial_x.canonical_observation(o),
    )
  }
  inspect(show(@floating_vs_decmial_x.run_x(fixture)), content="0.3333333333333333333333333333")
  inspect(show(@floating_vs_decmial_x.run_gda(fixture)), content="0.3333333333333333333333333333")
}

Mare Mark 集成

MareDecimalInput

MareDecimalInput 是一个 Mare Mark 数据集实例化后的输入。

pub(all) struct MareDecimalInput {
  left : DecimalValue
  right : DecimalValue
  operation : Operation
  semantics : DecimalSemantics
  digits : Int
  left_scale : Int
  right_scale : Int
}

PerformanceResult

PerformanceResult 根据配对的确认性样本汇总一种运算、一个语义组和一个系数规模的结果。

pub(all) struct PerformanceResult {
  operation : Operation
  semantics : DecimalSemantics
  timing_scope : String
  digits : Int
  x_median_us : Double
  gda_median_us : Double
  gda_relative_delta_pct : Double
  x_speedup_vs_gda : Double
  decision : String
  samples : Int
}

x_speedup_vs_gda 是 GDA 中位数除以 X 中位数(大于 11 表示 X 更快)。decision 为 "gda_faster"、"x_faster"、"equivalent"、"invalid" 或 "unknown"。与 dzmingli_vs_floating 不同,结果总是配对的;校验失败只体现在报告计数中。

PerformanceResult::to_json

PerformanceResult::to_json 输出一条制品版本为 mmka_1 的 "comparison" JSONL 记录。

pub fn PerformanceResult::to_json(Self) -> String

MareBenchmarkReport

MareBenchmarkReport 汇集一次运行的结果、原始 JSONL 和校验计数;validation_count 同时统计通过和失败的校验。

pub(all) struct MareBenchmarkReport {
  results : Array[PerformanceResult]
  jsonl : String
  validation_count : Int
  failed_count : Int
}

smoke_protocol

smoke_protocol 返回简短的测试协议:2 次预热、0.5 ms 校准批次和 3 次确认性重复。

pub fn smoke_protocol() -> @model.RunProtocol

default_benchmark_protocol

default_benchmark_protocol 返回已发布运行所用的协议:5 次预热、5 ms 校准批次、种子为 0xDEC1A1 的平衡分块、只报告离群值,以及 20 次确认性重复。

pub fn default_benchmark_protocol() -> @model.RunProtocol

benchmark_environment

benchmark_environment 根据 MARE_* 环境变量构造环境快照,缺失的变量取固定默认值。

pub fn benchmark_environment() -> @model.EnvironmentSnapshot

run_mare_benchmark

run_mare_benchmark(operations, semantics, digit_scales, protocol, seed) 在一个语义组下为每种运算运行一次 Mare Mark 实验,并返回合并后的报告。

pub async fn run_mare_benchmark(Array[Operation], DecimalSemantics, Array[Int], @model.RunProtocol, UInt64) -> MareBenchmarkReport

每个数据集在计时前都先与预言机核对。该函数不会因失败而中止;调用者应检查 failed_count。它在 native 和 js 目标上运行。

async test "mare mark smoke run" {
  let report = @floating_vs_decmial_x.run_mare_benchmark(
    [Divide],
    XCompatible,
    [16],
    @floating_vs_decmial_x.smoke_protocol(),
    42UL,
  )
  inspect(report.failed_count, content="0")
  inspect(report.results[0].timing_scope, content="semantic_equivalent_pipeline")
}

报告

mare_performance_report_document

mare_performance_report_document(results, target, run_id) 构建 Plot IR 文档:每种运算与语义组一张延迟图,每个组一张 X 对 GDA 的加速比图。

pub fn mare_performance_report_document(Array[PerformanceResult], String, String, validation_count? : Int, failed_count? : Int) -> @ir_model.PlotDocument

语料汇总为:总数 validation_count + failed_count、通过 validation_count、失败 failed_count。由于 validation_count 已包含失败数,只有当 failed_count 为零时该汇总才正确;参见设计页。

mare_performance_report_html

mare_performance_report_html 把同一文档渲染为自包含的 HTML 页面。

pub fn mare_performance_report_html(Array[PerformanceResult], String, String, validation_count? : Int, failed_count? : Int) -> String
test "performance report" {
  let result : @floating_vs_decmial_x.PerformanceResult = {
    operation: Add,
    semantics: ExactOverlap,
    timing_scope: "arithmetic_only",
    digits: 16,
    x_median_us: 1.0,
    gda_median_us: 2.0,
    gda_relative_delta_pct: 100.0,
    x_speedup_vs_gda: 2.0,
    decision: "x_faster",
    samples: 60,
  }
  let html = @floating_vs_decmial_x.mare_performance_report_html(
    [result],
    "native",
    "example",
    validation_count=2,
  )
  inspect(html.contains("x_decimal"), content="true")
}