C코드 최적화 - char, short 대신 int 사용

0~100의 값만 필요한 변수라면 성능 관점에서 char, short, int 중에 어느 걸 써야 할까?
int를 써야 한다. int 대신에 char, short 을 사용하면 성능이 떨어지는 경우가 있기 때문이다.

먼저 CPU 동작을 매우 간단하게만 알아보자. 32bit CPU인 경우에, CPU에는 여러 연산(더하기, 빼기 등)을 처리할 수 있는 연산기가 있고, 내부에는 32bit 레지스터(register)가 여러 개 있다. 레지스터는 CPU가 사용하는 조그마한 저장 공간이다. CPU는 레지스터 저장공간을 사용해서 연산을 한다. 예컨대, c = a + b; 라면 CPU는 a, b를 각각 레지스터0, 레지스터1에 올려 놓고 더하기 연산을 해서 레지스터2에 더하기 결과를 넣을 수 있다.

그런데, 32bit인 int는 레지스터에 그대로 넣으면 되는데, 8bit인 char, 16bit인 short은 레지스터에 어떻게 넣을까?
부호 있는 수(signed)이면 부호확장(sign extension)하고 부호 없는 수(unsigned)이면 제로확장(zero extension)해서 레지스터에 넣는다.
포스팅 ▶ 부호 확장(sign extension)과 제로 확장(zero extension)

int 대비 그만큼 추가 연산이 발생해서 성능에 불이익이 생긴다. (CPU마다 명령어(instruction)가 다르므로 추가 연산이 없는 CPU도 있을 수도 있지만 일반적으로는 추가 연산이 발생한다.)


아래에서 추가 연산이 발생하는 C코드를 보자.

unsigned char increase_char(unsigned char param)
{
    param = param + 1;
    // param = param + 1;
    // param = param & 0x000000ff;
    
    return param;
}

unsigned short increase_short(unsigned short param)
{
    param = param + 1;
    // param = param + 1;
    // param = param & 0x0000ffff;

    return param;
}

unsigned int increase_int(unsigned int param)
{
    param = param + 1;
    // param = param + 1;

    return param;
}

주석 부분은 C코드를 컴파일해서 생성된 어셈블리어를 C 코드 형식으로 표현한 것이다. unsigned char, unsigned short은 각각 상위 24bit, 16bit를 제로 확장하기 위해 AND 연산이 추가되었다. unsigned char를 예로 들면, 컴파일러는 param이 0x0000_00ff인 경우에 param + 1을 하면 0x0000_0100이 되고, 컴파일러는 param에 어떤 값이 들어올지 모르므로 항상 제로확장 코드를 추가한다.

더 자세한 내용은 아래 포스팅을 참조하자.
포스팅 ▶ char vs int, 성능 / 메모리 사용량 비교 with 어셈블리

C코드로 표현하지는 않았지만 signed char, signed short이라면 위에서 제로확장이 추가된 것과 비슷하게 부호확장 연산이 추가되고, signed int는 추가되는 연산이 없다.


따라서, 성능을 고려한다면 char, short 대신에 int를 사용해야 한다.

* Feedback은 언제나 환영합니다.

Comments