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 是与实现无关的十进制数 ,其值为 。
pub(all) struct DecimalValue {
coefficient : @bigint.BigInt
scale : Int
}
normalize
normalize 在标度保持非负的前提下去掉系数末尾的零;零变为 。
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
记 ,当 时结果系数为 ,否则为 ,标度为 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 位(每位在 中)的十进制字符串,其中 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 个可复现的用例,位数为 、标度为 ,不使用时钟或全局随机状态。
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
记 为系数位数, 为标度:
| 运算 | 精度 |
|---|---|
Add, Subtract | |
Multiply | |
Divide, ExactOverlap | |
Divide, XCompatible | |
Compare, Parse, Format |
设计页推导了每一行及其覆盖的操作数类别。
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 为 ,即 XCompatible 后处理所用的量子。
prepare_fixture
prepare_fixture(operation, left, right, semantics?) 计算精度 ,为 X 和 GDA 分别转换两个操作数,并构造精度为 、向零舍入的 GDA 上下文。
pub fn prepare_fixture(Operation, DecimalValue, DecimalValue, semantics? : DecimalSemantics) -> DecimalFixture
semantics 默认为 XCompatible。GDA 操作数以精度 和半偶舍入解析,因此有效位数超过 的操作数会被舍入;参见已知限制。
x_from_neutral
x_from_neutral 用 Decimal::new 转换值;当标度超出 X 的范围 时中止。
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 的积以及所有商量化到 。
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 中位数(大于 表示 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")
}