event のチュートリアル

このチュートリアルでは、ベンチマークのイベントがどこへ行くかを示します。分析用のメモリ、記録用の JSONL、その両方、または独自のシンクです。どの例も完全なテストです。

クイックスタート

moon add Luna-Flow/mare_mark@0.3.0
import {
  "Luna-Flow/mare_mark/model",
  "Luna-Flow/mare_mark/event",
}
fn sample_observation(implementation : String, block : Int, elapsed : Double) -> @model.Observation {
  @model.Observation::new(
    "sum", implementation, "1", 0, block, block, Confirmatory, elapsed, 100, Kept, ExcludedFromMeasurement, true,
  )
}

test "keep events in memory" {
  let memory = @event.InMemorySink::new()
  let sink = memory.as_sink()
  (sink.emit_observation)(sample_observation("loop", 0, 3.5))
  (sink.emit_observation)(sample_observation("formula", 0, 0.2))
  inspect(memory.observations.length(), content="2")
  inspect(memory.observations[1].raw_elapsed_us, content="0.2")
}

実際の使用では memory.as_sink() を @runner.RunContext::new に渡し、ランナーがコールバックを呼び出します。

日常的な作業

監査記録を書き出す

test "JSONL record" {
  let jsonl = @event.JsonlSink::new()
  let sink = jsonl.as_sink()
  (sink.emit_observation)(sample_observation("loop", 0, 3.5))
  let location = (sink.finish)(@model.RunSummary::new("sum-run", 1, 0, 0, true, None))
  inspect(location, content="jsonl://memory/sum-run")
  let lines = jsonl.to_jsonl().split("\n").to_array()
  inspect(lines.length(), content="2")
  inspect(lines[0].contains("\"elapsed_us\":3.5"), content="true")
}

jsonl.to_jsonl() をレポートの隣のファイルに書き出してください。レポートはいつでもそこから再生成できます。

実行しながらストリーミングする

長時間の実行では、各行ができた時点ですぐに書き出します:

test "streaming" {
  let file : Array[String] = []
  let sink = @event.streaming_jsonl(line => file.push(line + "\n"), "events.jsonl")
  (sink.emit_observation)(sample_observation("loop", 0, 3.5))
  (sink.emit_observation)(sample_observation("loop", 1, 3.6))
  inspect(file.length(), content="2")
  inspect(file[1].has_suffix("\n"), content="true")
}

file.push を実際のファイルへの追記に置き換えてください。

両方を保持する

test "tee" {
  let memory = @event.InMemorySink::new()
  let jsonl = @event.JsonlSink::new()
  let sink = @event.tee(memory.as_sink(), jsonl.as_sink())
  (sink.emit_observation)(sample_observation("loop", 0, 3.5))
  inspect(memory.observations.length(), content="1")
  inspect(jsonl.lines.length(), content="1")
  inspect((sink.finish)(@model.RunSummary::new("r", 1, 0, 0, true, None)), content="jsonl://memory/r")
}

独自のシンクを書く

ライブダッシュボードのために、実装ごとの最小値だけを逐次保持するシンク:

test "a custom sink" {
  let best : Map[String, Double] = Map([])
  let sink = @event.ObservationSink::new(
    observation => {
      if observation.valid {
        let current = best.get(observation.implementation_id).unwrap_or(observation.raw_elapsed_us)
        best[observation.implementation_id] = current.min(observation.raw_elapsed_us)
      }
    },
    _ => (),
    _ => (),
    summary => "dashboard://" + summary.run_id,
  )
  (sink.emit_observation)(sample_observation("loop", 0, 3.5))
  (sink.emit_observation)(sample_observation("loop", 1, 3.1))
  inspect(best.get("loop").unwrap(), content="3.1")
}

さらに先へ

  • @ir_sink は同じコンストラクタをより短い名前(in_memory、jsonl、jsonl_stream、tee)で提供します。ir_sink APIを参照してください。
  • 記録の形式は event API に一覧があります。

よくある落とし穴

  • 失敗コールバックを忘れる。 ObservationSink::new は、emit_failure= を渡さない限り検証の失敗を無視します。
  • InMemorySink が要約を保持すると期待する。 run が返す値を使ってください。
  • seed を数値として解析する。 これは 10 進文字列です。

次のステップ