오늘은 Unity에서 사용되는 반복문에 속도 비교를 해보려고 합니다.
Unity에서 사용되는 반복문은 아래와 같습니다.
- for 반복문
- 장점
- 가장 빠르고 단순한 반복문입니다.
- 반복 횟수가 명확한 경우에 사용하면 속도가 빠릅니다.
- 단점
- 동적 조건 기반 처리에는 부적합
- 사용 되는 곳
- 반복 횟수가 정해진 작업에 사용됩니다.
- while 반복문
- 장점
- 조건에 따라 반복 횟수를 유동적으로 조절이 가능합니다.
- 단점
- 무한 루프의 위험이 존재합니다.
- 사용 되는 곳
- 조건 기반의 유동적인 작업에 사용됩니다.
- foreach 반복문
- 장점
- 컬렉션을 순회할 때 간결하고 안전하며 반복자를 자동으로 처리합니다.
- 단점
- 반복 중 컬렉션 수정이 불가능하며 내부적으로 반복자를 생성하여 속도가 약간 느립니다.
- 사용 되는 곳
- 배열,리스트,딕셔너리 등 순차적으로 데이터를 처리하는 경우에 사용됩니다.
- do while 반복문
- 장점
- 최소 1회 실행을 보장하며 초기화 작업 후 조건을 확인 가능합니다.
- 단점
- 조건이 실패되도 한 번은 실행되어야 합니다.
- 사용 되는 곳
- 사용자 입력 초기화 후 조건 반복에 사용됩니다.
- Linq
- 장점
- 데이터 필터링,변환, 정렬을 직관적으로 처리하며 가독성이 뛰어납니다.
- 단점
- 성능이 중요한 경우에는 오버헤드가 발생할 우려가 있으며 동적 계산 작업에는 부적합합니다.
- 사용 되는 곳
- 데이터 처리 및 가공 작업에 사용됩니다.
- Task
- 장점
- 비동기적으로 실행되며 병렬 처리가 가능하고 CPU 집약적인 작업을 효율적으로 처리합니다.
- 단점
- 동기적으로 사용할 경우 퍼포먼스 이점이 사라지며 복잡성이 증가합니다.
- 사용 되는 곳
- AI 계산, 네트워크 요청 및 대규모 데이터 처리, 비동기 작업에 사용됩니다.
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
public class LoopPerformanceTest : MonoBehaviour
{
void Start()
{
const int iterations = 10_000_000;
int[] array = new int[iterations];
for (int i = 0; i < iterations; i++) array[i] = i;
Stopwatch stopwatch = new Stopwatch();
// for loop
stopwatch.Start();
for (int i = 0; i < iterations; i++)
{
int temp = array[i] * 2;
}
stopwatch.Stop();
UnityEngine.Debug.Log($"for loop: {stopwatch.ElapsedMilliseconds} ms");
// while loop
stopwatch.Reset();
stopwatch.Start();
int j = 0;
while (j < iterations)
{
int temp = array[j] * 2;
j++;
}
stopwatch.Stop();
UnityEngine.Debug.Log($"while loop: {stopwatch.ElapsedMilliseconds} ms");
// foreach loop
stopwatch.Reset();
stopwatch.Start();
foreach (var value in array)
{
int temp = value * 2;
}
stopwatch.Stop();
UnityEngine.Debug.Log($"foreach loop: {stopwatch.ElapsedMilliseconds} ms");
// LINQ
stopwatch.Reset();
stopwatch.Start();
var linqResult = array.Select(n => n * 2).ToArray();
stopwatch.Stop();
UnityEngine.Debug.Log($"LINQ: {stopwatch.ElapsedMilliseconds} ms");
// Task
stopwatch.Reset();
stopwatch.Start();
Task.Run(() =>
{
for (int i = 0; i < iterations; i++)
{
int temp = array[i] * 2;
}
}).Wait();
stopwatch.Stop();
UnityEngine.Debug.Log($"Task: {stopwatch.ElapsedMilliseconds} ms");
// do-while loop
stopwatch.Reset();
stopwatch.Start();
int k = 0;
do
{
int temp = array[k] * 2;
k++;
} while (k < iterations);
stopwatch.Stop();
UnityEngine.Debug.Log($"do-while loop: {stopwatch.ElapsedMilliseconds} ms");
}
}

- 10,000,000 반복 시 테스트 결과
- 단순 곱셉이여서 차이가 별로 없는 듯합니다.
- Linq의 경우에는 내부적으로 함수 호출과 람다를 통해 처리를 하여 속도가
느린 걸 확인할 수 있습니다.

- 100,000,000 반복 시 테스트 결과
- 생각 했던 순서가 아니라 의외였습니다. for문이거나 Task문이 제일 빠를 줄 알았는데
- foreach가 제일 빨랐으며 그 다음이 do - while문 이였습니다.
- Task의 경우에도 비동기로 실행되서 빠를 줄 알았지만
실행 자체는 동기적으로 대기하여 성능 차이가 없다고 합니다.
// 기존 테스트 방식
int temp = value * 2;
// 변경 테스트 방식
float temp = Mathf.Sqrt(value) * Mathf.Sin(value) + Mathf.Log(value + 1);

- 10,000,000 반복 시 테스트 결과
- do-while문이 굉장히 빠르게 나와 왜 빠른지 찾아보았떠니 do-while문은 조건 평가를 반복문 끝에서 한 번만 실행하므로, 첫번째 반복에서의 조건 평가를 건너뛰기 때문이라고 하였습니다.
- for문이 기존 단순 곱셈 방식에서의 속도보다 한참 느리게 나왔습니다.
- foreach문은 여전히 준수한 속도를 보여줍니다.
public class tempdata
{
public string name { get; set; }
}
List<tempdata> dataList = new List<tempdata>();
// 리스트 초기화
for (int i = 0; i < iterations; i++)
{
dataList.Add(new tempdata { name = $"Name_{i}" });
}
string name = dataList[i].name;

- 10,000,000 반복 시 테스트 결과
- 다른 반복문의 속도는 별 차이가 없지만 forecah문에 속도가 현저히 떨어졌습니다.
- foreach문에 경우 컬렉션을 순회하기 위해 반복자를 생성하면서 해당 반복자가
다른 클래스의 변수에 접근하면서 해당 객체를 참조하고 데이터를 가져오는
추가 작업을 수행하게 되면서 속도가 느려진다고 합니다.
| 순위 | 반복문 |
|---|---|
| 1 | do-while |
| 2 | Task |
| 3 | while |
| 4 | for |
| 5 | foreach |
| 6 | Linq |
단순 연산 시 추천하는 반복문
for 문, do-while 문, while 문
컬렉션 순회 시 추천하는 반복문
for 문, foreach 문
조건 기반 반복 시 추천하는 반복문
while 문, do-while 문
데이터 변환 및 필터링 시 추천하는 반복문
Linq , foreach 문
비동기 처리 시 추천하는 반복문
Task
생각보다 for문이 느리고 do-while문과 while문이 빠르단 걸 느꼈습니다.
근데 속도와 별개로 저 같은 경우에는 웬만하면 반복문은 for,foreach,Linq 정도 사용하는데
자료구조처럼 상황에 맞게 적절히 사용해야 하는 것 처럼
반복문도 상황에 맞게 사용하는 노련함을 길러야 할 것 같습니다.