generator API

Luna-Flow/mare_mark/generator はベンチマーク入力を再現可能にします。1 つの実行シードからデータセット、反復、ブロックごとに独立したシードを導出し、生成コンテキストを構築し、シリアライズされた入力のフィンガープリントを計算します。混合関数の導出は generator の設計にあります。

ソース: src/generator/generator.mbt。

import {
  "Luna-Flow/mare_mark/model",
  "Luna-Flow/mare_mark/generator",
}

シード

derive_seed

derive_seed は親シード、ドメイン名、インデックスから子シードを導出します。

pub fn derive_seed(UInt64, String, Int) -> UInt64

結果は 3 つの引数すべてに依存し、それ以外の何にも依存せず、ラップアラウンドする 64 ビット算術のみを使うため、どのターゲットでも同一になります。目的ごとに異なるドメイン(“dataset”、“noise”、ケース ID)を使い、異なる目的のストリームが無関係になるようにしてください。

test "derived seeds" {
  let run_seed = 42UL
  let first = @generator.derive_seed(run_seed, "dataset", 0)
  let second = @generator.derive_seed(run_seed, "dataset", 1)
  let other = @generator.derive_seed(run_seed, "noise", 0)
  inspect(first == @generator.derive_seed(42UL, "dataset", 0), content="true")
  inspect(first != second && first != other, content="true")
}

measurement_seed

measurement_seed は実行シードと、データセット、反復、ブロックの ID から 1 回の計測のシードを導出します。

pub fn measurement_seed(UInt64, Int, Int, Int) -> UInt64

これは derive_seed(derive_seed(derive_seed(seed, "dataset", d), "repetition", r), "block", b) です。

test "measurement seeds" {
  let a = @generator.measurement_seed(7UL, 0, 1, 2)
  let b = @generator.measurement_seed(7UL, 0, 2, 1)
  inspect(a != b, content="true")
  inspect(
    a == @generator.derive_seed(
      @generator.derive_seed(@generator.derive_seed(7UL, "dataset", 0), "repetition", 1),
      "block",
      2,
    ),
    content="true",
  )
}

コンテキストとジェネレータ

context

context は @model.GenerationContext を構築します。

pub fn[Scale] context(UInt64, String, String, @model.DatasetKey[Scale], String, String) -> @model.GenerationContext[Scale]

引数: シード、スイート ID、ケース ID、データセットキー、ジェネレータ ID、ジェネレータのバージョン。@model.GenerationContext::new と同じです。

Generator

Generator は生成関数を、その識別情報とフィンガープリントとともにまとめます。

pub struct Generator[Scale, Input] {
  id : String
  version : String
  generate : (@model.GenerationContext[Scale]) -> Input
  fingerprint : (Input) -> String
}
pub fn[Scale, Input] Generator::new(String, String, (@model.GenerationContext[Scale]) -> Input, (Input) -> String) -> Self[Scale, Input]

generate が生成する値を変えたときは必ず version を上げてください。バージョンはコンテキストの一部であり、すべての入力の来歴の一部でもあります。

test "a generator" {
  let ramp : @generator.Generator[Int, Array[Int]] = @generator.Generator::new(
    "ramp",
    "1",
    context => Array::makei(context.dataset_key.scale, i => i),
    xs => @generator.stable_fingerprint(xs.map(x => x.to_string()).join(",")),
  )
  let context = @generator.context(1UL, "suite", "sum", @model.DatasetKey::new(4, 0), ramp.id, ramp.version)
  let input = (ramp.generate)(context)
  debug_inspect(input, content="[0, 1, 2, 3]")
  inspect((ramp.fingerprint)(input).has_prefix("sha256:"), content="true")
}

フィンガープリント

stable_fingerprint

stable_fingerprint は、文字列の UTF-8 エンコーディングの SHA-256 ダイジェストを小文字の 16 進で表したものに "sha256:" を前置して返します。

pub fn stable_fingerprint(String) -> String

まず入力を正規形でシリアライズしてください。フィンガープリントが識別するのはシリアライズ結果であり、メモリ上の値ではありません。これは鍵なしハッシュです。偶発的な変更を検出しますが、データを認証するものではありません。

test "fingerprint" {
  inspect(
    @generator.stable_fingerprint("abc"),
    content="sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
  )
}