평소 CPU코딩에만 익숙해져있다면, 다음과 같은 코드를 보고 굉장히 비효율적이라고 생각할 수 있습니다.
[unroll] // warning X3557: loop only executes for 1 iteration(s), forcing loop to unroll
for (i = 0; i < NUM_DIR_LIGHTS; ++i)
{
color += ComputeDirectionalLight(lights[i], material, input.normalWorld, toEye);
}
[unroll]
for (i = NUM_DIR_LIGHTS; i < NUM_DIR_LIGHTS + NUM_POINT_LIGHTS; ++i)
{
color += ComputePointLight(lights[i], material, input.posWorld, input.normalWorld, toEye);
}
[unroll]
for (i = NUM_DIR_LIGHTS + NUM_POINT_LIGHTS; i < NUM_DIR_LIGHTS + NUM_POINT_LIGHTS + NUM_SPOT_LIGHTS; ++i)
{
color += ComputeSpotLight(lights[i], material, input.posWorld, input.normalWorld, toEye);
}
hlsl 픽셀세이더에서는 GPU병렬처리를 이용하기 때문에 if-else문을 사용한다고 해서 조건에 참인 값만 계산되는 것이 아닙니다. 비트 마스킹을 이용하기 때문이죠.
예를들어 이 코드는
float4 color = // some color;
if (someValue > 0.5)
color = ExpensiveCalculation(); // 비싼 계산
실제로 이 코드로 실행이 됩니다.
float4 color = // some color;
float4 temp = ExpensiveCalculation(); // 조건에 관계없이 항상 계산됨
color = (someValue > 0.5) * temp + (1 - (someValue > 0.5)) * color;
조건에 상관없이 실행이 되는 모습이죠.
하지만 병렬처리를 사용한다면, if-else문에서의 분기로 인해 성능 저하가 발생할 수 있습니다. 그렇다면 맨 위의 코드에서 반복문은 분기가 발생할까요? 그렇지 않습니다. 왜냐하면 반복문의 형태를 띄고있는 코드일 뿐이지, 사실 반복적으로 코드를 나열한 것과 같기 때문이죠.
[unroll]
for (int i = 0; i < 4; i++)
foo += bar[i];
foo += bar[0];
foo += bar[1];
foo += bar[2];
foo += bar[3];
이렇게 그냥 코드 나열입니다. 이러한 방식으로 불필요한 인덱스 계산과 반복문이 한번 돌 때마다 조건을 체크할 필요가 없어집니다.
하지만 [unroll] 사용 시 반복문의 횟수를 정확히 명시해줘야합니다. 왜냐하면 [unroll]에서 반복 횟수를 명시하지 않으면, 컴파일러는 가능한 최적의 반복 횟수를 추측하려고 시도하기 때문에 불필요하게 반복횟수를 많이 계산할 수 있기 때문입니다.
더 자세하게 내용을 이해하고 싶다면 다음 Unity Forum을 참고해주세요
https://discussions.unity.com/t/what-are-unroll-and-loop-when-to-use-them/881945/3
'문법 및 메모리 관련' 카테고리의 다른 글
The Difference Between Inheritance and Overriding – Static Binding vs Dynamic Binding (0) | 2025.05.21 |
---|---|
[C++ 자료구조] 벡터의 동적할당 시 메모리 확장 방식 (2) | 2024.12.21 |
[메모리 관리] -고정형or 가변형 벡터의 힙과 스택 메모리 할당 (1) | 2024.10.22 |
C++과 C# 반복문에서 배열 반복 사용 (0) | 2024.10.21 |
[SIMD] - 데이터 병렬처리 (4) | 2024.10.06 |