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

bool, int가 0, 1을 저장하는 용도로 쓰는 경우를 비교하며, 비교하는 김에 char, struct bit field도 같이 성능과 메모리 사용량을 비교하겠다. CPU 처리 단위와 크기가 같은 int가 속도가 더 빠를 것 같은데 어떨까?

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

bool 자료형

먼저 bool을 알아보면 bool(_Bool)은 0, 1을 저장하는 자료형이며 C99에 추가됐다. 아래는 C99 SPEC에 나와 있는 bool 설명 중 일부이다.

- An object declared as type _Bool is large enough to store the values 0 and 1.
  -> bool은 0, 1을 저장할 수 있는 크기여야 하며, 크기를 숫자로 정하지는 않았다.

- When any scalar value is converted to _Bool, the result is 0 if the value compares equal to 0; otherwise, the result is 1.
  -> bool에 0을 대입하면 0, 0이 아닌 수를 대입하면 1이 저장된다.

1 대입 코드 비교

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

<C 코드>
#include <stdbool.h>

bool bool1;
char char1;
int int1;
struct
{
    int bit:1;
} bitfield;

void assign_one_to_bool(void)
{
    bool1 = 1;
}

void assign_one_to_char(void)
{
    char1 = 1;
}

void assign_one_to_int(void)
{
    int1 = 1;
}

void assign_one_to_bitfield(void)
{
    bitfield.bit = 1;
}

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

<어셈블리 코드>
00000000 <assign_one_to_bool>:
   0:	100007b7          	lui	a5,0x10000
   4:	00100713          	li	a4,1
   8:	00e784a3          	sb	a4,9(a5) # 10000009 <bool1>
   c:	00008067          	ret

00000010 <assign_one_to_char>:
  10:	100007b7          	lui	a5,0x10000
  14:	00100713          	li	a4,1
  18:	00e78423          	sb	a4,8(a5) # 10000008 <char1>
  1c:	00008067          	ret

00000020 <assign_one_to_int>:
  20:	100007b7          	lui	a5,0x10000
  24:	00100713          	li	a4,1
  28:	00e7a223          	sw	a4,4(a5) # 10000004 <int1>
  2c:	00008067          	ret

00000030 <assign_one_to_bitfield>:
  30:	10000737          	lui	a4,0x10000

# 1bit 변경이 필요하지만 1bit store 명령어는 없기 때문에 1byte load해서 1bit만 1로 set하여 1byte store한다. 
  34:	00074783          	lbu	a5,0(a4) # 10000000 <bitfield>
  38:	0017e793          	ori	a5,a5,1
  3c:	00f70023          	sb	a5,0(a4)
  
  40:	00008067          	ret

성능

bool, char, int 대비 struct bit field가 명령어 1개를 더 수행하므로 느리다. CPU 처리 단위와 크기가 같은 int가 bool, char보다 성능이 좋을 수도 있다고 예상했으나, load / store 명령어가 4byte뿐 아니라 1byte 접근도 가능하여 성능 차이가 없었다.

bool = char = int > struct bit field

메모리 사용량

C99에서 bool은 0, 1을 저장할 수 있는 크기라고 명시하지만, 크기를 숫자로 정하지는 않아서 컴파일러마다 다른 크기를 가질 수 있다. 테스트 조건에서 컴파일해서 symbol table을 보면 bool이 1byte로 잡혀 있다. 그리고 bitfield1은 4byte로 잡혀 있으나 구조체 전체의 크기이고 bitfield.bit은 1bit를 차지한다.

                data size  code size  total
bool               1byte    16byte   17byte
char               1byte    16byte   17byte
int                4byte    16byte   20byte
struct bit field    1bit    20byte   20byte + 1bit

bool = char < int < struct bit field

int 변수 대입 코드 비교

코드를 짜다 보면 bool 변수, int 변수 둘 다 0, 1만 저장하는 경우에 bool 변수에 int 변수를 대입할 수 있는데, 어떻게 동작할까? 1이 들어 있는 int 변수를 대입하는 코드를 비교하자.

<C 코드>
#include <stdbool.h>

bool bool1;
char char1;
int int1;
struct
{
    int bit:1;
} bitfield;

int int2 = 1;

void assign_int_to_bool(void)
{
    bool1 = int2;
}

void assign_int_to_char(void)
{
    char1 = int2;
}

void assign_int_to_int(void)
{
    int1 = int2;
}

void assign_int_to_bitfield(void)
{
    bitfield.bit = int2;
}

<symbol table>
location                  size     name
10000000 g     O .sbss 00000004 bitfield
10000004 g     O .sbss 00000004 int1
10000008 g     O .sbss 00000001 char1
10000009 g     O .sbss 00000001 bool1
10020000 g     O .sdata 00000004 int2

<어셈블리 코드>
00000000 <assign_int_to_bool>:
   0:	100207b7          	lui	a5,0x10020
   4:	0007a783          	lw	a5,0(a5) # 10020000 <int2>
   8:	10000737          	lui	a4,0x10000

# int2에 1이 들어 있지만 0,1만 들어 있다고 가정할 수 없으므로 a5가 0이 아니면(ex. 1, 2, 3, ...) a5가 1이 되도록 한다.
# 따라서 int2가 3이라 하더라도 bool1에는 1로 변환하여 대입한다.
   c:	00f037b3          	snez	a5,a5

  10:	00f704a3          	sb	a5,9(a4) # 10000009 <bool1>
  14:	00008067          	ret

00000018 <assign_int_to_char>:
  18:	100207b7          	lui	a5,0x10020
  1c:	0007a703          	lw	a4,0(a5) # 10020000 <int2>
  20:	100007b7          	lui	a5,0x10000
  24:	00e78423          	sb	a4,8(a5) # 10000008 <char1>
  28:	00008067          	ret

0000002c <assign_int_to_int>:
  2c:	100207b7          	lui	a5,0x10020
  30:	0007a703          	lw	a4,0(a5) # 10020000 <int2>
  34:	100007b7          	lui	a5,0x10000
  38:	00e7a223          	sw	a4,4(a5) # 10000004 <int1>
  3c:	00008067          	ret

00000040 <assign_int_to_bitfield>:
  40:	100006b7          	lui	a3,0x10000
  44:	10020737          	lui	a4,0x10020
  48:	0006c783          	lbu	a5,0(a3) # 10000000 <bitfield>
  4c:	00072703          	lw	a4,0(a4) # 10020000 <int2>

# a5 하위 1bit를 a4의 하위 1bit로 변경한다.
  50:	ffe7f793          	andi	a5,a5,-2
  54:	00177713          	andi	a4,a4,1
  58:	00e7e7b3          	or	a5,a5,a4
  
  5c:	00f68023          	sb	a5,0(a3)
  60:	00008067          	ret

성능

char, int 대비 bool은 int2가 0보다 크면 1로 변환하여 대입하는 부분이 추가되어 명령어 1개를 더 수행하며, struct bit field는 int2에서 1bit을 추출해서 bool에 1bit을 set 하는 부분 때문에 4개 명령어를 더 수행한다.

char = int > bool > struct bit field

메모리 사용량

                data size  code size  total
bool               1byte    24byte   25byte
char               1byte    20byte   21byte
int                4byte    20byte   24byte
struct bit field    1bit    36byte   36byte + 1bit

char < int < bool < struct bit field

정리

0, 1을 저장하는 용도로 사용할 때는 bool이 C에서 정식으로 지원하는 자료형이라서 0, 1만 저장됨을 보장하고 속도, 메모리 사용량도 int, char 대비 떨어지지 않으므로 bool을 쓰는 게 좋을 것 같다.
하지만 bool에 int를 대입하면 int에 0, 1만 들어 있다고 가정할 수 없으므로 0이 아니면 1로 변환해주는 부분이 추가되어 명령어 1개가 추가로 수행되고 struct bit field는 data size가 1bit로 가장 작지만 1bit load / store가 없기 때문에 접근하고 수정할 때 명령어가 4개가 추가로 수행되어 그만큼 성능이 떨어지게 되고, code 크기도 증가한다.

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

Comments