快速入门
本指南在一个测试中带你从一个空包走到经过验证的基准测试、统计决策和 HTML 报告。随后它会在仓库附带的测试数据上展示命令行工具。
1. 添加模块
你需要 moonc 0.10 或更高版本的 MoonBit。
moon add Luna-Flow/mare_mark@0.3.0
在存放基准测试的包的 moon.pkg 中:
import {
"Luna-Flow/mare_mark/model",
"Luna-Flow/mare_mark/event",
"Luna-Flow/mare_mark/runner",
"Luna-Flow/mare_mark/stats",
"Luna-Flow/mare_mark/report",
"moonbitlang/async",
}
2. 基准测试、决策、报告
下面的测试比较两种计算平方和 的方法:循环和闭式公式 。
fn squares_loop(n : Int) -> Int64 {
let mut total = 0L
for i in 0..<n {
total += i.to_int64() * i.to_int64()
}
total
}
fn squares_formula(n : Int) -> Int64 {
let m = n.to_int64()
(m - 1L) * m * (2L * m - 1L) / 6L
}
async test "loop versus closed form" {
// 1. Describe the case: inputs, implementations, oracle.
let looped = @runner.Implementation::stateless("loop", "1", (n : Int) => {
@model.OperationResult::completed(squares_loop(n), ())
})
let formula = @runner.Implementation::stateless("formula", "1", (n : Int) => {
@model.OperationResult::completed(squares_formula(n), ())
})
let plan = @runner.single_step("sum-of-squares", [100, 10000])
.with_immutable_input(context => context.dataset_key.scale, n => n.to_string())
.compare([looped, formula])
.against_equal(squares_loop, (expected, actual) => expected == actual)
.compile()
.unwrap()
// 2. Run it with a seed, an environment and two sinks.
let memory = @event.InMemorySink::new()
let record = @event.JsonlSink::new()
let environment = @model.EnvironmentSnapshot::new(
@model.SemanticEnvironment::new(@model.ExecutionTarget::Native, "moonc 0.10", "release", "i64"),
@model.PerformanceEnvironment::new("native", "my-cpu", "default", 1, "monotonic"),
@model.ProvenanceEnvironment::new("my-os", "my-host", "2026-10-08T12:00:00Z", "HEAD", "getting-started"),
)
let summary = @runner.run(
plan,
@runner.RunContext::new(
environment,
@event.tee(memory.as_sink(), record.as_sink()),
42UL,
@runner.ProtocolPreset::Development.validated(),
),
)
inspect(summary.passed_count, content="4")
inspect(summary.failed_count, content="0")
// 3. Decide on the larger dataset with paired confirmatory blocks.
let baseline = memory.observations
.filter(o => o.dataset_id == 1 && o.implementation_id == "loop" && o.phase is Confirmatory)
.map(o => o.raw_elapsed_us)
let candidate = memory.observations
.filter(o => o.dataset_id == 1 && o.implementation_id == "formula" && o.phase is Confirmatory)
.map(o => o.raw_elapsed_us)
let comparison = @stats.compare_paired_with_bootstrap(
"loop", "formula", baseline, candidate, 5.0, @model.confirmatory_interval(), 42UL, 2000, 95.0,
).unwrap()
inspect(comparison.valid_samples, content="10")
// 4. Render the record.
let document = @report.document_from_jsonl(record.to_jsonl(), target="native").unwrap()
inspect(@report.html(document).has_prefix("<!doctype html>"), content="true")
}
发生了什么:
against_equal把循环作为参考判定器附加上去。在任何计时之前,两个实现都在两个规模上经过了验证(四次验证通过)。Development对每个实现进行预热,为每个实现校准批次大小,然后在每个规模上运行 3 个探索性区组和 10 个验证性区组,并轮换两个实现的顺序。- 验证性区组按位置配对(循环的第 个区组与公式的第 个区组)。
comparison.decision和comparison.speedup取决于你的机器;预期公式为Faster。 - JSONL 记录(
record.to_jsonl())保存了每个事件;请把它保存在 HTML 旁边。
用 moon test --target native 运行它。该测试也可以在 js 和 wasm 上运行。
3. 使用命令行
在仓库的检出目录中:
moon run src/cli --target native -- report testdata/report/sample.jsonl report.html
moon run src/cli --target native -- report - - < testdata/report/sample.jsonl > report.html
moon run src/cli --target native -- replay testdata/replay/sample.jsonl --dry-run
report 写出一个自包含的 HTML 文件。replay --dry-run 打印验证失败中记录的命令;把 --dry-run 换成 --yes 即可执行它。参见 cli 教程。
常见的入门错误
- 在实现函数内部做准备工作(分配、解析、复制),使其被计时。请把它放到夹具中。
- 比较来自不同区组、阶段、数据集或目标的数组。
- 把
Unsupported、超时或验证失败当作数字。 - 未先用
--dry-run阅读就执行重放产物。 - 把
summary.run_id当作唯一 id 复用;它指明的是协议和用例。