「Hearts of Iron IV」新DLC「Arms Against Tyranny」発売は10月10日!

「Victoria 3」開発日記#76――パフォーマンス

Vic3 開発日記

「Victoria 3」開発日記#76が公開されていましたので、その内容をご紹介。今回はパフォーマンスについて。1.2リリース前の開発日記です。

前回:開発日記#75――外交の改善


スポンサーリンク

開発日記

開発日記#76は、パフォーマンスについて。

パフォーマンスとはなにか

1.1が遅いと思うなら、リリースの1年前のゲームを見るべきだ。

  • 多くのゲームでは、グラフィック設定をあまり下げずにどれだけ高いfpsを出せるかというものであることがほとんどだ。しかしパラド社開発スタジオで作っているようなシミュレーションの重いゲームでは別の要素、すなわちティック速度が重要になる。この指標はfpsほどゲーム業界内で広く使われている名前わけではなく、他のゲームでは1秒あたりのティック数(Ticks Per Second)や1秒あたりの更新数(Updates Per Second)という名称で知られているかもしれない。ここではその逆数の指標、つまり1ティックが完了するまでにかかる平均時間を秒またはミリ秒で表したものを使っている。グラフはデバッグビルドのものとリリースビルドのものがあるため、数値は常に直接比較できるものではない可能性がある。
  • 1ティックが意味するものはゲーム内の時間で変わる。CK3やEU4では1日、HoI4では1時間、Vic3では1日の1/4の6時間だ。すべてのティックが同じというわけではなく、一部の処理は他のものと同じ頻度で行う必要はないため、私たちはティックをカテゴリーに分けている。Vic3では年・月・日・(通常の)ティックだ。

ティックの内容

ゲーム内の一部のティックタスクの概要。コンソールコマンド「TickTask.Graph」で見ることができる。

  • 「Victoria 3」は非常にシミュレーション中心のゲームであり、ティックごとに多くのことを行わなければならない。コードを整理された状態に保つために、私たちはティックをティックタスクというものに細分化している。ティックタスクはゲーム状態に対して実行する操作の明確に区別できるまとまりで、実行頻度や、実行を許可する前にそのタスクが依存する他のティックタスクについての情報も含む。

2月15日時点の毎晩のテストでもっとも高価なティックタスク上位10個。数値はデバッグビルドを使用して複数回実行した際の平均秒数。

  • ティックタスクの多くは1つか数個の値を更新するだけの小さなものだが、中には非常に大規模なものもある。こうしたタスクを実行する頻度やどのゲームオブジェクトで動作するかによって、ゲーム速度に与える影響はさまざまだ。ゲーム内で特に高くつくタスクとしては雇用の更新があり、これにPopニーズのキャッシュの更新や補正の更新が続く。
  • 上のグラフでわかるように、もっとも高価なタスクの多くは週次で実行されている。これにさらに日次ティックと通常のティックのタスクも相まって、通常はかなり長い時間がかかることを意味する。

リリースビルドでの1890年前後の週次ティックに関するOptickのキャプチャー画像。

  • 週次ティックで起こっていることを掘り下げるためにはプロファイラーを使用できる。Optickはパラド社開発スタジオで使用しているもののひとつで、主にゲーム開発をターゲットにしたオープンソースのプロファイラーだ。
  • 左側にはスレッドの名称が表示されている。Network/Session threadはゲームロジックのメインスレッドで、シミュレーションを実行し、プレイヤーのコマンドに対応するためのものだ。次にprimary task threadsだが、この数はエンジンがCPUのコア数に応じてスレッドを作成するため、マシンによって変わる。ここでは見やすくするために8個に制限している。タスクスレッドは並列化できる処理を行う。Main Threadはゲーム起動時にOSが生成する最初のスレッドで、インターフェースやグラフィックの更新を処理する。Render Threadは実際のレンダリングを行う。最後のsecondary task threadsはprimaryと似ているが、一般にグラフィックの更新を支援したり、ゲームを保存したりといった、ゲームロジック以外のことを行う。
  • テキストが入った色つきのボックスはプロファイラーに表示するに足るほど興味深いと私たちがみなしたコードの部分だ。より深く知りたい場合はSuperluminalやVTuneのような別のプロファイラーを使うと関数レベルやアセンブリまで直接見ることができる。ピンクのバーはスレッドがなにかを待っていることを示す。task threadsについて言えば、これは通常さらなる処理を待っていることを意味し、Session threadについて言えば、通常ならインターフェースやグラフィックの更新が必要なためにゲーム状態の変更を止められていることを意味する。

UpdateEmploymentティックタスクを拡大したもの。

  • ティック速度を見るとき、私たちは主にSession threadとprimary task threadsに一番注目する。ここではSession threadを展開して週次ティックで起こっていることを確認できるようにしており、目につくことがいくつかある。まず、よく発生する赤いCScopedGameStateReleaseブロックは、できるだけ60fpsに近い速度でレンダリングするために、インターフェースとグラフィックに必要なデータを読み込ませるにあたって更新を中断する必要があるときのものだ。これはどこでも起こるわけではなく、ティックタスクの間かティックタスクの特定のステップの間に限られている。これはデータの一貫性を保証するためで、例えば国家の予算の半分だけが更新されたときにインターフェースがデータを取得したりはしない。

補正は遅い

RecalculateModifierNodesティックタスクを拡大したもの。

  • パラド社開発スタジオゲームに共通するコンセプトとして補正があるが、「Victoria 3」はCK3と比較して補正の設定が桁違いに複雑だ。これを管理するために「Stellaris」と同様の補正ノードというシステムを使っている。これは要するに依存関係管理システムで、これによって私たちは補正にdirtyフラグを立てることができ、これとこれに依存関係にある他の補正のみを再計算できる。補正の再計算はやや高価なので、これはかなり有益だ。
  • しかしこのシステムはこれまで非常にシングルスレッド化されたもので、ティックの大部分は補正の更新に費やされていた。この開発日記の一番上のグラフを見ると2022年前半にパフォーマンスが急速に向上したことがわかるが、主に貢献したのは補正ノードの計算を並列化したことだ。どのノードがどのノードに依存しているかがわかっているので、ノードを各バッチがその前のバッチにのみ依存するようなバッチに分割できる。
スポンサーリンク

国家にはさまざまな大きさがある

WeeklyCountryBudgetUpdateParallelティックタスクを拡大したもの。高くつくものと手ごろなものに注目してほしい。

  • ティックで行われる処理の多くは世界のすべての国家に対して行う必要がある。しかしルクセンブルクのような小さな国家とロシアのような大きな国家では規模が大きく異なるため、一部の処理で100倍以上の差が生まれることもある。すべての処理は必要であり、どれを先に処理しても問題ないため、連続的に処理する場合は問題にならないが、並列処理を始めるとあまりに多くの大国が同じスレッドに含まれてしまうという問題が起こり得る。つまり、すべてのスレッドが処理を終えた後、最後のスレッドが終了するのを待たなければならないということだ。
  • これを回避するため、私たちはティックタスクが更新の各部分にヒューリスティックコストを明記できるシステムを考えた。これによって推定計算時間の標準偏差をチェックして突出したものを特定し、個別にスケジュールを組むことができるようになった。これが大きな違いとして表れるのは、国家の予算の更新だ。例えば中国・ロシア・イギリスを同じスレッドで更新させないことで、予算更新に必要な時間が大幅に短縮される。世界征服プレイでゲームの動作が遅くなるのもこのためだ。

1.2の改善

  • これについては多くの方が興味を持っていることと思うが、大小さまざまな改善が行われている。

新たな建設キューのインターフェース。

  • 建設キューは最終的に非常に大きくなるが、以前のインターフェースではウィジェットタイプのものを使用しており、適切にレイアウトするために、画面上にないものを含めたすべての要素のサイズを計算する必要があった。

新旧の建設キューの比較。(注:元はGIF画像)

  • この問題をさらに深刻にしたのは、キューに入った施設が完成までの時間などを計算するために互いに多くの依存関係を持っていたことだ。この問題も解決し、今日のベータ版で利用可能になっているはずだ。

最適化前/あとのグラフィックの更新。

  • ティック速度の大きな改善はグラフィックの更新を改善した結果だ。ゲーム終盤ではマップの更新に時間がかかり、その結果としてゲームロジックがグラフィックの更新を長く待ち続けることにつながっていた。エンジンの改良とゲーム側のコードの変更があったことで、グラフィックの更新に必要な時間は短縮されたが、これにはマップ名更新のスレッド化の改善、空に浮かぶものの更新の最適化、都市グラフィックにおいてどこに施設が表示されるかを明らかにするのに必要な処理の低減が含まれる。
  • 上で述べたように、雇用の更新はパフォーマンスに大きな影響がある。これはゲーム内のPop数(総人口ではなく)と非常に強い相関がある。特にゲーム終盤では大量の小さなPopが存在し、雇用の更新が極端に遅くなることがある。これを解決するためにゲームの設計を調整して小さなPopを積極的にまとめるようにし、ゲーム終盤のパフォーマンスは改善されたはずだ。Mod制作者はdefinesのPOP_MERGE_MAX_WORKFORCEとPOP_MERGE_MIN_NUM_POPS_SAME_PROFESSIONでこれを変更できる。
  • 1.2におけるもうひとつの改善点はClausewitz(注:ゲームエンジンのこと)のメモリ割り当て方法を変えたことだ。私たちは常に特殊なケース向けの専用のアロケータ(プールアロケータ、ゲームオブジェクト「データベース」など)は用意していたが、それでもOSのデフォルトのアロケータに回すだけの割り当てが多かった。これは特にWindowsでは遅くなることがある。
  • これを解決するため、私たちはmimallocというライブラリを使うようにした。これは非常にパフォーマンスのよいメモリアロケータライブラリで、基本的にはOSが提供する機能の当座の代替だ。Unreal Engineのような大規模なエンジンでは既に使用されているもので、上記の2つほど重要ではないものの、ゲームの時間軸の2/3ほどで1年間計測すると約4%高速化されている。これはエンジンの改良であるため、今後CK3でもこのようになる可能性がある。

1.1と1.2のティックタイムを年ごとに比較したもので、1.2のほうがずっと早い。数値はデバッグビルドを使用して数週間にわたって毎晩行ったテストの年平均値。

  • 他に多くの小さな改善もあり、上のグラフでわかるように、全体的に1.2は1.1より明らかに早くなっているはずだ。1.1のオーバーナイトテストは1.2ほど安定しておらず、わかりやすくするためにグラフを1871年で切っているが、一般的に1.2のパフォーマンス向上はゲーム終盤でより顕著だ。

来週は戦争の改善と新たな戦略目標機能について。

次回:開発日記#77――軍事に関する改善

スポンサーリンク

コメント

  1. こういう整合性が大事なプログラムは難しいわな。
    マルチスレッドにするだけなら誰にでも出来るけど、何をどのタイミングでjoinするかが問題やわな。結局殆どのスレッドがawaitしてるだけとかもザラやろうし。

  2. PC性能に余裕がないからありがたい

  3. 中盤から重くてやってらんなかったから助かる

  4. ベータやってみたらマジで早くなっててビックリした。
    終盤になってもヌルヌル動いててストレスフリーだわ。

  5. 1.2ベータプレイしてるけど、初めてストレスなく1936年までプレイできた。1.0に比べて相当軽くなってる。

    • 参考のためにCPUの性能がどのくらいか教えてくれると嬉しい

      • CPUをコピペしようとするとコメントが反映されないかも、スパム対策のせいか?
        Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz 3.70 GHz

        • スパム対策に日本語を含まないコメントは投稿できないようにしているので、これに引っかかっていたようです。

        • 上のコメントとは違う人間だけど8700kでも快適に動くのか
          丁度スウカゲツマエに8700kからVic3の為だけにi5 12400fに変えたから泣きそう…

  6. 最適化のためにいろいろ工夫しているんだな。これだけ複雑なゲームだとデータから原因を突き止めるのも大変そうだ

タイトルとURLをコピーしました