report API

Luna-Flow/mare_mark/report は JSONL イベントストリームを Plot IR ドキュメントに変換し、そのドキュメントを mmks_1 JSON、単独の SVG、または自己完結型の HTML ページとして描画します。4 つの関数はすべて純粋で、文字列と値を受け取り文字列を返します。ファイルの読み書きは cli の役割です。射影の規則は report の設計で説明しています。

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

import {
  "Luna-Flow/mare_mark/ir_model",
  "Luna-Flow/mare_mark/report",
}

イベントの解析

document_from_jsonl

document_from_jsonl は JSONL イベントストリームを解析して PlotDocument にします。

pub fn document_from_jsonl(String, target? : String) -> Result[@ir_model.PlotDocument, String]

target(既定値 "unknown")はドキュメントにコピーされます。イベント自体はこれを持ちません。空行は読み飛ばされます。それ以外の各行は JSON オブジェクトでなければならず、その artifact_version は、存在する場合は文字列 "mmka_1" でなければなりません。各行はその "type" によって解釈されます:

type必須フィールド動作
observationimplementation(文字列)、dataset_id、elapsed_us(数値)プロット点(x = dataset_id、y = elapsed_us、series = implementation)。ただし valid が false の場合や batch_sink が "kept" でない場合を除きます
validationなしケイパビリティ行列で implementation と actual_kind(存在しない場合は "unclassified")ごとに数えます。ステータスが invalid または infrastructure_failure の場合は不一致の行を追加し、case と dataset_id が存在すれば、そのケースとデータセットにおけるその実装の点を取り除きます
validation_failureなし反例の行(最小入力、最小フィンガープリント、縮小パス、リプレイコマンド)
summaryなしrun_id とコーパス件数を設定します。最後の要約が優先されます
それ以外calibration を含め、無視されます

ドキュメントは、タイトル "Benchmark observations"、単位 "µs/op"、区間の種類 "raw" の Scaling プロットを 1 つ持ちます。残る点が 1 つもない場合はプロットを持ちません。探索的な観測と確認的な観測は一緒にプロットされます。コーパスの合計は要約の 4 つの件数の和です。

エラーは 1 始まりの行番号を伴うメッセージです。不正な JSON、オブジェクトでない行、サポートされないまたは文字列でないアーティファクトバージョン、implementation、dataset_id、elapsed_us のいずれかを欠く観測、そして点も不一致も反例も含まないストリームがエラーになります。

test "parse a small event stream" {
  let source =
    #|{"type":"observation","case":"add","implementation":"a","dataset_id":0,"elapsed_us":12.5}
    #|{"type":"observation","case":"add","implementation":"b","dataset_id":0,"elapsed_us":10.0}
    #|{"type":"observation","case":"add","implementation":"b","dataset_id":1,"elapsed_us":30.0,"valid":false}
    #|{"type":"summary","run_id":"demo","passed_count":2}
  let document = @report.document_from_jsonl(source, target="native").unwrap()
  inspect(document.run_id, content="demo")
  inspect(document.plots[0].points.length(), content="2")
  inspect(document.differential.corpus.total, content="2")
  let bad = @report.document_from_jsonl("{\"artifact_version\":\"mmka_9\"}")
  inspect(bad is Err("unsupported artifact version 'mmka_9' at line 1"), content="true")
}

描画

plot_json

plot_json はドキュメントを mmks_1 JSON としてシリアライズします。

pub fn plot_json(@ir_model.PlotDocument) -> String

オブジェクトはキー schema_version、run_id、target、plots(それぞれ kind、title、unit、interval_kind と、x、y、series からなる points を持つ)、および differential(mismatches、capabilities、counterexamples、corpus)を持ちます。プロットの種類はスネークケース(scaling、raw_distribution、block_order、interval、outlier、change_point、heatmap、pareto)で書かれます。出力はコンパクトで、文字列は JSON エンコーダによってエスケープされます。

test "plot JSON" {
  let point = @ir_model.PlotPoint::new("0", 1.0, "a")
  let plot = @ir_model.Plot::new(@ir_model.PlotKind::Scaling, "t", "µs/op", "raw", [point])
  let document = @ir_model.PlotDocument::new("r", "native", [plot])
  let json = @report.plot_json(document)
  inspect(json.has_prefix("{\"schema_version\":\"mmks_1\",\"run_id\":\"r\""), content="true")
  inspect(json.contains("\"points\":[{\"x\":\"0\",\"y\":1,\"series\":\"a\"}]"), content="true")
}

plot_svg

plot_svg は 1 つのプロットを単独の SVG 要素として描画します。

pub fn plot_svg(@ir_model.Plot) -> String

SVG は 960 × 420 の view box、アクセシブルな <title> と <desc>、インラインスタイル、目盛り値付きの 5 本の水平グリッド線、最大でおよそ 10 個の x ラベル、および凡例を持ちます。各点はツールチップ series — x: y unit 付きの円として描かれます。Scaling、Interval、ChangePoint のプロットでは、さらに系列ごとに各 x カテゴリにおける点の平均を線で結びます。Heatmap のプロットは系列と x カテゴリごとに 1 つのセルを描き、その不透明度は平均値とともに大きくなります。空のプロットには “No observations” と表示されます。すべてのテキストは XML エスケープされます。

test "plot SVG" {
  let points = [
    @ir_model.PlotPoint::new("64", 2.0, "scalar"),
    @ir_model.PlotPoint::new("128", 4.5, "scalar"),
  ]
  let plot = @ir_model.Plot::new(@ir_model.PlotKind::Scaling, "a < b", "µs/op", "raw", points)
  let svg = @report.plot_svg(plot)
  inspect(svg.has_prefix("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 960 420\""), content="true")
  inspect(svg.contains("<title id=\"plot-title\">a &lt; b</title>"), content="true")
  inspect(svg.contains("<polyline"), content="true")
}

html

html はドキュメント全体を 1 つの自己完結型 HTML ページとして描画します。

pub fn html(@ir_model.PlotDocument) -> String

ページには、実行 ID、ターゲット、スキーマバージョンを含むヘッダー、続いて差分レポート(コーパスの結果、不一致、ケイパビリティのカバレッジ行列、最小反例)、さらにプロットごとに 1 つの図が含まれます。CSS はインライン、プロットはインライン SVG で、スクリプトも外部リソースもないため、ファイルをそのままアーカイブしたり添付したりできます。出力はドキュメントだけに依存します。

test "self-contained HTML" {
  let source =
    #|{"type":"observation","case":"add","implementation":"a","dataset_id":0,"elapsed_us":12.5}
    #|{"type":"summary","run_id":"demo"}
  let page = @report.html(@report.document_from_jsonl(source).unwrap())
  inspect(page.has_prefix("<!doctype html>"), content="true")
  inspect(page.contains("<title>mare_mark · demo</title>"), content="true")
  inspect(page.contains("<script"), content="false")
}