はじめに
Unityで2Dアクションゲームを作っていて、ビルドしたものでテストしていた時に、特定の行動だけ初回実行時に必ず実行速度の低下が目に見えてわかるレベルのスパイクが起こっていました。
その処理の内容としては、プレイヤーキャラクターがアイテムに衝突したら
- 効果音を鳴らして
- それぞれのアイテムの獲得処理(残機や体力、コイン枚数を増やす等)を行う
といったもので、特にどちらも重い処理ではありませんでした。
実装を見つめる
まずは自分自身で記述したコード自体を精査してみたのですが、前述のとおり逆に重くする方が難しいような単純な処理しかしていないため、エディター上でのプロファイラー等の情報とも併せてこれ自体が原因ではないという考えに至りました。
そこで、次はUnity内部の部分である「効果音を鳴らす」処理について、ロード処理のタイミングでDSPに効果音データを読み込ませたりしてみたのですが、まったくと言っていいほど改善の兆しがなかったので音声システムも原因ではないという考えに至りました。
しばらくして、設定次第ではビルドしたUnityPlayerでもプロファイラーが使えることを思い出したため、試してみたところ、今回のスパイクの部分には必ず「Mono.JIT」が存在していました。
推測される原因
その処理の部分的なコードのILからのJITコンパイルに時間がかかっていて、初回実行時にだけスパイクを引き起こしていたと考えられます。
仮にUnityPlayerのMonoのJITが決定論的にコンパイルするのであればすべての辻褄が合います。
対策
- 要旨
- JITにやさしいコードを書く(非現実的)
- Scripting BackendをIL2CPPに変更する(今回の結論)
原因が分かれば、あとは時間がかかる処理を時間がかからない処理にするだけですが、今回はJITコンパイラに原因があるため、この論法でいくとJITコンパイラに寄ったコードを書くことが直接的な対策となるのですが、これはなかなか非効率かつ非現実的です。
なので、そもそもJITコンパイラを使わなくてもいい方法であるIL2CPPビルドを行いました。
すると、同じコードでも当該箇所ではスパイクを起こさなくなりました。
結論
- Monoバックエンドはビルドが早くて色々便利だけれども、たまに牙をむいてくる
- ある処理が毎回ではなく初回のみスパイクを起こす場合はバックエンドをIL2CPPにすることを試してみるとよいかもしれない
- やはり事前コンパイルはつよい