bench API
bench は floating の共通ベンチマークツールキットで、Maremark フレームワーク(Luna-Flow/mare_mark)の上に構築されています。不変のベンチマーク仕様を構築し、測定環境を記述し、検証済みのプロトコルのもとで仕様を実行し、記録された観測値を集約します。集約には、ブートストラップ信頼区間付きのペア比較、回帰判定、データセットごとの自動チューニングの決定が含まれます。コアごとのスイート bench/bin_float、bench/decimal、bench/decimal_gda、bench/ball_float がこれを利用します。スイートの実行方法はチュートリアルで示し、統計手法の導出は設計ページで行います。
moon.pkg でパッケージをインポートします(仕様と観測値を構築するには Maremark のパッケージが必要です)。
import {
"Luna-Flow/floating/bench",
"Luna-Flow/mare_mark/model",
"Luna-Flow/mare_mark/runner",
"Luna-Flow/mare_mark/event",
}
@model、@runner、@event、@stats を接頭辞とする型は Luna-Flow/mare_mark に属します。@model.Observation は計時された 1 回のバッチを記録します。その内容は、ケース、実装、データセット、反復およびブロックの id、フェーズ(探索的または確認的)、raw_elapsed_us(バッチ時間をバッチの反復回数で割った値、すなわち 1 回の呼び出しの平均時間をマイクロ秒で表したもの)、および valid フラグです。
ベンチマークの構築と実行
immutable_bench
immutable_bench(id, operation, scales, scale_text, generate, fingerprint, implementations, reference, comparator, input_text, output_text) は、入力をスケールごとに一度だけ生成し、以後変更しないベンチマークを構築します。
pub fn[Scale, Input, Expected, Output] immutable_bench(String, String, Array[Scale], (Scale) -> String, (@model.GenerationContext[Scale]) -> Input, (Input) -> String, Array[@runner.Implementation[Input, Output, Unit]], (Input) -> Expected, (Expected, Output) -> Bool, (Input) -> String, (Output) -> String) -> @runner.BenchSpec[Scale, Input, Input, Expected, Output, Unit, Output?, Output?]
| 引数 | 意味 |
|---|---|
id | ケース id。フィクスチャ(id-fixture、バージョン "1")とオラクル(id-reference)にも使われる |
operation | ケース記述子に記録される演算名 |
scales, scale_text | データセット(例えばビット数や桁数)とそのラベル。データセット は scales[k] |
generate, fingerprint | スケールに対する入力を構築する。レポートでその入力を識別する |
implementations | 同じ入力で比較される状態を持たない実装 |
reference, comparator | 独立に求めた期待値と、各出力をそれと照合する検査 |
input_text, output_text | レポートと再現実行のためのテキスト形式 |
仕様は各バッチの最後の出力をシンクとして保持し(計算が最適化で除去されないようにするため)、参照オラクルで出力を検証し、反復単位を 1 つ使い、各ケースを状態を持たない厳密なものとして記述します。
environment
environment(target, dtype_abi, run_id) は、各実行とともに記録される環境スナップショットを構築します。
pub fn environment(@model.ExecutionTarget, String, String) -> @model.EnvironmentSnapshot
ターゲット、固定のツールチェーンラベル、release プロファイル、および与えられたデータ型 ABI ラベルを記録します。呼び出し側のツールだけが知り得る性能や来歴のフィールド(CPU、周波数ポリシー、コミット)は external-metadata とされ、ソースの状態は working-tree とされます。
run
run(spec, environment, sink, seed, protocol) は仕様をコンパイルして実行し、観測値を sink へストリーム出力します。
pub async fn[Scale, Input, Prepared, Expected, Output, Context, State, SinkValue] run(@runner.BenchSpec[Scale, Input, Prepared, Expected, Output, Context, State, SinkValue], @model.EnvironmentSnapshot, @event.ObservationSink, UInt64, @runner.ValidatedProtocol) -> @model.RunSummary
seed は実装の測定順序を固定します。protocol は通常 @runner.ProtocolPreset::Development.validated() のようなプリセットです。仕様がコンパイルできない場合、この関数は異常終了します。返される要約は観測値の数とオラクル失敗の数を数えたものです。
観測値の集約
paired_hotspot
paired_hotspot(observations, case_id, dataset_id, baseline_id, candidate_id, practical_delta_pct, seed) は 1 つのデータセット上で 2 つの実装を比較します。
pub fn paired_hotspot(Array[@model.Observation], String, Int, String, String, Double, UInt64) -> Result[@stats.Comparison, @stats.BootstrapError]
当該ケースとデータセットについて各実装の有効な確認的観測値を選び、ブロック id 順に並べ、位置でペアにし、2000 回のリサンプリングで @stats.compare_paired_with_bootstrap を呼び出します。比較結果の relative_delta_pct は であり、decision は practical_delta_pct に対する Faster、Slower、Equivalent のいずれかです。渡される信頼度の引数は 0.95 ですが、Maremark はこれをパーセントとして解釈するため、報告される interval は 95 % ではなく 0.95 % のブートストラップ区間になります。区間が重要な場合は relative_delta_pct と decision、または confirmatory_regression を使ってください。エラー:2 つの実装のサンプル数が異なる場合は MismatchedPairs、サンプルがない場合は EmptySamples、および NonFiniteSample。
confirmatory_regression
confirmatory_regression(baseline, candidate, seed) は、ペアになった 2 つのサンプル配列を 95 % パーセンタイル・ブートストラップ区間で比較します。
pub fn confirmatory_regression(Array[Double], Array[Double], UInt64) -> Result[@stats.Comparison, @stats.BootstrapError]
実用的な閾値は 3 %、ブートストラップは 10 000 回のリサンプリングを使い、区間はサンプルの単位でのペア差 candidate[j] - baseline[j] の中央値に対するものです。ラベルはベースラインと現在の候補を識別します。
is_significant_regression
is_significant_regression(comparison) は、候補が実用上遅く、かつ中央値の差の区間が完全にゼロより上にあるとき真になります。
pub fn is_significant_regression(@stats.Comparison) -> Bool
すなわち、decision が Slower であり、かつ interval.low > 0 であることです。
///|
test "regression verdict" {
let baseline = [100.0, 101.0, 99.0, 100.5, 99.5, 100.0, 101.0, 99.0, 100.0, 100.5]
let slower = baseline.map(x => x * 1.1)
let comparison = @bench.confirmatory_regression(baseline, slower, 7UL).unwrap()
inspect(comparison.relative_delta_pct > 9.0, content="true")
inspect(@bench.is_significant_regression(comparison), content="true")
let same = @bench.confirmatory_regression(baseline, baseline, 7UL).unwrap()
inspect(@bench.is_significant_regression(same), content="false")
}
自動チューニング
TuneDecision
TuneDecision は 1 つのデータセットに対して選ばれた実装です。
pub struct TuneDecision {
dataset_id : Int
candidate_id : String
median_us : Double
valid_samples : Int
}
median_us は選ばれた候補の 1 回の呼び出しあたりの時間の中央値であり、valid_samples はその計算に用いた確認的観測値の数です。
tune_dataset
tune_dataset(observations, case_id, dataset_id, candidate_ids, practical_delta_pct) は 1 つのデータセットに対して最速の候補を選びます。
pub fn tune_dataset(Array[@model.Observation], String, Int, Array[String], Double) -> TuneDecision?
各候補について、当該ケースとデータセットの有効な確認的観測値を取り、有限かつ非負のサンプルの中央値で候補を評価します。そのようなサンプルを持たない候補は無効です。結果は中央値が最小の有効な候補で、同値の場合は候補 id の小さい方が選ばれます。有効な候補がない場合は None です。このスコアは @tune.select_best の第 1 基準と第 2 基準の両方に使われるため、practical_delta_pct は選択に影響しません。
///|
test "no observations, no decision" {
inspect(@bench.tune_dataset([], "mul", 0, ["kernel", "full"], 3.0) is None, content="true")
inspect(
@bench.paired_hotspot([], "mul", 0, "kernel", "full", 3.0, 1UL) is Err(_),
content="true",
)
}
公開インターフェース全体
// Generated using `moon info`, DON'T EDIT IT
package "Luna-Flow/floating/bench"
import {
"Luna-Flow/mare_mark/event",
"Luna-Flow/mare_mark/model",
"Luna-Flow/mare_mark/runner",
"Luna-Flow/mare_mark/stats",
}
// Values
pub fn confirmatory_regression(Array[Double], Array[Double], UInt64) -> Result[@stats.Comparison, @stats.BootstrapError]
pub fn environment(@model.ExecutionTarget, String, String) -> @model.EnvironmentSnapshot
pub fn[Scale, Input, Expected, Output] immutable_bench(String, String, Array[Scale], (Scale) -> String, (@model.GenerationContext[Scale]) -> Input, (Input) -> String, Array[@runner.Implementation[Input, Output, Unit]], (Input) -> Expected, (Expected, Output) -> Bool, (Input) -> String, (Output) -> String) -> @runner.BenchSpec[Scale, Input, Input, Expected, Output, Unit, Output?, Output?]
pub fn is_significant_regression(@stats.Comparison) -> Bool
pub fn paired_hotspot(Array[@model.Observation], String, Int, String, String, Double, UInt64) -> Result[@stats.Comparison, @stats.BootstrapError]
pub async fn[Scale, Input, Prepared, Expected, Output, Context, State, SinkValue] run(@runner.BenchSpec[Scale, Input, Prepared, Expected, Output, Context, State, SinkValue], @model.EnvironmentSnapshot, @event.ObservationSink, UInt64, @runner.ValidatedProtocol) -> @model.RunSummary
pub fn tune_dataset(Array[@model.Observation], String, Int, Array[String], Double) -> TuneDecision?
// Errors
// Types and methods
pub struct TuneDecision {
dataset_id : Int
candidate_id : String
median_us : Double
valid_samples : Int
}
// Type aliases
// Traits