char vs int, 성능 / 메모리 사용량 비교

char와 int의 속도는 같을까? 다를까? char와 int의 성능과 메모리 사용량을 비교해보자.

* 성능 / 메모리 사용량 비교 방법 : C 코드를 컴파일하여 생성된 RISC-V 어셈블리(assembly) 코드로 비교

1 대입 코드 비교

각 자료형에 1을 대입하는 코드를 비교하자.

<C 코드>
char char1;
int int1;

void assign_one_to_char(void)
{
    char1 = 1;
}

void assign_one_to_int(void)
{
    int1 = 1;
}

<symbol table>
location                  size     name
10000000 g     O .sbss 00000004 int1
10000004 g     O .sbss 00000001 char1

<어셈블리 코드>
00000000 <assign_one_to_char>:
   0:	100007b7          	lui	a5,0x10000
   4:	00100713          	li	a4,1
   8:	00e78223          	sb	a4,4(a5) # 10000004 <char1>
   c:	00008067          	ret

00000010 <assign_one_to_int>:
  10:	100007b7          	lui	a5,0x10000
  14:	00100713          	li	a4,1
  18:	00e7a023          	sw	a4,0(a5) # 10000000 <int1>
  1c:	00008067          	ret

성능

명령어 개수가 같으므로 성능은 같다. load / store 명령어가 CPU 처리 단위인 4byte뿐 아니라 1byte 접근도 가능하여 성능 차이가 없다.

char = int

메모리 사용량

data 크기 code 크기 합계
char 1 16 17
int 4 16 20

char < int

1 증가 코드 비교

각 자료형을 1 증가시키는 코드를 비교하자.

<C 코드>
char increase_char(char param)
{
    param++;
    return param;
}

int increase_int(int param)
{
    param++;
    return param;
}

<symbol table>
전역변수가 없다.

<어셈블리 코드>
00000000 <increase_char>:
   0:	00150513          	addi	a0,a0,1
   4:	0ff57513          	zext.b	a0,a0
   8:	00008067          	ret

0000000c <increase_int>:
   c:	00150513          	addi	a0,a0,1
  10:	00008067          	ret
0x4번지에서 a0의 하위 8bit로 제로 확장한다. a0가 0x0000_00000~0x0000_00fe라면 괜찮지만, a0가 0x0000_00ff라면 0x0번지로 인해 a0가 0x0000_0100이 되기 때문에 하위 8비트로 제로 확장해서 상위 24bit를 0으로 만들어 0x0000_0000으로 바꾼다. 그리고 a0에 어떤 값이 들어 있는지 모르기 때문에 항상 제로 확장한다.
(C SPEC에 int가 signed로 지정되어 있는 것과 다르게 char는 signed, unsigned가 지정되어 있지 않으며 riscv-gcc는 확인해보니 char가 unsigned로 되어 있다. unsigned char는 0x0000_0000~0x0000_00ff의 값을 가지므로 0x04번지에서 제로 확장을 했다.)

성능

char는 증가 연산으로 char 크기를 넘어가는 경우를 고려해서 모든 경우에 제로 확장해서 명령어가 1개 추가되었고, int는 크기가 32bit이고 register 크기도 32bit이기 때문에 int는 증가 연산으로 int 크기를 넘어갈 수 없기 때문에 명령어가 추가되지 않았다.
int가 char보다 명령어가 1개 적으므로 더 빠르다.

int > char

메모리 사용량

예제에서는 단순화하기 위해 전역변수를 나타내지 않았지만 char / int 전역변수를 각각의 함수의 인수로 받아서 1 증가시킨다고 가정하고 계산하겠다. 따라서 data 크기를 char는 1byte, int는 4byte로 계산했다.

data 크기code 크기합계
char11213
int4812

int < char

정리

단순히 각각의 자료형에 대입하는 경우에는 4byte뿐 아니라 1byte load / store 명령어도 있어서 char, int 간에 성능 차이가 없다. 이런 경우에는 data가 char 자료형 범위 이내라면 속도는 같으므로 메모리 사용량을 줄이려면 char를 쓰면 된다.
하지만 add 등을 사용하면 char는 자료형의 크기를 넘어가게 되는 경우를 고려해서 모든 경우에 제로 확장하는 명령어가 1개 추가됐고 그만큼 code 크기도 증가했다. int는 int 크기가 32bit이고 register 크기도 32bit라서 자료형의 크기를 넘어가는 경우가 없어 추가 명령어가 필요 없다. 이런 경우에는 data가 char 자료형 범위 이내라도 [data 크기 vs 성능, code 크기]의 trade-off를 고려해서 자료형을 선택해야 한다.

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

Comments