콘텐츠로 이동

Unity Performance Testing — 자동화 성능 검증

Unity Performance Testing 패키지(com.unity.test-framework.performance)는 Unity Test Framework 위에서 동작하는 성능 벤치마크 도구입니다. 프레임 시간, 메모리, GC 할당을 자동 측정하고 결과를 JSON으로 저장해 CI 파이프라인에서 성능 회귀를 감지할 수 있습니다.


Packages/manifest.json
{
"dependencies": {
"com.unity.test-framework": "1.3.9",
"com.unity.test-framework.performance": "3.0.3"
}
}
Unity.PerformanceTesting
// Assembly Definition — Tests 어셈블리에 추가

using NUnit.Framework;
using Unity.PerformanceTesting;
using UnityEngine;
using UnityEngine.TestTools;
using System.Collections;
public class BasicPerformanceTests
{
// 동기 메서드 측정
[Test, Performance]
public void MathOperations_Performance()
{
Measure.Method(() =>
{
float result = 0f;
for (int i = 0; i < 10000; i++)
result += Mathf.Sqrt(i);
})
.WarmupCount(5)
.MeasurementCount(20)
.IterationsPerMeasurement(1)
.GC() // GC 할당도 측정
.Run();
}
// 비동기/코루틴 측정
[UnityTest, Performance]
public IEnumerator SceneLoad_Performance()
{
using (Measure.Frames().Scope())
{
yield return SceneManager.LoadSceneAsync("GameScene");
}
// Scope 내 모든 프레임의 시간을 수집
}
}

[Test, Performance]
public void ECS_Query_Performance()
{
// 커스텀 SampleGroup 정의
var queryTime = new SampleGroup(
"ECS Query Time",
SampleUnit.Millisecond,
increaseIsBetter: false);
var entityCount = new SampleGroup(
"Entity Count",
SampleUnit.None,
increaseIsBetter: true);
Measure.Method(() =>
{
var sw = System.Diagnostics.Stopwatch.StartNew();
int count = RunECSQuery();
sw.Stop();
Measure.Custom(queryTime,
sw.Elapsed.TotalMilliseconds);
Measure.Custom(entityCount, count);
})
.WarmupCount(3)
.MeasurementCount(15)
.Run();
}

[UnityTest, Performance]
public IEnumerator GameplayLoop_FrameTime()
{
// 씬 로드
yield return SceneManager.LoadSceneAsync("BattleScene");
// 설정 단계 (측정 제외)
SpawnEnemies(100);
yield return new WaitForSeconds(1f);
// 60프레임 측정
yield return Measure.Frames()
.WarmupCount(10)
.MeasurementCount(60)
.SampleGroup(new SampleGroup(
"Frame Time",
SampleUnit.Millisecond))
.Run();
}
// 특정 구간만 측정
[UnityTest, Performance]
public IEnumerator ExplosionEffect_Performance()
{
yield return SceneManager.LoadSceneAsync("TestScene");
using (Measure.Frames()
.SampleGroup("Explosion Frame Time")
.Scope())
{
TriggerExplosion();
yield return new WaitForSeconds(2f);
}
}

[Test, Performance]
public void ObjectPool_Memory()
{
// 메모리 할당량 측정
Measure.Method(() =>
{
var pool = new List<GameObject>();
for (int i = 0; i < 100; i++)
pool.Add(new GameObject());
foreach (var go in pool)
Object.DestroyImmediate(go);
})
.GC() // GC.Collect() 호출 및 할당 추적
.MeasurementCount(10)
.Run();
// SampleGroup으로 메모리 직접 샘플링
var memGroup = new SampleGroup(
"Total Memory MB",
SampleUnit.Megabyte);
Measure.Custom(memGroup,
GC.GetTotalMemory(false) / (1024f * 1024f));
}

Terminal window
# 테스트 실행 및 JSON 결과 저장
Unity -batchmode -projectPath . \
-runTests \
-testPlatform EditMode \
-testResults results.xml \
-performanceTestResults perf_results.json
# 결과 파일 위치
# Assets/StreamingAssets/PerformanceTest*.json
// 결과 JSON 구조
// {
// "results": [{
// "name": "MathOperations_Performance",
// "sampleGroups": [{
// "name": "Time",
// "unit": "Millisecond",
// "samples": [0.12, 0.11, 0.13, ...],
// "median": 0.12,
// "average": 0.12,
// "standardDeviation": 0.005
// }]
// }]
// }

// 임계값 기반 회귀 감지
[Test, Performance]
public void Pathfinding_Performance_Threshold()
{
float totalTime = 0f;
const int runs = 50;
Measure.Method(() =>
{
var sw = System.Diagnostics.Stopwatch.StartNew();
RunPathfinding();
totalTime += (float)sw.Elapsed.TotalMilliseconds;
})
.MeasurementCount(runs)
.Run();
float avg = totalTime / runs;
// 5ms 초과 시 실패
Assert.Less(avg, 5f,
$"경로 탐색 평균 시간이 임계값 초과: {avg:F2}ms");
}

Burst Job은 에디터에서 Burst가 비활성화된 상태로 측정될 수 있습니다. 정확한 성능은 플레이어 빌드에서 측정하거나, BurstCompiler.Options.EnableBurstCompilation을 확인하세요.

[Test, Performance]
public void BurstJob_vs_Managed_Compare()
{
const int count = 100_000;
using var data = new NativeArray<float>(count, Allocator.TempJob);
using var result = new NativeArray<float>(1, Allocator.TempJob);
// Burst Job 측정
var burstGroup = new SampleGroup("BurstSum", SampleUnit.Microsecond);
Measure.Method(() =>
{
new SumJob { Values = data, Result = result }
.Schedule().Complete();
})
.SampleGroup(burstGroup)
.MeasurementCount(20)
.Run();
// 관리형 코드 측정
var managedGroup = new SampleGroup("ManagedSum", SampleUnit.Microsecond);
Measure.Method(() =>
{
float sum = 0f;
for (int i = 0; i < count; i++) sum += data[i];
result[0] = sum;
})
.SampleGroup(managedGroup)
.MeasurementCount(20)
.Run();
}
// 플랫폼에 따라 임계값을 다르게 설정
[Test, Performance]
public void FrameTime_PlatformBudget()
{
float budget = Application.isMobilePlatform ? 33.3f : 16.7f; // ms
yield return Measure.Frames()
.MeasurementCount(60)
.Run();
// 결과는 JSON에 저장 — CI에서 budget과 비교
Assert.Less(
PerformanceTest.Active.SampleGroups
.Find(g => g.Name == "Frame Time")?.Median ?? 0f,
budget,
$"프레임 예산 초과 (목표: {budget}ms)");
}

Unity Performance Testing은 Measure.Method로 CPU 코드를, Measure.Frames로 렌더링 성능을 측정하세요. .GC()를 항상 포함해 GC 할당 제로화(zero-alloc) 목표를 수치로 검증하고, JSON 결과를 CI에서 이전 빌드와 비교해 성능 회귀를 자동으로 감지하는 파이프라인을 구축하면 게임 최적화 작업이 훨씬 체계적으로 진행됩니다.