ベンチマーク手法
asiai は確立されたベンチマーク標準(MLPerf、SPEC CPU 2017、NVIDIA GenAI-Perf)に従い、信頼性が高く、再現可能で、比較可能な結果を生成します。
プロトコル
- プリフライトゲートチェック: メモリプレッシャーがcriticalまたはシステムが大幅にスロットリング(80%未満)の場合、開始を拒否
- ウォームアップ: エンジンごとに1回の計測外生成でJITコンパイラとキャッシュをプライミング
- 計測実行: デフォルトでプロンプトごと・エンジンごとに3回実行(
--runsで設定可能) - サンプリング: 決定論的な出力のため
temperature=0(グリーディ) - モデルアンロード: 各エンジンのベンチマーク後、次のエンジン開始前にモデルをアンロードしてユニファイドメモリを解放。大規模モデルの比較時のメモリ蓄積とスワッピングを防止
- アダプティブクールダウン: アンロード後、macOSのメモリプレッシャーが「normal」に戻るまで待機(最大30秒)、さらに最低5秒のサーマルクールダウンを追加
- 健全性チェック: tok/s ≤ 0の結果は破棄。TTFT > 60sまたはtok/s > 500は警告を発生(スワッピングまたは測定エラーの可能性)
- レポーティング: 中央値tok/sをプライマリメトリクス(SPEC標準)、平均 ± 標準偏差をセカンダリとして使用
- スロットリング: いずれかの実行中に
thermal_speed_limit < 100%の場合に警告を出力。サーマルドリフト(実行間のtok/sの単調減少、≥ 5%の低下)を検出・報告 - メタデータ: エンジンバージョン、モデルフォーマット、量子化、ハードウェアチップ、macOSバージョンを結果ごとに保存
メトリクス
tok/s — 生成速度
プロンプト処理(TTFT)を除く生成時間のみの1秒あたりトークン数。
Ollama(ネイティブAPI、/api/generate):
tok_per_sec = eval_count / (eval_duration_ns / 1e9)
OpenAI互換エンジン(LM Studio、llama.cpp、mlx-lm、vllm-mlx):
generation_s = wall_clock_s - ttft_s
tok_per_sec = completion_tokens / generation_s
トークンカウント:サーバーレスポンスの usage.completion_tokens から取得。サーバーがこのフィールドを報告しない場合、asiai は len(text) // 4 にフォールバックし警告をログに記録します。このフォールバックは最大約25%の誤差があります。
クロスバリデーション(2026年4月、Qwen3.5-35B NVFP4、M4 Pro 64GB):
| 方式 | tok/s | リファレンスとの差異 |
|---|---|---|
| Ollama ネイティブ(内部GPU) | 66.6 | リファレンス |
| OpenAI streaming(クライアント) | 66.1 | -0.8% |
大規模コンテキストサイズ(例:64kトークン)では、TTFTが総時間を支配する場合があります。tok/sからTTFTを除外することで、高速なジェネレーターが遅く見えるのを防ぎます。
TTFT — 最初のトークンまでの時間
リクエスト送信から最初の出力トークン受信までの時間(ミリ秒)。
v1.6.0以降、asiai はOllamaに対して2つのTTFT値を測定し、他のすべてのエンジンに対しては1つを測定します:
Ollama(二重測定):
- サーバーサイドTTFT(
ttft_ms):Ollamaレスポンスのprompt_eval_durationから抽出。ネットワークオーバーヘッドゼロの純粋なGPUプロンプト処理時間 — 可能な限り最も正確な測定です。ttft_source: serverとして報告。 - クライアントサイドTTFT(
ttft_client_ms):最初のSSEコンテンツチャンク到着時に測定。HTTPセットアップ、リクエスト送信、サーバー処理を含みます。他のすべてのエンジンと同じ方法です。
OpenAI互換エンジン(LM Studio、llama.cpp、mlx-lm、vllm-mlx):
- クライアントサイドTTFT(
ttft_client_ms):最初のSSEコンテンツチャンクで測定。これらのエンジンは内部プロンプト処理タイミングを公開しないため、利用可能な唯一の測定方法です。ttft_msとttft_client_msは同じ値を含みます。
比較可能なメトリクス:ttft_client_ms はエンジン間で比較可能なメトリクスです — エンジンに関係なく同じ測定方法を使用します。異なるエンジン間でTTFTを比較する場合はこれを使用してください。OllamaのサーバーサイドTTFT ttft_ms は絶対的なプロンプト処理時間としてはより正確ですが、他のエンジンと直接比較することはできません。
クロスバリデーション(2026年4月、Qwen3.5-35B NVFP4、M4 Pro 64GB):
| 方式 | TTFT | 差異 |
|---|---|---|
Ollama サーバーサイド(ttft_ms) |
27 ms | リファレンス |
Ollama クライアントサイド(ttft_client_ms) |
51 ms | +24 ms |
24msの差異はlocalhostにおけるHTTPオーバーヘッドを表しています。このオーバーヘッドは一貫性があり予測可能ですが、エンジン間を比較する際には十分に重要です。
Power — GPU電力(ワット)
実行中の平均GPU電力。Apple IOReport Energy Modelフレームワークで測定(sudo不要)。エンジンごとに1回の測定 — セッション全体の平均ではありません。
tok/s/W — エネルギー効率
tok_per_sec_per_watt = tok_per_sec / power_watts
Variance — プールド標準偏差
プールされたプロンプト内標準偏差。プロンプト間のばらつきを混在させずに、実行間のノイズを捕捉します。ベッセルの補正(N-1分母)を使用し、不偏標本分散を算出します。
安定性分類:
- CV < 5% →
stable - CV < 10% →
variable - CV >= 10% →
unstable
CV = (std_dev / mean) * 100
VRAM — メモリ使用量
プライマリ:エンジンネイティブAPI(Ollama /api/ps、LM Studio /v1/models)。
フォールバック:ctypes経由の ri_phys_footprint(Activity Monitorと同じ)。UIで「(est.)」とラベル付け。
環境安全性
asiai はベンチマーク前チェックを実行します:
- メモリプレッシャー:criticalの場合は開始を拒否
- サーマルスロットリング:速度制限 < 80% の場合に警告
- 重複プロセス:同じエンジンの複数インスタンスが実行中の場合に警告(例:同じポートで2つの
ollama serveプロセス) - エンジンrunnerタイプ:Ollamaの場合、
--mlx-engineまたは--ollama-enginerunnerがアクティブかを検出
これらのチェックにより、リソース競合や誤ったルーティングによる測定エラーを防止します。
準拠状況
| プラクティス | ステータス |
|---|---|
| プリフライトゲートチェック(メモリプレッシャー + サーマル) | 実装済み |
| 重複プロセス検出 | 実装済み (v1.5.0) |
| Ollama runnerタイプ検出(MLX vs llama.cpp) | 実装済み (v1.5.0) |
| TTFTをtok/sから分離 | 実装済み |
| TTFTソースラベリング(server vs client) | 実装済み (v1.5.0) |
| 二重TTFT測定(server + client) | 実装済み (v1.6.0) |
| 決定論的サンプリング(temperature=0) | 実装済み |
| サーバーAPIからのトークンカウント(SSEチャンクではない) | 実装済み(フォールバック時に警告) |
| エンジンごとの電力監視(IOReport、sudo不要) | 実装済み |
| エンジンごと1回のウォームアップ生成 | 実装済み |
| デフォルト3回実行(SPEC最小値) | 実装済み |
| プライマリメトリクスとしての中央値(SPEC標準) | 実装済み |
| プールドプロンプト内標準偏差(Bessel N-1) | 実装済み(v1.5.0で修正) |
| エンジン間のモデルアンロード | 実装済み |
| アダプティブクールダウン(メモリプレッシャー対応) | 実装済み |
| 健全性チェック(tok/s、TTFTの境界値) | 実装済み |
| サーマルスロットリング検出 + 警告 | 実装済み |
| サーマルドリフト検出(単調減少) | 実装済み |
| エンジンバージョン + runnerタイプを結果ごとに保存 | 実装済み (v1.5.0) |
| ri_phys_footprintによるユニバーサルVRAM | 実装済み |
| 過去のリグレッション検出 | 実装済み |
| クロスバリデーションスクリプト(3手法を比較) | 利用可能 (scripts/cross-validate-bench.py) |
Apple Siliconに関する考慮事項
ユニファイドメモリ
Apple SiliconはCPUとGPUでメモリを共有します。asiai はエンジンを順次実行し、エンジン間でモデルをアンロードすることでメモリ競合とスワッピングを回避します。VRAMはOllamaとLM Studioでネイティブに報告されます。他のエンジンでは、ri_phys_footprint(Activity Monitorと同じmacOSの物理フットプリントメトリクス)によりメモリ使用量を推定します。推定値はUIで「(est.)」とラベル付けされます。
サーマルスロットリング
- MacBook Air(ファンなし):持続負荷で深刻なスロットリング
- MacBook Pro(ファン付き):軽度のスロットリング
- Mac Mini/Studio/Pro:アクティブ冷却、最小限のスロットリング
asiai は結果ごとに thermal_speed_limit を記録し、スロットリングが検出された場合に警告します。
KVキャッシュ
大規模コンテキストサイズ(32k以上)では、KVキャッシュを事前割り当てするエンジンで不安定になる場合があります。公平な結果のために、エンジンのコンテキスト長を実際のテストサイズに合わせてください。
電力測定
asiai はAppleのIOReport Energy Modelフレームワークを通じてGPU、CPU、ANE、DRAMの消費電力を測定します — sudo不要。電力はすべてのベンチマークとすべての監視スナップショットで自動的に測定されます。
IOReportは sudo powermetrics と同じハードウェアエネルギーカウンターを読み取りますが、ユーザースペースAPI(ctypes経由の libIOReport.dylib)を通じて行います。これによりパスワードなしsudo設定の必要性が排除されます。
バリデーション
M4 Pro 64GBでLLM推論負荷下で、エンジンごとに2秒間隔で10個のペアサンプルを使用してIOReportを sudo powermetrics と比較検証しました:
| エンジン | IOReport平均 | powermetrics平均 | 平均差異 | 最大差異 |
|---|---|---|---|---|
| LM Studio (MLX) | 12.6 W | 12.6 W | 0.9% | 2.1% |
| Ollama (llama.cpp) | 15.6 W | 15.4 W | 1.3% | 4.1% |
両エンジンで10/10ペアサンプルにおいて平均差異1.5%未満を確認。ANE電力は全20サンプルで0.000Wであり、現在LLMエンジンがNeural Engineを使用していないことを確認。
--power フラグはIOReportと sudo powermetrics を同時に実行する追加のクロスバリデーションを有効にし、両方の読み取り値を比較用に保存します。
電力効率
電力効率(ワットあたりtok/s)はベンチマーク結果ごとに tok_per_sec / gpu_watts として計算されます。このメトリクスにより、エンジンとハードウェア間の推論コストの比較が可能になります。
メタデータ
すべてのベンチマーク結果には以下が保存されます:engine、engine_version、model、model_format、model_quantization、hw_chip、os_version、thermal_level、thermal_speed_limit、power_watts、power_source、metrics_version。これにより公平なリグレッション比較とクロスマシンベンチマークが可能になります。