Posts C# - If vs try-catch 성능
Post
Cancel

C# - If vs try-catch 성능

Note


개발을 하다보면 고민되는 경우가 많다.

예외 조건을 처리할 때 if로 예외를 회피할지, try-catch로 처리할지,

실제 성능은 어떻게 될지도 궁금한 부분이다.


try-catch는 예외가 발생하지 않으면 성능 소모가 없다고도 하고,

성능 소모가 있지만 O(1)이라고도 하고,

찾아보면 다양한 주장들을 확인해볼 수 있다.


대신 공통적인 사실은

try-catch를 통해 예외를 핸들링하게 되면

무조건 if보다 성능 소모가 크다는 점이다.

try-catch는 예외 발생 지점의 스택을 거슬러 올라가서

모두 추적하고 기록하게 되는데,

이 과정에서 성능 소모가 크게 발생한다는 것이다.


이제 몇가지 테스트 케이스를 통해서 성능을 테스트하고,

iftry-catch 선택을 위한 원칙을 세우려고 한다.

성능 테스트에는 Benchmark DotNet을 이용한다.


Test Case 1


1
2
3
4
5
6
int[] array = new int[1000];

for (int i = min; i < max; i++)
{
    array[i] = i;
}

배열의 인덱스를 참조하여, 해당 위치에 간단히 값을 넣는다.

잘못된 인덱스를 참조하게 되면 IndexOutOfRangeException이 발생한다.


테스트 대상

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// [1]
for (int i = min; i < max; i++)
{
    if (i < 0) continue;

    array[i] = i;
}

// [2]
for (int i = min; i < max; i++)
{
    if (i < 0) continue;
    if (i >= array.Length) continue;

    array[i] = i;
}

// [3]
for (int i = min; i < max; i++)
{
    try
    {
        array[i] = i;
    }
    catch(IndexOutOfRangeException)
    {
        continue;
    }
}

// [4]
for (int i = min; i < max; i++)
{
    try
    {
        array[i] = i;
    }
    catch(Exception)
    {
        continue;
    }
}

모두 공통적으로 잘못된 인덱스 참조 시 continue를 통해 다음 반복을 이어나간다.

[1]에서는 인덱스의 하한만 검사하고,

[2]에서는 인덱스의 하한과 상한을 모두 검사한다.

[3]에서는 IndexOutOfRangeExceptioncatch를 통해 체크하고,

[4]에서는 Exception으로 모든 예외를 체크한다.


테스트 조건

min, max 값을 다음과 같이 설정하여 테스트를 진행한다.

  • -1000, 0 (모두 예외 발생)

  • -500, 500

  • -100, 900

  • 0, 1000 (예외 미발생)


결과

image

예외가 발생하는 경우에는 비교조차 불가능할 정도로 If가 빨랐으며,

심지어 예외가 발생하지 않는 경우에도 If가 빠르다.


결론

  • 항상 Iftry-catch보다 성능이 좋다.

  • 예외가 발생하는 경우, 압도적으로 If의 성능이 좋다.

  • catch를 통해 특정 예외만 검사하는 것과 Exception을 모두 검사하는 것은 성능 차이가 없다.


Test Case 2


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class TestClass
{
    public float value;

    public void SetValue(float value)
    {
        this.value = value;
    }
}

public TestClass[] array = new TestClass[1000];

[Params(1000, 900, 500, 100, 0)]
public int NullCount;

[GlobalSetup]
public void GlobalSetup()
{
    for (int i = NullCount; i < 1000; i++)
    {
        array[i] = new TestClass();
    }
}

[GlobalCleanup]
public void GlobalCleanUp()
{
    for (int i = 0; i < 1000; i++)
    {
        array[i] = null;
    }
}


1
2
3
4
for (int i = 0; i < 1000; i++)
{
    array[i].SetValue(i);
}

크기 1000인 배열에 간단한 클래스 객체를 생성해 넣는다.

NullCount에 따라 null인 영역의 비율이 정해진다.

그리고 해당 인덱스에 있는 객체로부터 .SetValue()를 호출하며,

객체가 null일 경우 NullReferenceException이 발생하게 된다.


테스트 대상

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// [1]
for (int i = 0; i < 1000; i++)
{
    if (array[i] != null)
        array[i].SetValue(i);
}

// [2]
for (int i = 0; i < 1000; i++)
{
    array[i]?.SetValue(i);
}

// [3]
for (int i = 0; i < 1000; i++)
{
    try
    {
        array[i].SetValue(i);
    }
    catch(NullReferenceException)
    {
        continue;
    }
}

// [4]
for (int i = 0; i < 1000; i++)
{
    try
    {
        array[i].SetValue(i);
    }
    catch(Exception)
    {
        continue;
    }
}

[1]에서는 if를 통해 null인지 검사하여, null이 아닌 경우에만 메소드를 호출하고

[2]에서는 널 조건 연산자 ?를 통해 메소드를 호출한다.

[3]에서는 try-catch를 통해 NullReferenceException을 검사하고,

[4]에서는 모든 예외를 검사한다.

공통적으로, 객체가 null일 경우 아무 것도 수행하지 않는다.


테스트 조건

배열 내 1000개의 객체 중 null인 객체의 개수를 각각 테스트마다 다르게 지정한다.

0, 100, 500, 900, 1000개로 지정하여 각각 테스트한다.


결과

image


결론

  • 예외가 발생하지 않을 때도 try-catch가 느리다.

  • 예외가 발생하면 압도적으로 try-catch가 느리다.

  • if에 의한 null 조건 분기와 ? 연산자 사용은 성능 차이가 없다.

  • try-catch에서 예외를 특정하는 것과 모든 예외를 처리하는 것은 성능 차이가 없다.


최종 결론


iftry-catch에 관한 개발자들의 여론에 비해

이번 테스트 결과는 신빙성이 없을 정도로 try-catch의 성능이 압도적으로 좋지 않았다.

그래서 더 다양한 테스트 케이스를 설정하고 다시 검증을 하는 것이 좋을 것 같다.


그래도 테스트 결과만으로 결론을 내리고, 원칙을 세워보자면 다음과 같다.

  1. if로 검사할 수 없는 예외는 try-catch로 처리한다.

  2. if로 검사할 수 있는 예외는 최대한 if로 처리한다.

  3. 예외 종류에 관계 없이 동일한 처리를 수행한다면, 굳이 catch를 늘려서 예외를 특정하여 나열할 필요 없이 Exception으로 받아 처리해도 된다.


References


This post is licensed under CC BY 4.0 by the author.