リアルタイムグラフィックスのためのジョン・カーマック流パフォーマンス思考
ジョン・カーマック流の「パフォーマンス最優先」マインドセットの実践ガイド:プロファイリング、フレームタイム予算、トレードオフ、複雑なリアルタイムシステムを出荷する方法まで。

なぜカーマックのアプローチが今も重要なのか
ジョン・カーマックはゲームエンジン界の伝説として扱われがちですが、有用なのは神話ではなく繰り返し再現できる習慣です。これは個人のスタイルをそのまま真似する話でも「天才の一手」を想定する話でもありません。締め切りや複雑さが積み重なる状況で、より速く滑らかなソフトウェアを安定的に作るための実践的原則の話です。
平易に言えばパフォーマンスエンジニアリングとは
パフォーマンスエンジニアリングは、実機の実際の条件下でソフトウェアが速度目標を満たすようにすること—正しさを壊さずに—です。これは「何が何でも速くする」という話ではありません。規律あるループです:
- 「十分速い」が何かを決める
- 時間が実際にどこへ行っているかを測る
- 意図的に1つの変更を加える
- 正しい指標が改善したことを検証する
このマインドセットはカーマックの仕事に何度も現れます:データで議論し、変更は説明可能に保ち、維持しやすい手法を好む。
なぜリアルタイムグラフィックスは現実を暴くのか
リアルタイムグラフィックスは厳しい締め切り(各フレームごと)を持つため容赦がありません。締め切りを守れなければ、ユーザーは即座にスタッター、入力遅延、不均一な動きとして感じます。他のソフトウェアはキューやロード画面、バックグラウンド作業で非効率を隠せますが、レンダラは妥協できません:時間内に終えるかどうかだけです。
この教訓はゲーム以外にも一般化します。UI、オーディオ、AR/VR、トレーディング、ロボティクスなど、遅延に厳しいシステムは予算思考、ボトルネックの理解、突発的なスパイクの回避から恩恵を受けます。
得られるもの
フレームタイム(あるいはレイテンシ)予算の立て方、最適化前のプロファイリングのやり方、修正する「一つのこと」の選び方、そして回帰を防いでパフォーマンスを後回しのパニックでなく日常運用にするためのチェックリストやヒューリスティクスが手に入ります。
FPSではなくフレームタイムで考える
カーマック流のパフォーマンス思考は単純な切り替えから始まります:主要単位として「FPS」を語るのをやめ、フレームタイムで語り始めることです。
FPSは逆数で(「60 FPS」は良い、「55 FPS」は近いと感じられる)感覚に基づく指標ですが、ユーザー体験は各フレームにかかる時間と、その時間の一貫性に左右されます。16.6 msから33.3 msに跳ねると、平均FPSが見かけ上悪くなくても即座に違和感が出ます。
フレームタイムが優れる理由
- FPSは変動を隠す。 平均60 FPSのビルドが2つあっても、片方は40–60 msの断続的なフレームでスタッターするかもしれません。
- フレームタイムは作業量に対応する。 ミリ秒単位でCPU/GPUの作業を各システムに帰属できます。
- 目標が明確になる。 「16.6 ms未満に保つ」は具体的な要件です。 「滑らかに感じる」はそうではありません。
何に対して予算を使っているのか
リアルタイム製品は「レンダを速くする」以外の複数の予算を持っています:
- CPU時間(ゲームロジック、アニメーション、カリング、ドローコールの送信)
- GPU時間(シェーディング、ポストプロセス、オーバードロー、解像度)
- メモリ(フットプリント、スパイク、断片化、ストリーミングの余力)
- 読み込み時間(起動、レベルロード、シェーダーコンパイル、ストリーミングのスタール)
これらの予算は相互に影響します。GPU時間を節約するためにCPU負荷の高いバッチングを入れると裏目に出ることもあり、メモリを削るとストリーミングや解凍コストが増えることもあります。
例:60 FPSで16.6 ms
もし目標が60 FPSなら、総予算は1フレームあたり16.6 msです。大まかな内訳は次のようになるかもしれません:
- CPU: 7 ms(シミュレーション、ゲームプレイ、可視性)
- GPU: 9 ms(レンダ+ポスト)
- OS/ドライバ+オーバーヘッドバッファ: 約0.6 ms
CPUかGPUのどちらかが予算を超えればフレームを落とします。だからチームは「CPUバウンド」「GPUバウンド」と話すのです—ラベルではなく、次にどこからミリ秒を取るかを決めるための手段です。
「十分速い」は製品要件である
重要なのはハイエンドPCでの最高FPSを追いかけることではありません。ターゲットとするオーディエンス(対象ハード、解像度、バッテリー制限、サーマル、入力応答性)にとって何が十分速いかを定義し、それを管理・擁護できる明示的な予算として扱うことです。
まずプロファイリング:計測してから決める
カーマックのデフォルトの動きは「最適化する」ではなく「検証する」です。リアルタイムのパフォーマンス問題はもっともらしい説明が山ほどあり、その多くはあなたのビルドや機材では間違っています。プロファイリングによって直感を証拠に変えます。
まず測る(推測する前に)
プロファイリングを最終手段ではなく第一級の機能として扱ってください。フレームタイム、CPU/GPUのタイムライン、そしてそれらを説明するカウント(トライアングル数、ドローコール、状態変更、アロケーション、可能ならキャッシュミス)を取得します。目的は一つの問いに答えること:時間は実際にどこに行っているのか?
有用なモデル:遅いフレームごとに一つの要素が制約になっていることが多いです。GPUが重いパスで詰まっているのか、CPUのアニメーション更新が詰まっているのか、メインスレッドが同期で止まっているのか。まずその制約を見つけ、他はノイズと見なします。
科学者のように反復する
規律あるループがムダな改変を防ぎます:
- 再現可能なシーンとカメラパスでベースラインを測る
- 1つだけ変更する
- 再測定して差分を記録する
改善が明確でないなら、それは助けになっていないと仮定してください—次のコンテンツ追加で残らない可能性が高いです。
プラセボ最適化に注意
パフォーマンス作業は自己欺瞞に陥りやすいです:
- ベンチマークの誤り: テストシーンが不安定、デバッグビルド、バックグラウンドタスク、サーマルスロットリング、VSyncの違い
- 確証バイアス: フレームタイムデータなしに「速く感じる」と主張する
- 誤解を招く平均: 平均が良くなってもスパイクが悪化している場合がある
まずプロファイリングすることで努力が集中し、トレードオフが正当化され、変更はレビューで説明しやすくなります。
ボトルネック:実際に遅い“ひとつのこと”を見つける
リアルタイムのパフォーマンス問題はごちゃごちゃしているように見えますが、実は同時に多くの作業が進んでいるからです:ゲームプレイ、レンダリング、ストリーミング、アニメーション、UI、物理。カーマックの本能はノイズを切り裂き、現在フレームタイムを決めている支配的制約を特定することです。
よくあるボトルネックのカテゴリ
遅延の大部分は次のいくつかのバケットに入ります:
- CPUバウンド: メインスレッドや重要なワーカーが時間内に仕事を終えられない(ゲームロジック、ドローコール送信、物理、アニメーション評価)。
- GPUバウンド: GPUがフレームを終えられない(重いシェーダー、ピクセル多め、重いポスト処理、複雑なジオメトリ)。
- メモリバウンド: 帯域やレイテンシに制約される(キャッシュミス、データの分散、頻繁なコピー)。
- I/Oバウンド: アセットのストリーミング、シェーダーコンパイル、解凍、ファイル読み込み、ネットワーク待ち。
ラベル付けが目的ではなく、適切なレバーを選ぶことが目的です。
書き換える前にできる簡単な診断
いくつかの高速な実験で、何が支配的か分かります:
- 解像度スケーリングテスト: レンダ解像度を落とす(または動的解像度を強制)。フレームタイムが大きく改善するならGPU/ピクセルが支配的。
- 機能トグル: シャドウ、SSR、AO、パーティクル、重いパスを一つずつオフにする。大きな変化が出た機能が時間を食っている証拠。
- 計装とキャプチャ: 組み込みタイマー、CPUプロファイラ、GPUキャプチャでミリ秒がどこに落ちているかを見る。
「一つの大きな石」原則
10のシステムをそれぞれ1%削るより、毎フレーム繰り返される最大のコストを見つけて潰す方が勝ちます。単一の4 msのオフエンダーを取り除く方が、週単位のマイクロ最適化より効果的です。
ボトルネックは移動する
大きな石を直すと、次に大きな石が見えてきます。それは正常です。パフォーマンス作業はループです:測る→変える→再測定→優先順位付け。目標は完璧なプロファイルではなく、予測可能なフレームタイムに向けた着実な進捗です。
滑らかさが勝つ:スパイク、スタッター、テール遅延
平均フレームタイムが良くても、体験が悪いことがあります。リアルタイムグラフィックスは最悪の瞬間で評価されます:大爆発で落ちるフレーム、新しい部屋に入ったときのヒッチ、メニュー開閉時の突然のスタッター。これがテール遅延です—まれだが許容できない遅いフレーム。
なぜテールが平均より重要か
たとえ通常は16.6 ms(60 FPS)で動作していても、数秒ごとに60–120 msにスパイクするなら体感は「壊れている」となります。人間はリズムに敏感で、長いフレームひとつで入力予測性、カメラ動作、音声/映像の同期が壊れます。
スパイクの一般的原因
スパイクは均等に分散されない作業から生じます:
- ガベージコレクションやメモリページフォルトで世界が止まる
- シェーダーコンパイルやパイプライン作成がオンデマンドで走る
- アセットストリーミングが突然解凍やアップロード、ファイルI/Oを必要とする
- OSのスケジューリングやバックグラウンド作業がCPU時間を奪う(または周波数/サーマルが変化する)
スタッターを減らすための戦略
高コストな作業を予測可能にする:
- 事前計算:可能なものはビルド時に済ませる(シェーダーのオフラインビルド、ベイクデータ)
- ウォームアップ:ロード画面や制御されたウォームアップシーンでシェーダーやパイプラインをコンパイル・作成する
- アモタイズ:ストリーミングや解凍、アップロードを複数フレームに分散する
- 1フレームの作業を制限する:時間予算を設け、残りは遅延させる(例:「今フレームのストリーミングは最大2 ms」)
テールを記録して可視化する
平均FPSのラインだけを描かないでください。フレームごとのタイミングを記録して可視化します:
- フレームタイムのヒストグラムでクラスタと外れ値を確認
- パーセンタイル(p95、p99、p99.9)でテールを追う
- スパイクマーカーと相関イベント(GC開始、シェーダーコンパイル、アセットロード)
最悪の1%のフレームを説明できないなら、パフォーマンスを本当に説明したことにはなりません。
トレードオフを明確にする(品質 vs 速度 vs 複雑さ)
すべてを一度に得られると装わない瞬間からパフォーマンス作業は容易になります。カーマック流はチームにトレードオフを声に出して名前を付けさせます:何を得て、何を払って、誰が違いを感じるのか。
軸を明確にする(そして実コストを示す)
多くの決定は次の軸上にあります:
- 品質:視覚的忠実度、シミュレーション精度、入力フィール
- 速度:フレームタイム、ロード時間、コンパイル時間、反復時間
- メモリ:VRAM、RAM、帯域
- 複雑さ:デバッグ難易度、エッジケース、テスト負担
- 出荷までの時間:スケジュールリスク、統合リスク、チームの集中度
ある変更が1つの軸を改善して3つにコストを課すなら、それを文書化してください。「これでソフトシャドウが柔らかくなるがGPUに0.4 ms、VRAMに80 MBを追加する」は有用な表現です。「見た目が良い」だけでは不十分です。
「十分良い」閾値を定義する
リアルタイムグラフィックスは完璧を目指すものではなく、目標を一貫して満たすことが重要です。次のような閾値に合意してください:
- 参照機での最低FPS/最大フレームタイム
- 許容できる最悪スパイク(平均だけでなく)
- プラットフォームごとのメモリ上限
チームが例えば「1080p、ベースラインGPUで16.6 ms」という目標に合意すれば、議論は具体化します:この機能は予算内か、それとも他を削るべきか?
元に戻せる決定を好む
不確かなら元に戻せる選択を:
- リスクのある効果はフィーチャーフラグで管理する
- ロー/ミッド/ハイのスケーラブル設定を実装し、実コストにマップする
- 古いハード向けのフォールバックパスを用意する
可逆性はスケジュールを保護します。安全側で出荷して、野心的なものはトグルの向こうに残しておけます。
ユーザーが体感する部分を最優先にする
目に見えない小さな平均改善に多くの工数を投じないでください。1%の平均改善が1ヶ月の複雑さに見合うことは稀です—ただし、それがスタッターを無くす、入力遅延を改善する、致命的なメモリクラッシュを防ぐなら別です。プレイヤーが即座に感じる変更を優先し、残りは後回しにします。
工学的規律:正しさが速度を可能にする
プログラムが正しいとき、パフォーマンス作業は劇的に楽になります。多くの「最適化」時間は実際には正しさのバグ追跡に費やされています:重複作業で生じた偶発的なO(N^2)ループ、フラグのリセット忘れで2回実行されるレンダーパス、フレームタイムを徐々に悪化させるメモリリーク、ランダムなスタッターを生むレースコンディションなど。
正しさをパフォーマンスツールとして扱う
安定で予測可能なエンジンはクリーンな計測を与えます。挙動が実行ごとに変わるとプロファイルを信用できず、ノイズを最適化してしまいます。
規律ある工学プラクティスが速度を助けます:
- 明確な不変条件(invariants):常に成り立つべきことを定義する(例:「各可視オブジェクトは一度だけサブミットされる」「GPUリソースはインフライト中に変更されない」「フレームグラフにサイクルがない」)。
- デバッグビルドでの検証:アサーションや軽量チェックを入れて早期に検出する。バッファサイズや状態遷移、フレームごとのアロケーションが既知の限界内にあるかを検証する。
パフォーマンスバグを必ず再現可能にする
多くのフレームタイムスパイクは「ハイゼンバグ」です:ログを足したりデバッガで止めると消える。解決策は決定論的な再現です。
小さな制御されたテストハーネスを作ります:
- 最小テストシーンで機能を分離(シャドウ、パーティクル、UI、ストリーミングなど)
- 固定カメラパスとスクリプト化された入力で毎回同じ実行にする
- 設定を固定(解像度、品質レベル、可能なら固定タイムステップ)して変数を減らす
ヒッチが出たら、それを100回再生できるボタンが欲しい—「10分後に時々出る」という曖昧な報告ではなく。
変更は小さく、学びは多く
スピード作業は小さくレビュー可能な変更が向きます。大きなリファクタは多くの失敗モードを同時に生みます:回帰、新たなアロケーション、隠れた追加作業。差分が小さければ「フレームタイムが何で、なぜ変わったか?」に答えやすくなります。
規律は官僚主義ではなく、測定の信頼性を保ち、最適化を迷信でなく確かなものにするための手段です。
マシンと協調する:データ、キャッシュ、オーバーヘッド
リアルタイムのパフォーマンスは「より速いコード」だけが全てではありません。CPUとGPUが効率よく作業できるように仕事を配置することです。カーマックは繰り返し言っています:マシンは文字どおりで、予測可能なデータが好きで、避けられるオーバーヘッドを嫌います。
データ指向の考え方:メモリを読みやすくする
現代のCPUは非常に高速ですが、メモリ待ちになると途端に停滞します。データがたくさんの小さなオブジェクトに分散していると、CPUはポインタ追跡に時間を費やし計算が進みません。
有用な比喩:10個の買い物に10回買い物に行くな。1つのカートにまとめて、通路を一度だけ歩け。コードでは頻繁に使う値を近くに集め(配列やタイトにパックした構造体)、1つのキャッシュラインで使うデータを多く取り込めるようにします。
アロケーションパターン:小さな churn が大きな痛みになる
頻繁なアロケーションは隠れたコストを生みます:アロケータのオーバーヘッド、メモリ断片化、不定期のポーズ。たとえ1つずつは小さくても、継続的なストリームはフレームごとに税を課します。
一般的な対策は意図的に地味です:バッファを再利用する、オブジェクトプールを使う、ホットパスでは長寿命アロケーションを好む。目的は巧妙さではなく一貫性です。
バッチング:数学を最適化する前にオーバーヘッドを減らす
フレームタイムのかなりの部分はブックキーピングに消えます:状態変更、ドローコール、ドライバ作業、システムコール、スレッド同期など。
バッチングはレンダリングとシミュレーションの「大きなカート」版です。多数の小さな操作を発行する代わりに類似作業をまとめ、コストの高い境界を越える回数を減らします。多くの場合、オーバーヘッドを削ることがシェーダーやインナーループを微調整するより大きな勝利を生みます。
簡潔さをパフォーマンス戦略にする
パフォーマンス作業は単に速いコードを作ることだけでなく、コードを少なく持つことでもあります。複雑さには毎日支払うコストがあり:バグの特定に時間がかかり、修正にはより注意深いテストが必要になり、反復が遅くなり、回帰が稼働頻度の低い経路から忍び込むことがあります。
複雑さの隠れた税
「巧妙な」システムは美しく見えるかもしれませんが、締め切り前に特定のマップ、GPU、設定コンボだけでスパイクが出ると問題になります。余計なフィーチャーフラグ、フォールバック、特例は挙動の組み合わせを増やし、理解と計測の負担を増やします。この複雑さは開発者時間を浪費するだけでなく、ランタイムオーバーヘッド(余分な分岐、アロケーション、キャッシュミス、同期)として現れることが多く、手遅れになるまで見えにくいです。
説明できる解決を優先する
良いルール:パフォーマンスモデルを数分でチームメイトに説明できないなら、おそらく信頼して最適化できません。
単純な解決には二つの利点があります:
- プロファイリングと推論が容易(変数が少ない)
- マイナーな変更が予期せぬ遅延を生む「未知の未知」を減らす
「コードを消す」は実際の最適化手段である
時には最速の道は機能を削ること、オプションを減らすこと、複数のバリアントを統合することです。機能が少なければコードパスが少なくなり、状態の組み合わせが減り、パフォーマンスが密かに劣化する場所が減ります。
コードを削ることは品質向上でもあります:生成し得るバグを生むモジュールを削除してしまえば、そのバグは存在しません。
リファクタかパッチか?迅速な判断チェックリスト
パッチ(外科的修正)を選ぶとき:
- 特定のホットパスが特定され、小さな変更で測定可能に改善する場合
- システムが安定して広く使われており、アーキテクチャ変更が新たな回帰を招くリスクがある場合
- 現行リリース予定に収まる安全な改善が必要な場合
リファクタ(構造の簡素化)を選ぶとき:
- プロファイリングが多くの呼び出し箇所やレイヤーにまたがるオーバーヘッドを示す場合
- 同じ領域で別の変更が入るたびにパフォーマンスが壊れる場合
- コードを安全に変更するには部族的知識が必要な場合
- パスを削除・統合して概念数を減らせる場合
簡潔さは「野心がない」ことではありません。プレッシャー下でも理解できる設計を選ぶことです—パフォーマンスが最も重要なときに。
回帰を防ぐ:パフォーマンスを習慣にする
パフォーマンス作業が定着するのは、劣化を検知できる仕組みがあるときだけです。これがパフォーマンス回帰テストの目的:新しい変更が遅くしていないか、滑らかさを失っていないか、メモリ負荷が重くなっていないかを反復可能に検出することです。
機能テストが「動くか」を答えるのに対し、回帰テストは「速さが保たれているか」を答えます。ビルドが機能的に正しくても、フレームタイムが4 ms増えたりロードが倍になったら良いリリースとは言えません。
実際に使われる軽量ワークフロー
ラボは不要です—一貫性が必要です。
- 実使用を代表する小さなベースラインシーンを選ぶ:GPU重視のビュー、CPU重視のビュー、ワーストケースのストレスシーン
- それらを安定化・スクリプト化して毎回同じカメラパスと入力にする
- 固定ハードウェア(既知のPC/コンソール/開発機)で実行し、ドライバやOS、クロックを変えたら記録する
- 結果をバージョン管理された履歴として保存:コミットハッシュ、ビルド設定、マシンID、測定指標
目標は完璧な数値ではなく、信頼できるトレンドラインです。
CIに馴染む指標
議論しにくい指標を選びます:
- フレームタイムのパーセンタイル(p50/p95/p99)、平均FPSだけでなくパーセンタイルを追う
- ピークメモリ(とアロケーションのスパイク)。メモリの増加はクラッシュの前触れになることが多い
- ロード時間(コールドスタートとシーン遷移)、プレイヤーは数秒の差を敏感に感じる
シンプルな閾値を定義する(例:p95フレームタイムが5%以上悪化してはいけない)。
回帰を検出したら
回帰はバグとして扱い、オーナーと期限をつけます。
まずバイセクトして導入された変更を特定する。回帰がリリースを阻害するなら素早くリバートし、修正を入れて再適用します。
修正したらガードレールを追加:テストを残し、コードに注記し、期待される予算を文書化する。習慣化が勝利です—パフォーマンスは「後でやるもの」ではなく維持するものになります。
複雑系を出荷する:パフォーマンス、締め切り、現実
「出荷」はカレンダーのイベントではなく工学的要件です。ラボでしかうまく動かない、手作業で調整しないと目標に届かないシステムは完成とは言えません。カーマックのマインドセットは実世界の制約(ハードの多様性、雑多なコンテンツ、予測不能なプレイヤー行動)を初期仕様として扱います。
出荷とは何を必ず満たすかを決めること
リリース間近では完璧より予測可能性が重要です。必須条件を平易に定義します:目標FPS、最悪フレームタイムの閾値、メモリ上限、ロード時間。これらに違反するものを「ポリッシュ」ではなくバグとして扱います。これによりパフォーマンス作業は任意の最適化から信頼性確保に変わります。
プレイヤーが実際に感じることを優先する
すべての遅延が同じ重みではありません。ユーザー視点で重要な問題を先に直します:
- スタッターや長いスパイクは、平均がわずかに遅いレンダリングより体感品質に悪影響を与えます。\n- メニューのヒッチ、ストリーミングのポップ、入力遅延は平均FPSの小さな低下より体験を損ねます。\n- 日常的なシナリオ(戦闘の多い場面、カメラ回し、エフェクト多発時)での回帰は稀なコーナーケースより優先度が高いです。
プロファイリングの規律が効きます:どの問題が「大きい」かを測定に基づいて選べます。
変更は段階的に、デフォルトは安全側に
レイトサイクルでのパフォーマンス作業は危険です。変更は計測器を先に入れ、トグルの背後で有効にし、露出範囲を広げる運用が安全です。デフォルトはパフォーマンスを守る設定を選ぶべきで、見た目が少し控えめでも安定感を優先します。
プラットフォームやティアが複数あるなら、デフォルトは製品的な決定です:豪華に見せるより安定して感じさせる方が良い場合が多い。
非技術系ステークホルダーに制約を伝える
トレードオフを成果ベースで翻訳します:「この効果は中級GPUで毎フレーム2 msを消費し、戦闘時に60 FPSを割るリスクがあります」。提案は選択肢にします:解像度を下げる、シェーダーを簡素化する、スポーン率を制限する、あるいは低い目標を受け入れる。制約は具体的な選択肢とユーザーへの影響で示すと受け入れられやすいです。
今日このマインドセットを適用するための実用チェックリスト
新しいエンジンや全面書き換えは不要です。カーマック流の思考を採り入れるには、パフォーマンスを可視化し、テスト可能にし、偶発的に壊れにくくする反復可能なループが必要です。
反復ループ(測る→予算→切り分け→最適化→検証→記録)
- 測る: ベースライン(平均、p95、最悪のスパイク)をキャプチャする。\n
- 予算化: CPUとGPU(必要ならメモリ)ごとのフレーム予算を設定し、機能目標の横に書く。\n
- 切り分け: 最小シーンやテストでコストを再現する。再現できないなら直せない。\n
- 最適化: 1つずつ変更する。単に「速くする」のではなく作業量を減らす変更を優先する。\n
- 検証: 再プロファイルして差分を比較し、品質や正しさの回帰がないか確認する。\n
- 記録: 何を変え、なぜ効果があり、将来何を監視するかを記録する。
すぐに適用できる経験則
- 一番大きなバーを最適化せよ、最も目につく推測を追うな。
- ユーザーが感じるなら平均よりスパイクを追え。
- コストを説明できないなら、その機能をまだ所有していない。
- まれな最悪ケースより予測可能なコストを好め。
- 新しい作業はあらかじめ予算化する(CPU ms、GPU ms、メモリ、帯域)。
- オブジェクト当たり/フレーム当たりの隠れループを避ける。
- パフォーマンステストを「完了」の一部にする、事前の大慌てにしない。
マージ前の簡単な「パフォーマンスレビュー」テンプレート
- 機能要約: 何を変えたか、何を可能にするか
- ターゲットプラットフォーム&設定:(例:コンソールの性能モード、中級PC)
- 予算: CPU __ ms、GPU __ ms、メモリ __ MB
- ベースライン vs 変更後: 平均 / ms、p95 / ms、最悪スパイク / ms
- ボトルネック想定: CPUかGPUか? 証拠:
- テストシーン&再現手順:
- リスク&ガードレール: 何が回帰するか、どの指標がアラートを出すか
- ロールバックプラン: 無効化や段階的劣化の方法
Koder.aiがこのワークフローにどう合うか
これらの習慣をチーム横断で運用化したいなら、鍵は摩擦を減らすことです:手早い実験、再現可能なハーネス、簡単なロールバック。
Koder.aiはエンジン自体ではなく周辺ツールを構築する際に役立ちます。vibe-codingプラットフォームとして実際にエクスポートできるソースコード(ReactのWebアプリ、GoとPostgreSQLのバックエンド、Flutterのモバイル)を素早く生成できるので、フレームタイムパーセンタイルや回帰履歴、パフォーマンスレビューのチェックリスト用の内部ダッシュボードを速やかに立ち上げ、チャットで要件を進化させながらスナップショットやロールバックも利用できます。
詳しいガイダンスが欲しい場合は /blog を参照するか、チームがどのように運用化しているかは /pricing をご覧ください。
よくある質問
なぜ記事はFPSではなくフレームタイム(ms)を重視しているのですか?
フレームタイムはミリ秒(ms)で表される「1フレームあたりの時間」で、CPU/GPUが実際に行った作業量に直接対応します。
- FPSは逆数であるため変動を隠しがちです。
- フレームタイムはスタッターを露呈する(例えば、平均FPSが良く見えても40〜120 msの断続的なフレームがあると体感は悪くなります)。
- 予算化が容易です:16.6 ms = 60 FPS, 33.3 ms = 30 FPS。
プロジェクトに実用的なフレームタイム予算をどう設定すればよいですか?
例えば60 FPSを目標とするなら、それをハードな締め切り(16.6 ms)に変換してから、さらに明確な予算に分割します。
例としての出発点:
- CPU: 約7 ms
- GPU: 約9 ms
- オーバーヘッドバッファ: 約0.6 ms
これらを製品要件として扱い、プラットフォーム、解像度、サーマル、入力遅延などに合わせて調整してください。
最小限のプロファイリング環境として何が必要ですか?
まずテストを再現可能にし、何かを変える前に計測してください。
- 固定シーン+固定カメラパスを用意する
- CPUタイムライン+GPUタイムラインを取得する
- 補助データを記録する(ドローコール数、三角形数、アロケーション、ストリーミングイベントなど)
まず「時間がどこに行っているか」を把握してから最適化の判断をしてください。
短時間でCPUボトルネックかGPUボトルネックかを見分けるには?
支配的な制約を切り分ける高速な実験を行ってください。
- 解像度を下げる:大きく改善するならGPU/ピクセル限定の可能性が高い。
- 機能を一つずつ切る(シャドウ、SSR、AO、パーティクルなど):フレームタイムが大きく変わるものが“大きな石”です。
- CPUプロファイラとGPUキャプチャで確認する。
支配的なコストをミリ秒で名指しできるまで、システムを書き換えないでください。
なぜフレームタイムのスパイク(テール遅延)が平均FPSより重要なのですか?
ユーザーは平均ではなく「最悪のフレーム」を感じます。
次を追跡してください:
- パーセンタイル(p95/p99/p99.9)でテール遅延を把握する
- ヒストグラムでクラスターと外れ値を見る
- イベント相関(GC、シェーダーコンパイル、アセット読み込み)でスパイクの原因を特定する
平均が16.6 msでも80 msにスパイクするビルドは体感上壊れていると感じられます。
スタッターやヒッチを素早く減らす実用的な方法は?
高コストな作業を予測可能にし、分散させることが目的です:
- 事前計算(オフラインでのシェーダービルド、ベイク済みデータ)
- ウォームアップ(ロード中や制御されたウォームアップシーンでシェーダーやパイプラインを作成)
- ストリーミング/解凍/アップロードを複数フレームに分散する
- 1フレームあたりの作業に上限を設ける(例:「今フレームはストリーミングに最大2 ms」)
また、スパイクをログ化して再現できるようにすることが重要です。
ビジュアル品質、パフォーマンス、複雑さの間でどう決めればよいですか?
数値とユーザー影響でトレードオフを明示してください。
例:
- 「これによりシャドウの滑らかさが向上するが、GPU 0.4 msとVRAM 80 MBを追加する」
合意された閾値に照らして決めます:
- 参照機での最大フレームタイム
- 許容される最悪スパイク
- プラットフォームごとのメモリ上限
不確かなら可逆的な選択(フィーチャーフラグやスケーラブル設定)を優先してください。
なぜ正しさ(correctness)がパフォーマンス作業で重要なのですか?
挙動が安定していないと計測結果は信用できません。
実践的な手順:
- 不変条件(invariants)を定義する(例:「各可視オブジェクトは一度だけサブミットされる」)
- デバッグビルドで検証を入れる:アサーションや軽量チェックで壊れた状態を早期検出する
- 決定論的な再現ハーネスを構築する(最小シーン、スクリプト化された入力、固定カメラパス)
再現できるようにすれば、パフォーマンスの問題をノイズではなく原因に基づいて直せます。
「マシンと協調する(データ・キャッシュ・オーバーヘッド)」とは実際にどういうことですか?
“速いコード”の多くはメモリやオーバーヘッドに関する仕事です。
注力点:
- データローカリティ:ホットデータを連続して配置し、キャッシュミスを減らす
- アロケーション制御:バッファ再利用、オブジェクトプール、フレームごとの雑多な確保を避ける
- バッチ処理:ドローコールや状態変更、同期ポイントを減らしてオーバーヘッドを削減する
しばしば、オーバーヘッドを削ることがインナーループの微調整より大きな効果を生みます。
プロジェクトが進む中でパフォーマンス回帰をどう防げばよいですか?
パフォーマンスを測定可能で再現性のある習慣にしてください。
- 小さなベースラインシーンセットを決める(GPU重視、CPU重視、ストレスシーン)
- 固定ハードウェア/設定で実行し、コミットハッシュやビルド構成と一緒に結果を保存する
- p50/p95/p99フレームタイム、ピークメモリ、ロード時間を追跡する
- 閾値を決める(例:p95が5%以上悪化してはいけない)
回帰が出たらバイセクトし、担当を決め、リリースを阻害するなら素早くリバートしてください。