dzmingli_vs_floating API
包 Luna-Flow/diff_bench/dzmingli_vs_floating 在相同的十进制输入上比较 DzmingLi/decimal@0.2.2 与 Luna-Flow/floating/decimal_gda@0.7.1,用精确的 BigInt 预言机(oracle)检验两者,并用 Mare Mark 测量它们。它是基准测试框架,而不是十进制库:每个函数在输入超出其约定时都会中止,而不是返回 Result,因为无效的测试夹具(fixture)本身就是框架的缺陷。
教程演示这些条目的用法,设计页推导精度约定与预言机。源码:src/dzmingli_vs_floating/。
本页示例都是黑盒测试:它们以 @dzmingli_vs_floating 和 moonbitlang/core/bigint 的形式导入包。
中立的十进制模型
DecimalValue
DecimalValue 是与实现无关的十进制数,所有夹具、预言机结果和观测值都会转换为它。
pub(all) struct DecimalValue {
coefficient : @bigint.BigInt
scale : Int
}
值 表示 。符号保存在系数中。本包产生的值满足 ;负指数由 parse_decimal_value 乘入系数。许多不同的对表示同一个数,例如 与 ;normalize 从中选出一个。
normalize
normalize 在标度保持非负的前提下去掉系数末尾的十进制零。
pub fn normalize(DecimalValue) -> DecimalValue
零变为 。对其他值,结果满足 或系数不能被 整除。这一形式对每个数都是唯一的,因此两个值相等当且仅当它们的规范化形式相等(证明见设计页)。代价:每去掉一个零做一次 BigInt 除法。
canonical_string
canonical_string 以不带指数的普通位置记数法输出规范化后的值。
pub fn canonical_string(DecimalValue) -> String
该字符串对负值带前导 -,当 时带 0. 前缀和前导小数零,且没有末尾零。它在数上是单射,因此字符串相等即数值相等。该字符串是本包所有校验的比较边界。
parse_decimal_value
parse_decimal_value 把有限十进制字符串读入为规范化的 DecimalValue。
pub fn parse_decimal_value(String) -> DecimalValue
接受的语法:可选的前导符号、至多含一个 . 的数字串,以及可选的指数 e 或 E(指数可带符号)。数字逐位累加到 BigInt 中,因此解析器不依赖 BigInt::from_string。遇到空数字串、无数字的指数、NaN、Infinity 或任何其他字符时中止。指数保存在 Int 中。
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 = @dzmingli_vs_floating.parse_decimal_value("-12.3400e1")
inspect(v.coefficient, content="-1234")
inspect(v.scale, content="1")
let wide : @dzmingli_vs_floating.DecimalValue = { coefficient: 1200N, scale: 2 }
inspect(@dzmingli_vs_floating.normalize(wide).scale, content="0")
inspect(@dzmingli_vs_floating.canonical_string(v), content="-123.4")
inspect(@dzmingli_vs_floating.to_oracle_result(wide).canonical, content="12")
}
精确算术
这些函数在 DecimalValue 上精确计算;没有精度,也没有舍入。结果是规范化的。
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 = @dzmingli_vs_floating.parse_decimal_value("1.25")
let b = @dzmingli_vs_floating.parse_decimal_value("-0.005")
let show = @dzmingli_vs_floating.canonical_string
inspect(show(@dzmingli_vs_floating.add(a, b)), content="1.245")
inspect(show(@dzmingli_vs_floating.subtract(a, b)), content="1.255")
inspect(show(@dzmingli_vs_floating.multiply(a, b)), content="-0.00625")
inspect(@dzmingli_vs_floating.compare(a, b), content="1")
}
参考预言机
预言机只用 BigInt 算术精确计算每个被测运算的期望结果。其语义等同于精度足够且向零舍入的 GDA 上下文;设计页逐条给出规则。
oracle_multiply
oracle_multiply 返回精确积;它就是以预言机名义出现的 multiply。
pub fn oracle_multiply(DecimalValue, DecimalValue) -> DecimalValue
oracle_divide
oracle_divide 在商具有有限十进制展开时返回精确商。
pub fn oracle_divide(DecimalValue, DecimalValue) -> DecimalValue
除数为零时,或约分后分母含有 和 以外的素因子时(“repeating decimal in exact decimal oracle”)中止。代价:一次 BigInt 最大公约数,加上结果每个小数位一次乘以 。
oracle_operation
oracle_operation 计算一个二元或一元 Operation,返回规范化结果。
pub fn oracle_operation(Operation, DecimalValue, DecimalValue) -> OracleResult
它等于第三操作数为零的 oracle_operation3,因此除非加数为零,否则它对 Fma 给出错误答案。
oracle_operation3
oracle_operation3 计算任意 Operation;第三操作数是 Fma 的加数,对其他运算被忽略。
pub fn oracle_operation3(Operation, DecimalValue, DecimalValue, DecimalValue) -> OracleResult
| 运算 | 期望值 |
|---|---|
Add, Subtract, Multiply | 精确结果 |
Divide | 精确的有限商;否则中止 |
DivideInteger | 向零截断的商 |
Remainder | ,符号与 相同 |
Power | 通过重复乘法计算 ; 必须是非负整数 |
Fma | 精确的 |
SquareRoot | 标度为偶数的完全平方数的精确平方根;否则中止 |
Plus, Minus, Abs | , , |
Quantize | 向零截断到 的标度 |
Rescale | 向零截断到标度 ; 必须是整数 |
ScaleB | ; 必须是整数 |
Reduce | 规范化的 |
ToIntegralExact, ToIntegralValue | 向零截断为整数 |
Compare | 以十进制数表示的 -1、0 或 1 |
Parse, Format | 不变 |
test "reference oracle" {
let a = @dzmingli_vs_floating.parse_decimal_value("1.25")
let b = @dzmingli_vs_floating.parse_decimal_value("8")
let show = @dzmingli_vs_floating.canonical_string
inspect(show(@dzmingli_vs_floating.oracle_divide(a, b)), content="0.15625")
let minus_seven_and_a_half = @dzmingli_vs_floating.parse_decimal_value("-7.5")
let two = @dzmingli_vs_floating.parse_decimal_value("2")
let r = @dzmingli_vs_floating.oracle_operation(Remainder, minus_seven_and_a_half, two)
inspect(r.canonical, content="-1.5")
let fma = @dzmingli_vs_floating.oracle_operation3(Fma, a, b, two)
inspect(fma.canonical, content="12")
}
运算与套件
Operation
Operation 列出基准中的运算族。
pub(all) enum Operation {
Add
Subtract
Multiply
Divide
DivideInteger
Remainder
Power
Fma
SquareRoot
Plus
Minus
Abs
Quantize
Rescale
ScaleB
Reduce
ToIntegralExact
ToIntegralValue
Compare
Parse
Format
}
Parse 与 Format 是恒等路径:两个适配器都返回预先准备好的左操作数,因此它们什么也不测量,可执行程序也不运行它们。
operation_name
operation_name 返回 JSONL 记录和指纹中使用的稳定蛇形命名,例如 "divide_integer" 或 "sqrt"。
pub fn operation_name(Operation) -> String
OperandShape
OperandShape 为生成的用例打标签。
pub(all) enum OperandShape {
Small
Large
Boundary
Cancellation
Repeating
}
该标签会被序列化进用例及其指纹;generate_cases 按索引循环分配它,它不影响操作数的生成方式。
TimingScope
TimingScope 选择一次计时调用包含哪些内容。
pub(all) enum TimingScope {
OperationOnly
FullPath
}
OperationOnly 对计时前已准备好的操作数计时公开运算。FullPath 还会在准备好的上下文中解析两个规范操作数字符串(对 Fma 还有加数)。
timing_scope_name
timing_scope_name 返回 "arithmetic_only" 或 "full_path"。
pub fn timing_scope_name(TimingScope) -> String
Suite
Suite 是基准套件的描述性元数据。
pub struct Suite {
name : String
operations : Array[Operation]
scales : Array[Int]
timing_scope : TimingScope
warmup : Int
samples : Int
}
default_suite
default_suite 返回名为 "dzmingli-vs-floating-gda" 的套件:全部 21 种运算、标度 [0, 2, 6, 18, 28]、OperationOnly、5 次预热和 20 个样本。Mare Mark 运行器不读取它;它们的协议来自 default_benchmark_protocol。
pub fn default_suite() -> Suite
suite_case_count
suite_case_count 返回 运算数 × 标度数 × 每种运算的用例数。
pub fn suite_case_count(Suite, Int) -> Int
test "operations and suites" {
inspect(@dzmingli_vs_floating.operation_name(SquareRoot), content="sqrt")
inspect(@dzmingli_vs_floating.timing_scope_name(FullPath), content="full_path")
let suite = @dzmingli_vs_floating.default_suite()
inspect(@dzmingli_vs_floating.suite_case_count(suite, 4), content="420")
}
确定性用例
generate_decimal
generate_decimal(seed, digits, scale, negative) 构造一个恰有 digits 位系数数字的十进制字符串,其中 scale 位在小数点之后。
pub fn generate_decimal(Int, Int, Int, Bool) -> String
第 位数字为 ,因此每位都在 中:系数恰有 digits 位且没有末尾零。当 scale >= digits 时字符串以 0. 和前导零开头。若 digits <= 0 或 scale < 0 则中止。
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]
用例 的两个操作数都有 位数字,标度为 。除数没有经过筛选,因此 Divide 用例可能不能整除为有限小数;请把它们交给适配器,而不是 oracle_divide。
serialize_case
serialize_case 用换行连接版本标签 decimal-neutral-v1、id、运算名、形状名、两个操作数和标度。
pub fn serialize_case(BenchmarkCase) -> String
fingerprint_case
fingerprint_case 用 Mare Mark 的 stable_fingerprint 对 serialize_case 求哈希,返回 sha256: 字符串。
pub fn fingerprint_case(BenchmarkCase) -> String
expand_digit_scales
expand_digit_scales(sizes, n) 把每个系数规模重复 n 次,使 Mare Mark 为每个规模创建 n 个独立数据集。
pub fn expand_digit_scales(Array[Int], Int) -> Array[Int]
test "deterministic cases" {
inspect(@dzmingli_vs_floating.generate_decimal(3, 6, 2, true), content="-4297.53")
let cases = @dzmingli_vs_floating.generate_cases(7, 3, Add)
inspect(cases[1].left, content="-9753186429753186.429")
inspect(
@dzmingli_vs_floating.fingerprint_case(cases[0]) ==
@dzmingli_vs_floating.fingerprint_case(@dzmingli_vs_floating.generate_cases(7, 3, Add)[0]),
content="true",
)
assert_eq(@dzmingli_vs_floating.expand_digit_scales([4, 16], 2), [4, 4, 16, 16])
}
夹具与适配器
working_precision
working_precision 返回一个有效数字位数,它足以容纳给定操作数上某运算的精确结果。
pub fn working_precision(Operation, DecimalValue, DecimalValue) -> Int
记 为两个系数的位数, 为标度:
| 运算 | 结果 |
|---|---|
Add, Subtract | |
Multiply, Fma, Power, Divide, DivideInteger, Remainder | |
SquareRoot 与一元运算 | |
Compare, Parse, Format |
prepare_fixture3 再加两位保护位,并对 Power 和 Fma 另作处理;设计页证明了每个界覆盖哪些操作数类别。
DecimalFixture
DecimalFixture 保存两个实现在计时前构造好的操作数与上下文。
pub struct DecimalFixture {
left_text : String
right_text : String
third_text : String
operation : Operation
dz_left : @decimal.Decimal
dz_right : @decimal.Decimal
dz_third : @decimal.Decimal
dz_context : @decimal.Context
gda_left : @decimal_gda.Decimal
gda_right : @decimal_gda.Decimal
gda_third : @decimal_gda.Decimal
gda_context : @decimal_gda.GdaContext
}
@decimal 指 DzmingLi/decimal,@decimal_gda 指 Luna-Flow/floating/decimal_gda。*_text 字段是 FullPath 会重新解析的规范字符串。
prepare_fixture3
prepare_fixture3(operation, left, right, third) 选定精度 ,以精度 和向零舍入构造两个上下文,并把三个规范字符串分别解析进两个库。
pub fn prepare_fixture3(Operation, DecimalValue, DecimalValue, DecimalValue) -> DecimalFixture
对 Power 为 ,对 Fma 为 ;其余为 working_precision(...) + 2。若 DzmingLi 报告转换语法错误则中止。
prepare_fixture
prepare_fixture(operation, left, right) 是第三操作数为零的 prepare_fixture3。
pub fn prepare_fixture(Operation, DecimalValue, DecimalValue) -> DecimalFixture
dz_from_neutral
dz_from_neutral 在 Context::exact() 下把值转换为 DzmingLi 十进制数。
pub fn dz_from_neutral(DecimalValue) -> @decimal.Decimal
gda_from_neutral
gda_from_neutral(value, precision) 在给定精度、向零舍入的上下文中把规范字符串解析为 GDA 十进制数。
pub fn gda_from_neutral(DecimalValue, Int) -> @decimal_gda.Decimal
DecimalObservation
DecimalObservation 是一次适配器调用的原始结果。
pub(all) enum DecimalObservation {
Dz(@decimal.Decimal)
Gda(@decimal_gda.GdaOutcome[@decimal_gda.Decimal])
}
GDA 变体保留完整的结果,包括标志和下一个上下文;校验只读取其中的值。
run_dz
run_dz 用 DzmingLi 在预先准备的操作数上执行夹具的运算。这就是 OperationOnly 的计时主体。
pub fn run_dz(DecimalFixture) -> DecimalObservation
run_dz_full
run_dz_full 先在 dz_context 下解析夹具的三个字符串,再执行运算。这就是 FullPath 的计时主体。
pub fn run_dz_full(DecimalFixture) -> DecimalObservation
run_gda
run_gda 用 floating GDA 在预先准备的操作数上执行夹具的运算。
pub fn run_gda(DecimalFixture) -> DecimalObservation
run_gda_full
run_gda_full 先在 gda_context 下解析夹具的三个字符串,再执行运算。
pub fn run_gda_full(DecimalFixture) -> DecimalObservation
canonical_observation
canonical_observation 把观测值转换为规范化的 DecimalValue。
pub fn canonical_observation(DecimalObservation) -> DecimalValue
DzmingLi 的结果经由 to_sci_string,GDA 的结果经由 to_string,二者再经由 parse_decimal_value。指数、末尾零和状态标志都被丢弃。结果为 NaN 或无穷时中止。
test "fixtures and adapters" {
let a = @dzmingli_vs_floating.parse_decimal_value("1.25")
let b = @dzmingli_vs_floating.parse_decimal_value("8")
inspect(@dzmingli_vs_floating.working_precision(Divide, a, b), content="6")
let fixture = @dzmingli_vs_floating.prepare_fixture(Divide, a, b)
inspect(fixture.gda_context.precision(), content="8")
let show = (o : @dzmingli_vs_floating.DecimalObservation) => {
@dzmingli_vs_floating.canonical_string(
@dzmingli_vs_floating.canonical_observation(o),
)
}
inspect(show(@dzmingli_vs_floating.run_dz(fixture)), content="0.15625")
inspect(show(@dzmingli_vs_floating.run_gda(fixture)), content="0.15625")
inspect(show(@dzmingli_vs_floating.run_gda_full(fixture)), content="0.15625")
}
Mare Mark 集成
MareDecimalInput
MareDecimalInput 是一个 Mare Mark 数据集实例化后的输入。
pub(all) struct MareDecimalInput {
left : DecimalValue
right : DecimalValue
third : DecimalValue
operation : Operation
timing_scope : TimingScope
digits : Int
left_scale : Int
right_scale : Int
}
PerformanceResult
PerformanceResult 汇总一种运算、一个计时范围和一个系数规模的结果。
pub(all) struct PerformanceResult {
operation : Operation
timing_scope : TimingScope
digits : Int
correctness_valid : Bool
dz_median_us : Double
gda_median_us : Double
gda_relative_delta_pct : Double
dz_speedup_vs_gda : Double
decision : String
samples : Int
}
当该规模下两个实现的每次校验都通过时,correctness_valid 为真。此时中位数来自配对的确认性样本,dz_speedup_vs_gda 是 GDA 中位数除以 DzmingLi 中位数(大于 表示 DzmingLi 更快),gda_relative_delta_pct 是配对差的中位数相对于 DzmingLi 中位数的比例,decision 为 "gda_faster"、"dzmingli_faster"、"equivalent"、"invalid" 或 "unknown"。否则中位数取各实现自己的有效样本,加速比与差值为 0.0,decision 为 "invalid_correctness"。
PerformanceResult::to_json
PerformanceResult::to_json 输出一条 "comparison" JSONL 记录,标注 Mare Mark 制品版本 mmka_1。
pub fn PerformanceResult::to_json(Self) -> String
MareBenchmarkReport
MareBenchmarkReport 汇集一次运行的结果、原始 JSONL 和校验计数。
pub(all) struct MareBenchmarkReport {
results : Array[PerformanceResult]
jsonl : String
validation_count : Int
failed_count : Int
}
validation_count 统计每一次预言机校验,无论通过与否。
smoke_protocol
smoke_protocol 返回用于测试的简短 Mare Mark 协议: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_* 环境变量构造记录在 JSONL 中的环境快照。
pub fn benchmark_environment() -> @model.EnvironmentSnapshot
它读取 MARE_TARGET、MARE_COMPILER、MARE_BUILD_MODE、MARE_CPU、MARE_DEVICE、MARE_FREQUENCY_POLICY、MARE_OS、MARE_EXECUTION_CONTEXT 和 MARE_REPOSITORY_STATE。缺失或为空的变量会变成 unknown、unspecified 或其他固定默认值;不会从主机探测任何信息。
run_mare_benchmark
run_mare_benchmark(operations, scope, digit_scales, protocol, seed) 为每种运算运行一次 Mare Mark 实验,并返回合并后的报告。
pub async fn run_mare_benchmark(Array[Operation], TimingScope, Array[Int], @model.RunProtocol, UInt64) -> MareBenchmarkReport
digit_scales 的每一项是一个数据集;用 expand_digit_scales 重复规模。每个数据集在计时前都先与预言机核对。该函数不会因校验失败而中止;调用者应检查 failed_count。它需要异步运行时,因此在 native 和 js 目标上运行。
async test "mare mark smoke run" {
let report = @dzmingli_vs_floating.run_mare_benchmark(
[Add],
OperationOnly,
[4],
@dzmingli_vs_floating.smoke_protocol(),
42UL,
)
inspect(report.failed_count, content="0")
inspect(report.validation_count, content="2")
inspect(report.results[0].correctness_valid, content="true")
inspect(report.results[0].to_json().contains("\"type\":\"comparison\""), content="true")
}
报告
mare_performance_report_document
mare_performance_report_document(results, target, run_id) 构建 Mare Mark Plot IR 文档:每种运算与计时范围一张延迟图,每个计时范围一张 DzmingLi 对 GDA 的加速比图。
pub fn mare_performance_report_document(Array[PerformanceResult], String, String, validation_count? : Int, failed_count? : Int) -> @ir_model.PlotDocument
correctness_valid 为假的规模保留 GDA 延迟点,但不提供 DzmingLi 点和加速比点。语料汇总为:总数 validation_count、通过 validation_count - failed_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 : @dzmingli_vs_floating.PerformanceResult = {
operation: Add,
timing_scope: OperationOnly,
digits: 16,
correctness_valid: true,
dz_median_us: 1.0,
gda_median_us: 2.0,
gda_relative_delta_pct: 100.0,
dz_speedup_vs_gda: 2.0,
decision: "dzmingli_faster",
samples: 60,
}
let html = @dzmingli_vs_floating.mare_performance_report_html(
[result],
"native",
"example",
validation_count=2,
)
inspect(html.contains("Total 2 · passed 2 · failed 0"), content="true")
}