はじめに
GCのヒープ領域を確保していることをしてしていて、このヒープ領域がいっぱいになるとC#のGCが発動する
このGCは、メインスレッド上で行われ、他の処理を止めてしまうことでパフォーマンスへ影響を与える
WebGLでは非常に限られたヒープ領域でやりくりするため、このGCによるパフォーマンス低下が顕著に現れる
そこで今回は、Allocateによってどのぐらいパフォーマンスへ影響が出るか計測してみる
実験内容
以下のようなスクリプトを用意し、フレームあたりどのぐらいAllocateするかをUIから設定できるようにする
FpsMesure.cs
using TMPro;
using UnityEngine;
public class FpsMesurere : MonoBehaviour
{
[SerializeField] private TMP_Text fps1FrameText;
[SerializeField] private TMP_Text fps1SecondText;
[SerializeField] private TMP_Text fps5SecondText;
private int frameCount1Sec;
private int frameCount5Sec;
private float elapsedTime1Sec;
private float elapsedTime5Sec;
private float fps1Sec;
private float fps5Sec;
void Update()
{
var deltaTime = Time.deltaTime;
// 1フレームのFPS
var fps1Frame = 1.0f / deltaTime;
fps1FrameText.text = $"1Frame FPS: {fps1Frame:F1} FPS";
// 1秒平均
frameCount1Sec++;
elapsedTime1Sec += deltaTime;
if (elapsedTime1Sec >= 1.0f)
{
fps1Sec = frameCount1Sec / elapsedTime1Sec;
frameCount1Sec = 0;
elapsedTime1Sec = 0f;
}
fps1SecondText.text = $"1Sec Average FPS: {fps1Sec:F1} FPS";
// 5秒平均
frameCount5Sec++;
elapsedTime5Sec += deltaTime;
if (elapsedTime5Sec >= 5.0f)
{
fps5Sec = frameCount5Sec / elapsedTime5Sec;
frameCount5Sec = 0;
elapsedTime5Sec = 0f;
}
fps5SecondText.text = $"5Sec Average FPS: {fps5Sec:F1} FPS";
}
}
Allocator
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class Allocator : MonoBehaviour
{
[SerializeField] TMP_Text perFrameAllocateNumText;
[SerializeField] Button PerFrame0AllocateBtn;
[SerializeField] Button PerFrame100AllocateBtn;
[SerializeField] Button PerFrame1000AllocateBtn;
[SerializeField] Button PerFrame10000AllocateBtn;
[SerializeField] Button PerFrame30000AllocateBtn;
[SerializeField] Button PerFrame50000AllocateBtn;
int perFrameAllocateNum = 0;
void Start()
{
PerFrame0AllocateBtn.onClick.AddListener(() => SetAllocateNum(0));
PerFrame100AllocateBtn.onClick.AddListener(() => SetAllocateNum(100));
PerFrame1000AllocateBtn.onClick.AddListener(() => SetAllocateNum(1000));
PerFrame10000AllocateBtn.onClick.AddListener(() => SetAllocateNum(10000));
PerFrame30000AllocateBtn.onClick.AddListener(() => SetAllocateNum(30000));
PerFrame50000AllocateBtn.onClick.AddListener(() => SetAllocateNum(50000));
UpdateText();
}
void SetAllocateNum(int num)
{
perFrameAllocateNum = num;
UpdateText();
}
void UpdateText()
{
perFrameAllocateNumText.text = $"Allocate/frame: {perFrameAllocateNum}";
}
void Update()
{
for (int i = 0; i < perFrameAllocateNum; i++)
{
Allocate();
}
}
void Allocate()
{
// 1KBのバイト配列をアロケート(GCに負荷をかける)
byte[] data = new byte[1024];
}
}
これをWebGLでBuild and Runして計測を行う
環境
Unity 6000.3.0.f1
MacBook Pro(Apple M3 Pro, 18GB)
Google Chrome(142.0.7444.176)
Target Frame Time 60FPS
計測結果
Allocate数を変更後、30秒後の5Sec FPSの値を計測結果とした
| Allocate / frame | FPS |
|---|---|
| 0 | 60.0 |
| 100 | 60.0 |
| 1000 | 60.0 |
| 10000 | 42.1 |
| 30000 | 22.7 |
| 50000 | 15.1 |
- 1000Allocate/Frame程度であればパフォーマンスへ影響がない
- 10000Allocate/Frameを超えたところからパフォーマンスへ影響がある
- 50000Allocate/Frameを超えるとほぼプレイ不可
終わりに
new()や、一時配列、ToString()など、よく使うメソッドの中にAllocateが潜んでいる
これからは意識的にAllocate数(量)を減らしていきたい

