Incremental GC
Time Slicing, gc.TimeLimitSeconds, ์ ์ง์ ์์ง ์ ๋ต์ผ๋ก GC ํ์น๋ฅผ ์ต์ํํ๋ ๊ธฐ๋ฒ์ ๋ถ์ํฉ๋๋ค
Incremental GC ๊ฐ์
์ ์ง์ ๊ฐ๋น์ง ์ปฌ๋ ์ ์ ํ์์ฑ๊ณผ ๊ธฐ๋ณธ ์๋ฆฌ
์ Incremental GC์ธ๊ฐ?
๊ธฐ๋ณธ(Full) GC๋ ํ ํ๋ ์์์ ์ ์ฒด Mark-Sweep์ ์๋ฃํฉ๋๋ค. ๋๊ท๋ชจ ํ๋ก์ ํธ์์ ์์ญ๋ง ๊ฐ์ UObject๊ฐ ์์ผ๋ฉด ์์ญ ms์ ํ์น๊ฐ ๋ฐ์ํฉ๋๋ค. Incremental GC๋ ์ด ์์ ์ ์ฌ๋ฌ ํ๋ ์์ ๋ถ์ฐํฉ๋๋ค.
// Full GC (๊ธฐ๋ณธ)
Frame N: [Game Logic][====== Full GC (30ms) ======][Render]
// ๊ฒฐ๊ณผ: 30ms ํ์น โ ํ๋ ์ ๋๋กญ
// Incremental GC
Frame N: [Game Logic][GC 2ms][Render]
Frame N+1: [Game Logic][GC 2ms][Render]
Frame N+2: [Game Logic][GC 2ms][Render]
...
Frame N+14:[Game Logic][GC 2ms][Render]
// ๊ฒฐ๊ณผ: ๊ฐ ํ๋ ์ 2ms โ ํ์น ์์
Incremental GC๋ gc.AllowIncrementalReachability=1๋ก ํ์ฑํ๋๋ฉฐ UE5์์๋ ๊ธฐ๋ณธ ํ์ฑํ์
๋๋ค. gc.TimeLimitSeconds๋ก ํ๋ ์๋น ์ต๋ GC ์๊ฐ์ ์ค์ ํฉ๋๋ค.
Time Slicing ๋ฉ์ปค๋์ฆ
ํ๋ ์ ์๊ฐ ์์ฐ ๋ด์์ GC ์์ ์ ๋ถ๋ฐฐํ๋ ๋ฐฉ๋ฒ
gc.TimeLimitSeconds
ํต์ฌ ์ฝ์ ๋ณ์์ธ gc.TimeLimitSeconds๋ ํ ํ๋ ์์์ GC๊ฐ ์ฌ์ฉํ ์ ์๋ ์ต๋ ์๊ฐ(์ด)์ ์ค์ ํฉ๋๋ค. ์ด ์๊ฐ์ด ์ด๊ณผ๋๋ฉด ํ์ฌ ์ํ๋ฅผ ์ ์ฅํ๊ณ ๋ค์ ํ๋ ์์์ ์ด์ด์ ์์
ํฉ๋๋ค.
// ์ฃผ์ GC ํ์ด๋ฐ ์ฝ์ ๋ณ์
gc.TimeLimitSeconds = 0.002 // ํ๋ ์๋น GC ์๊ฐ ์ ํ (2ms)
gc.TimeBetweenPurgingPendingKillObjects = 60.0 // GC ๊ฐ๊ฒฉ (์ด)
gc.AllowIncrementalReachability = 1 // Incremental ํ์ฑํ
gc.IncrementalBeginDestroyEnabled = 1 // BeginDestroy๋ ์ ์ง์
// Incremental Reachability ๋ด๋ถ (๊ฐ๋ตํ)
bool PerformIncrementalReachabilityAnalysis(float TimeLimit)
{
double StartTime = FPlatformTime::Seconds();
while (ObjectsToProcess.Num() > 0)
{
UObject* Current = ObjectsToProcess.Pop();
ProcessObjectReferences(Current);
// ์๊ฐ ์ ํ ์ฒดํฌ
if (FPlatformTime::Seconds() - StartTime > TimeLimit)
{
// ํ์ฌ ์ํ ์ ์ฅ, ๋ค์ ํ๋ ์์์ ๊ณ์
return false; // ๋ฏธ์๋ฃ
}
}
return true; // ์๋ฃ
}
๊ถ์ฅ TimeLimitSeconds ๊ฐ
| ํ๋ซํผ | ๋ชฉํ FPS | ๊ถ์ฅ ๊ฐ | ํ๋ ์ ์์ฐ ๋น์จ |
|---|---|---|---|
| PC (60fps) | 60 FPS | 0.002 (2ms) | ~12% |
| PC (120fps) | 120 FPS | 0.001 (1ms) | ~12% |
| ์ฝ์ (30fps) | 30 FPS | 0.004 (4ms) | ~12% |
| ๋ชจ๋ฐ์ผ | 30-60 FPS | 0.003 (3ms) | ~10-18% |
์ฐ๊ธฐ ๋ฐฐ๋ฆฌ์ด์ ์ฐธ์กฐ ์์ ์ฑ
Incremental GC ์ค ์๋ก์ด ์ฐธ์กฐ๊ฐ ์๊ธธ ๋์ ์์ ๋ณด์ฅ
TObjectPtr ์ฐ๊ธฐ ๋ฐฐ๋ฆฌ์ด
Incremental GC์ ํต์ฌ ๊ณผ์ ๋ Mark ์งํ ์ค ์๋ก์ด ์ฐธ์กฐ๊ฐ ์๊ธฐ๋ ๊ฒฝ์ฐ์
๋๋ค. ์ด๋ฏธ ๊ฒ์ฌํ ๊ฐ์ฒด๊ฐ ์๋ก Unreachable ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋ฉด, ํด๋น ๊ฐ์ฒด๊ฐ ์๋ชป ์์ง๋ ์ ์์ต๋๋ค. UE5์ TObjectPtr๋ ์ฐ๊ธฐ ๋ฐฐ๋ฆฌ์ด๋ก ์ด๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
// TObjectPtr์ ์ฐ๊ธฐ ๋ฐฐ๋ฆฌ์ด (๊ฐ๋ตํ)
template<typename T>
class TObjectPtr
{
FObjectPtr ObjectPtr;
void operator=(T* NewValue)
{
if (IsGarbageCollecting())
{
// GC ์งํ ์ค์ด๋ฉด ์ ์ฐธ์กฐ ๋์์ Reachable๋ก ๋งํน
if (NewValue)
{
FUObjectItem* Item = GUObjectArray.ObjectToItem(NewValue);
if (Item->HasAnyFlags(EInternalObjectFlags::Unreachable))
{
Item->ClearFlags(EInternalObjectFlags::Unreachable);
// ์ฌ์ค์บ ํ์ ์ถ๊ฐ
GGCReferenceProcessor.MarkForRescan(NewValue);
}
}
}
ObjectPtr = NewValue;
}
};
Incremental GC๊ฐ ํ์ฑํ๋ ์ํ์์ raw UObject*๋ฅผ ์ฌ์ฉํ๋ฉด ์ฐ๊ธฐ ๋ฐฐ๋ฆฌ์ด๊ฐ ๋์ํ์ง ์์ต๋๋ค. ์ด๋ GC๊ฐ ์งํ ์ค์ผ ๋ ์๋ก ์ฐธ์กฐ๋ฅผ ์ค์ ํด๋ ๋์ ๊ฐ์ฒด๊ฐ Unreachable๋ก ๋จ์ ์๋ชป ์์ง๋ ์ ์์์ ์๋ฏธํฉ๋๋ค. UE5์์ TObjectPtr ์ฌ์ฉ์ด ๊ฐ๋ ฅํ ๊ถ์ฅ๋๋ ์ด์ ์
๋๋ค.
Incremental GC ์ค์ ๊ณผ ๋ชจ๋ํฐ๋ง
ํ๋ก์ ํธ ์๊ตฌ์ฌํญ์ ๋ง๋ Incremental GC ํ๋
์ฃผ์ ์ค์ ๋ณ์
; Incremental GC ๊ด๋ จ ์ค์
[/Script/Engine.GarbageCollectionSettings]
gc.TimeLimitSeconds=0.002
gc.AllowIncrementalReachability=True
gc.IncrementalBeginDestroyEnabled=True
gc.CreateGCClusters=True
gc.TimeBetweenPurgingPendingKillObjects=60.0
gc.MaxObjectsNotConsideredByGC=0
gc.MaxObjectsInGame=2162688
gc.MaxObjectsInEditor=12582912
gc.NumRetriesBeforeForcingGC=10
; ๋ฐํ์์์ ๋ณ๊ฒฝ
; ์ฝ์: gc.TimeLimitSeconds 0.003
Incremental GC ๋ชจ๋ํฐ๋ง
// GC ํต๊ณ ํ์
stat gc
// ์ถ๋ ฅ ์์:
GC Total Time: 2.1 ms โ ์ด๋ฒ ํ๋ ์ GC ์๊ฐ
Mark Time: 1.5 ms โ Mark ๋จ๊ณ ์์
Sweep Time: 0.6 ms โ Sweep ๋จ๊ณ ์์
Objects Destroyed: 42 โ ํ๊ดด๋ ๊ฐ์ฒด ์
Incremental: Yes โ Incremental ๋ชจ๋ ์ฌ๋ถ
Remaining Objects: 1523 โ ์์ง ์ฒ๋ฆฌ ์ ๋ ๊ฐ์ฒด
๊ฐ์ฒด ์์ฑ ์๋๊ฐ GC ์ฒ๋ฆฌ ์๋๋ฅผ ์ด๊ณผํ๋ฉด Incremental GC๊ฐ ์์ํ ์๋ฃ๋์ง ์์ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ ์์ง์ gc.NumRetriesBeforeForcingGC ํ์๋งํผ ์๋ ํ ๊ฐ์ Full GC๋ฅผ ์คํํฉ๋๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๋ ค๋ฉด ๊ฐ์ฒด ์์ฑ ์๋๋ฅผ ์ ์ดํ๊ฑฐ๋ Object Pooling์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
ํต์ฌ ์์ฝ
- Incremental GC๋ Mark-Sweep ์์ ์ ์ฌ๋ฌ ํ๋ ์์ ๋ถ์ฐํ์ฌ ํ์น๋ฅผ ๋ฐฉ์งํฉ๋๋ค
- gc.TimeLimitSeconds๋ก ํ๋ ์๋น GC ์๊ฐ ์์ฐ์ ์ค์ ํ๋ฉฐ, ์ผ๋ฐ์ ์ผ๋ก 2ms๊ฐ ๊ถ์ฅ๋ฉ๋๋ค
- TObjectPtr์ ์ฐ๊ธฐ ๋ฐฐ๋ฆฌ์ด๊ฐ Incremental GC ์ค ์ ์ฐธ์กฐ์ ์์ ์ฑ์ ๋ณด์ฅํฉ๋๋ค
- raw
UObject*๋ ์ฐ๊ธฐ ๋ฐฐ๋ฆฌ์ด๊ฐ ์์ผ๋ฏ๋ก Incremental GC์์ ์ํํฉ๋๋ค - ๊ฐ์ฒด ์์ฑ์ด ๊ณผ๋ํ๋ฉด ๊ฐ์ Full GC๊ฐ ๋ฐ์ํ๋ฏ๋ก Object Pooling์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค
๋์ ๊ณผ์
๋ฐฐ์ด ๋ด์ฉ์ ์ง์ ์ค์ตํด๋ณด์ธ์
์ฝ์์์ gc.TimeLimitSeconds๋ฅผ 0.001, 0.005, 0.01๋ก ๋ณ๊ฒฝํ๋ฉด์ GC๊ฐ ํ ํ๋ ์์์ ์ฒ๋ฆฌํ๋ ์์ ๋ ๋ณํ๋ฅผ stat gc๋ก ๊ด์ฐฐํ์ธ์. ๊ฐ ์ค์ ์์ GC๊ฐ ์๋ฃ๋๊ธฐ๊น์ง ๊ฑธ๋ฆฌ๋ ์ด ํ๋ ์ ์๋ฅผ ๊ธฐ๋กํ์ธ์.
stat unit๊ณผ stat gc๋ฅผ ๋์์ ํ์ฑํํ๊ณ , 10,000๊ฐ์ UObject๋ฅผ ์์ฑ ํ ์ฐธ์กฐ ํด์ ํ์ธ์. gc.IncrementalBeginDestroyEnabled=1๊ณผ =0์์์ ํ๋ ์ ํ์ ๋ณํ๋ฅผ ๋น๊ต ๊ทธ๋ํ๋ก ๊ธฐ๋กํ์ธ์.
๊ฒ์ํ๋ ์ด ์ํฉ(์ ํฌ ์ค, ๋ก๋ฉ ์ค, ์ผ์์ ์ง)์ ๋ฐ๋ผ gc.TimeLimitSeconds๋ฅผ ๋์ ์ผ๋ก ์กฐ์ ํ๋ GC Manager ํด๋์ค๋ฅผ ์ค๊ณํ์ธ์. ์ ํฌ ์ค์๋ ์์ฐ์ ์ค์ด๊ณ , ๋ก๋ฉ ํ๋ฉด์์๋ ForceGarbageCollection์ ํธ์ถํ๋ ์ ๋ต์ ๊ตฌํํ์ธ์.