model のチュートリアル

このチュートリアルでは、共通の型を使ってベンチマークの実行を記述する方法を示します。実行間で比較できる環境スナップショット、記録できるプロトコル、操作が値を持たない理由を示す結果、そして結果から導かれるデプロイポリシーです。どの例も完全なテストです。

クイックスタート

moon add Luna-Flow/mare_mark@0.3.0
import {
  "Luna-Flow/mare_mark/model",
}

マシンを記述し、2 つの実行を比較してよいかどうかを検査します:

test "describe two runs" {
  let semantic = @model.SemanticEnvironment::new(@model.ExecutionTarget::Native, "moonc 0.10.14", "release", "f64")
  let performance = @model.PerformanceEnvironment::new(
    "native", "AMD EPYC 7763", "default", 1, "monotonic", frequency_policy="performance",
  )
  let first = @model.EnvironmentSnapshot::new(
    semantic, performance,
    @model.ProvenanceEnvironment::new("linux", "ci-7", "2026-10-08T08:00:00Z", "a1b2c3", "nightly-101"),
  )
  let second = @model.EnvironmentSnapshot::new(
    semantic, performance,
    @model.ProvenanceEnvironment::new("linux", "ci-3", "2026-10-09T08:00:00Z", "d4e5f6", "nightly-102"),
  )
  inspect(@model.environment_compatible(first, second), content="true")
}

ホスト、日付、リビジョンの違いは問題になりません。問題になるのは宣言されたハードウェア、ツールチェーン、フラグです。

日常的な作業

結果とともにプロトコルを記録する

test "a protocol and its identity" {
  let protocol = @model.RunProtocol::new(
    @model.ExperimentDesign::FixedDatasetRepeatedMeasurements,
    3,
    Some(5000.0),
    @model.CalibrationProtocol::new(5000.0, 1, 10000, 250000.0, @model.BatchPolicy::PerImplementation),
    1.0,
    @model.OrderPolicy::BalancedBlocks(1UL),
    @model.OutlierPolicy::ReportOnly,
    @model.ValidationCoverage::EveryDataset,
    3,
    10,
  )
  inspect(@model.protocol_identity(protocol), content="mmkp_1:3:10:1")
}

識別子は短いキャッシュキーです。プロトコル全体も保存してください。キーが含むのはウォームアップ回数、確認サンプル数、閾値だけです。

正確な結果を返す

入力を扱えない実装は、偽の値を返す代わりにその旨を伝えます:

fn checked_sqrt(x : Double) -> @model.OperationResult[Double, Unit] {
  if x < 0.0 {
    @model.OperationResult::new(Unsupported("negative input"), Some(()))
  } else {
    @model.OperationResult::completed(x.sqrt(), ())
  }
}

test "unsupported is not wrong" {
  let result = checked_sqrt(-4.0)
  inspect(result.outcome.kind(), content="unsupported")
  inspect(result.outcome.value_option() is None, content="true")
  inspect(checked_sqrt(9.0).outcome.value_option() == Some(3.0), content="true")
}

ランナーは Unsupported を失敗とは別に数え、レポートはそれをケイパビリティ行列に記載します。

クロスオーバーをデプロイポリシーに変える

test "piecewise deployment" {
  let crossover : @model.CrossoverResult[Int] = @model.CrossoverResult::found(
    @model.ScaleBoundary::new(64, 128), "piecewise-confirmed", ["A", "A", "B"],
  )
  guard crossover is Found(boundary, _, evidence) else { fail("no crossover") }
  let policy : @model.DeploymentPolicy[Int, String] = Piecewise([
    @model.Region::new(None, Some(boundary.below), "scalar", evidence),
    @model.Region::new(Some(boundary.at_or_above), None, "blocked", evidence),
  ])
  guard policy is Piecewise(regions) else { fail("not piecewise") }
  inspect(regions.length(), content="2")
}

さらに先へ

  • 独自のシンクをテストするには、Observation、Validation、RunSummary の値を手で構築します。event のチュートリアルでそうしています。
  • 文書化された差異(例えば設計上異なる丸めを行うライブラリ)には ExpectedDifference を使ってください。そうすれば隠されるのではなく数えられます。
  • 外部の JSONL を読む前に ArtifactVersion::V1.identifier() を確認してください。

よくある落とし穴

  • 環境を曖昧に記述する。 "cpu" は他のどの "cpu" とも一致してしまいます。モデルと周波数ポリシーを書いてください。
  • 実行 ID を RunSummary にだけ入れる。 ランナーの run_id は一意ではありません。ProvenanceEnvironment.run_id を使ってください。
  • ランナーがすべてのプロトコルのフィールドに基づいて動作すると期待する。 experiment_design、outlier_policy、validation_coverage には基づいて動作しません。

次のステップ