perf_support の設計

設計目標

数値カーネルのベンチマークが役に立つのは、再現可能で比較可能な場合だけです。すなわち、どの実行・どのマシンでも同じ入力を使い、準備ではなく計算そのものを計測し、計測したコードが実際に実行されたことを保証する必要があります。perf_support は、このリポジトリのベンチマークサブシステムにそれらの保証を提供します。

数学的背景

決定的な入力

入力はケースごとのシードから SplitMix64 生成器で生成し、目的の分布(たとえば [−1,1][-1, 1] 上の一様分布)に写します。構造を持つ族は、そうした乱数から構成的に作ります。たとえば対称正定値行列を MTM+αIM^{\mathsf T} M + \alpha I として作ります。x≠0x \ne 0 について xT(MTM+αI)x=∥Mx∥2+α∥x∥2>0x^{\mathsf T}(M^{\mathsf T}M + \alpha I)x = \lVert Mx \rVert^2 + \alpha\lVert x\rVert^2 > 0 なので、これは任意の α>0\alpha > 0 について SPD です。同じシードからは常に同じビット列が得られるので、フィクスチャは保存せずに再生成できます。

チェックサム

各実行は、その結果のビットパターンを FNV 風の混合で 64 ビット値に畳み込みます。

h0=1469598103934665603,hk+1=(hk⊕wk)⋅1099511628211 mod 264,h_0 = 1469598103934665603, \qquad h_{k+1} = (h_k \oplus w_k) \cdot 1099511628211 \bmod 2^{64},

ここで wkw_k は kk 番目の出力値の IEEE ビットパターンです(行列では形状が先)。チェックサムには 2 つの目的があります。コンパイラが計算をデッドコードとして除去するのを防ぐことと、実行・ターゲット・バージョン間で結果のビットが変わったことを検出することです。暗号学的ハッシュではありません。

設計上の判断

フィクスチャは実行時、レジストリはコード

ケースの一覧は bench/datasets/manifest.json から MoonBit のコード(generated_registry.mbt)として生成され、大きな入力配列は実行時に読み込む JSON ファイルに置かれます。これによりベンチマークのバイナリが小さく保たれてコードサイズが計測を歪めず、Rust のベースラインも同じフィクスチャを利用できます。

自己修復するフィクスチャ

フィクスチャファイルがない場合は、レジストリのシードから再生成して書き出すので、クリーンなチェックアウトでもどのケースでも実行できます。ファイルが存在してもバージョン、メタデータ、形状がレジストリと一致しない場合は実行を中断します。食い違った入力を黙って使うと、数値が無意味になってしまうからです。

変更ポリシー

入力を変更する操作もあります(たとえば reduce_row_elimination)。そのため mutation_policy = "scratch_per_sample" のケースは新しいコピーに対して実行し、どのサンプルも同じ処理を計測するようにします。

検査なしカーネル

ベンチマークは unchecked_* メソッドを呼び出します。入力が前提条件を満たすことはわかっており、計測したいのは検証のコストではないからです。

正しさと不変条件

  • データセットのバージョンを固定すれば、prepare_case(c) はどの実行でもビット単位で同一の入力を生成します。
  • run_prepared_case_inplace(p, true) は p を変更しません。
  • チェックサムはすべての出力値と出力の形状に依存します。

却下した代替案

  • すべての入力をコードに埋め込む。 大きなリテラル配列はバイナリとコンパイル時間を膨らませます。
  • 実行ごとにランダムな入力。 実行間で結果を比較できなくなります。

境界

perf_support 自身は何も計測しません。計時と統計量は perf_runner、perf、bench/run.py にあります。対象は Double 入力の @mutable パッケージだけで、既定のテストゲートには含まれません。