메모리를 절약하기 위해 변수에 더 작은 데이터 유형을 사용하는 것이 좋습니다?


32

C ++ 언어를 처음 배웠을 때 int, float 등 이외에도 이러한 데이터 형식의 더 작거나 더 큰 버전이 언어 내에 존재한다는 것을 알게되었습니다. 예를 들어 변수 x를 호출 할 수 있습니다

int x;
or 
short int x;

주요 차이점은 short int는 2 바이트의 메모리를 사용하고 int는 4 바이트를 사용하고 short int는 더 작은 값을 갖지만 더 작게 만들기 위해 이것을 호출 할 수 있다는 것입니다.

int x;
short int x;
unsigned short int x;

훨씬 더 제한적입니다.

내 질문은 변수가 프로그램 내에서 취하는 값에 따라 별도의 데이터 유형을 사용하는 것이 좋습니다. 이러한 데이터 유형에 따라 항상 변수를 선언하는 것이 좋습니다?


3
Flyweight 디자인 패턴 을 알고 있습니까? "다른 유사한 객체들과 가능한 한 많은 데이터를 공유함으로써 메모리 사용을 최소화하는 객체; 간단한 반복 표현이 허용 할 수없는 양의 메모리를 사용할 때 많은 객체를 사용하는 방법입니다"
gnat

5
표준 패킹 / 정렬 컴파일러 설정을 사용하면 변수가 4 바이트 경계에 맞춰 지므로 전혀 차이가 없을 수 있습니다.
nikie

36
조기 최적화의 전형적인 사례.
scarfridge

1
@nikie-x86 프로세서에서 4 바이트 경계에 정렬 될 수 있지만 일반적으로 그렇지 않습니다. MSP430은 모든 바이트 주소에 char을 배치하고 다른 모든 바이트는 짝수 바이트 주소에 배치합니다. AVR-32와 ARM Cortex-M이 동일하다고 생각합니다.
uɐɪ

3
귀하의 질문의 두 번째 부분은 unsigned어떻게 든 정수 를 추가 하면 공간을 적게 차지 한다는 것을 암시합니다 . 그것은 별개의 표현 가능한 값의 개수가 같을 것입니다 (기호가 표현되는 방법에 따라 1을 주거나 가져 가십시오).
underscore_d

답변:


41

대부분의 경우 공간 비용은 무시할 수 있으며 걱정할 필요는 없지만 유형을 선언하여 제공하는 추가 정보에 대해 걱정해야합니다. 예를 들어,

unsigned int salary;

다른 개발자에게 유용한 정보를 제공하고 있습니다. 급여는 음수 일 수 없습니다.

short, int, long의 차이점은 응용 프로그램에서 공간 문제를 거의 일으키지 않습니다. 실수로 숫자가 항상 일부 데이터 유형에 적합하다는 잘못된 가정을 할 가능성이 높습니다. 숫자가 항상 매우 작을 것이라고 100 % 확신하지 않는 한 항상 int를 사용하는 것이 더 안전합니다. 그럼에도 불구하고 눈에 띄는 공간을 절약 할 수는 없습니다.


5
요즘에는 문제가 거의 발생하지 않지만 다른 개발자가 사용할 라이브러리 또는 클래스를 디자인하는 경우 또 다른 문제입니다. 어쩌면 그들은 백만 개의 객체에 대한 스토리지가 필요할 것입니다.이 경우이 한 필드의 2MB와 비교하여 4MB의 차이가 큽니다.
dodgy_coder

30
사용 unsigned이 경우에하는 것은 나쁜 생각이다 :뿐만 아니라 급여는 음수가 될 수 없지만, 차이 두 급여 사이의 어느 음수가 될 수 없습니다. (일반적으로 비트
비틀림 이외의 용도로

15
@zvrba : 두 급여의 차이점은 그 자체가 급여가 아니므로 서명 된 다른 유형을 사용하는 것이 합법적입니다.
JeremyP

12
@JeremyP 예. 그러나 C를 사용하는 경우 (그리고 C ++에서도 마찬가지입니다) 부호없는 정수 뺄셈 은 부호없는 int를 생성 하며 이는 음수가 될 수 없습니다. 부호있는 정수로 캐스트하면 올바른 값으로 변할 수 있지만 계산 결과는 부호없는 정수입니다. 부호있는 / 부호없는 계산 기묘함에 대해서는 이 답변 을 참조하십시오 . 따라서 실제로 비트를 꼬이지 않는 한 부호없는 변수를 사용해서는 안됩니다.
Tacroy

5
@zvrba : 차이점은 화폐 수량이지만 급여는 아닙니다. 이제 급여가 화폐 수량 (대부분의 사람들이 할 일을 입력하여 양수와 0으로 제한됨)이라고 주장 할 수 있지만 두 급여의 차이는 그 자체가 급여가 아닙니다.
JeremyP

29

OP는 프로그램을 작성하는 시스템 유형에 대해서는 아무 말도하지 않았지만 OP는 C ++이 언급 된 이후 GB의 메모리가있는 일반적인 PC를 생각한다고 가정합니다. 주석 중 하나에서 알 수 있듯이 이러한 종류의 메모리에서도 배열과 같은 한 유형의 수백만 항목이있는 경우 변수의 크기가 달라질 수 있습니다.

OP가 PC로 제한하지 않기 때문에 실제로 문제의 범위를 벗어난 임베디드 시스템의 세계에 들어가면 데이터 유형의 크기가 매우 중요합니다. 방금 8K 워드의 프로그램 메모리와 368 바이트 의 RAM이 있는 8 비트 마이크로 컨트롤러에서 빠른 프로젝트를 마쳤습니다 . 분명히 모든 바이트가 중요합니다. 필요한 것보다 더 큰 변수는 사용하지 않습니다. (공간 관점과 코드 크기 모두에서 8 비트 프로세서는 16 및 32 비트 데이터를 조작하기 위해 많은 명령을 사용합니다). 자원이 제한된 CPU를 사용하는 이유는 무엇입니까? 대량으로 구입하는 데는 1/4 정도의 비용이 들지 않습니다.

현재 512K 바이트의 플래시와 128K 바이트의 RAM을 갖춘 32 비트 MIPS 기반 마이크로 컨트롤러로 또 다른 임베디드 프로젝트를 수행하고 있습니다 (수량은 약 $ 6). PC와 마찬가지로 "자연"데이터 크기는 32 비트입니다. 이제 문자 나 반바지 대신 대부분의 변수에 정수를 사용하는 것이 코드 방식으로 더 효율적이됩니다. 그러나 다시 한 번, 더 작은 데이터 유형이 보장되는지 여부에 관계없이 모든 유형의 배열 또는 구조를 고려해야합니다. 더 큰 시스템의 컴파일러와 달리 구조의 변수 는 임베디드 시스템에 압축 가능성이 높습니다 . 모든 "비트"를 피하기 위해 항상 모든 32 비트 변수를 먼저, 그 다음 16 비트, 8 비트로 설정하려고 노력합니다.


10
임베디드 시스템에 다른 규칙이 적용된다는 사실에 +1하십시오. C ++이 언급되었다고해서 대상이 PC라는 의미는 아닙니다. 최근 프로젝트 중 하나는 32k RAM 및 256K Flash 프로세서에서 C ++로 작성되었습니다.
uɐɪ

13

답은 시스템에 따라 다릅니다. 일반적으로 더 작은 유형을 사용하면 다음과 같은 장점과 단점이 있습니다.

장점

  • 더 작은 유형은 대부분의 시스템에서 더 적은 메모리를 사용합니다.
  • 더 작은 유형은 일부 시스템에서 더 빠른 계산을 제공합니다. 많은 시스템에서 float vs double의 경우 특히 그렇습니다. 또한 int 유형이 작을수록 8 비트 또는 16 비트 CPU에서 훨씬 빠른 코드를 제공합니다.

단점

  • 많은 CPU에는 정렬 요구 사항이 있습니다. 일부는 정렬되지 않은 것보다 빠르게 정렬 된 데이터에 액세스합니다. 일부 데이터에 액세스 할 수 있도록 데이터를 정렬 해야 합니다. 더 큰 정수 유형은 하나의 정렬 된 단위와 동일하므로 정렬되지 않은 것 같습니다. 이것은 컴파일러가 더 작은 정수를 더 큰 정수로 넣어야한다는 것을 의미합니다. 더 작은 유형이 더 큰 구조체의 일부인 경우 정렬을 수정하기 위해 컴파일러가 구조체의 어느 위치 에나 다양한 패딩 바이트를 자동으로 삽입 할 수 있습니다.
  • 위험한 암시 적 변환. C와 C ++에는 타입 캐스트없이 암시 적으로 변수를 더 큰 것으로 승격시키는 방법에 대한 몇 가지 모호하고 위험한 규칙이 있습니다. "정수 승격 규칙"과 "일반 산술 변환"이라고하는 두 개의 암시 적 변환 규칙이 서로 얽혀 있습니다. 자세한 내용은 여기를 참조 하십시오 . 이 규칙은 C 및 C ++의 버그에 대한 가장 일반적인 원인 중 하나입니다. 프로그램 전체에서 동일한 정수 유형을 사용하면 많은 문제를 피할 수 있습니다.

내 조언은 다음과 같습니다.

system                             int types

small/low level embedded system    stdint.h with smaller types
32-bit embedded system             stdint.h, stick to int32_t and uint32_t.
32-bit desktop system              Only use (unsigned) int and long long.
64-bit system                      Only use (unsigned) int and long long.

또는 stdint.h int_leastn_t또는 int_fastn_tfrom from stdint.h를 사용할 수 있습니다. 여기서 n은 숫자 8, 16, 32 또는 64입니다. int_leastn_ttype은 "이것이 n 바이트 이상 이길 원하지만 컴파일러에서 다음과 같이 할당해도 상관 없습니다." 정렬에 적합한 더 큰 유형 "

int_fastn_t "이것은 n 바이트 길이를 원하지만 코드가 더 빨리 실행되도록하려면 컴파일러가 지정된 것보다 큰 유형을 사용해야합니다."

일반적으로 다양한 stdint.h 유형 int은 이식 가능하기 때문에 일반 등 보다 훨씬 낫습니다 . 와 의도는 int그것이 휴대용 수 있도록 폭 단독으로 지정된주지하는 것이 었습니다. 그러나 실제로는 특정 시스템에 얼마나 큰지 알 수 없기 때문에 이식하기가 어렵습니다.


얼라인먼트에 대해 주목하십시오. 현재 진행중인 프로젝트에서 16 비트 MSP430에서 uint8_t를 무의미하게 사용하면 신비한 방식으로 MCU에 충돌이 발생했습니다 (대부분 정렬되지 않은 액세스가 어딘가에서 발생했거나 GCC의 결함 일 수도 있음). 치명적이지 않은 경우> 8 비트 아치에서 8 비트 유형을 사용하는 것은 비효율적입니다. 컴파일러는 추가 '및 reg, 0xff'명령을 생성합니다. 이식성을 위해 'int / unsigned'를 사용하고 컴파일러에서 추가 제약 조건을 제거하십시오.
alexei

11

특정 운영 체제의 작동 방식에 따라 일반적으로 바이트 또는 단어 또는 다른 작은 데이터 유형을 호출 할 때 값이 전체 레지스터를 차지하도록 메모리가 최적화되지 않은 것으로 할당됩니다. 개인적인. 컴파일러 또는 인터프리터가 이것을 해석하는 방법은 다른 것입니다. 예를 들어 C #에서 프로그램을 컴파일하는 경우 값이 실제로 레지스터 자체를 차지할 수 있지만 값이 경계 검사되어 의도 한 데이터 유형의 범위를 초과하는 값을 저장하십시오.

성능 측면에서, 그리고 그러한 것들에 대해 정말 신기한 경우, 대상 레지스터 크기와 가장 일치하는 데이터 유형을 사용하는 것이 더 빠를 것입니다.하지만 변수로 작업하기가 쉬운 멋진 구문 설탕을 모두 놓칠 수 있습니다 .

이것이 어떻게 도움이 되나요? 코딩 할 상황을 결정하는 것은 당신에게 달려 있습니다. 필자가 작성한 거의 모든 프로그램에 대해 컴파일러를 신뢰하여 작업을 최적화하고 가장 유용한 데이터 유형을 사용하는 것으로 충분합니다. 높은 정밀도가 필요한 경우 더 큰 부동 소수점 데이터 유형을 사용하십시오. 양수 값으로 만 작업하는 경우 부호없는 정수를 사용할 수 있지만 대부분 int 데이터 유형을 사용하면 충분합니다.

그러나 통신 프로토콜 작성 또는 암호화 알고리즘과 같은 매우 엄격한 데이터 요구 사항이있는 경우 특히 데이터 오버런 / 언더런과 관련된 문제를 피하려는 경우 범위 확인 데이터 유형을 사용하면 매우 유용 할 수 있습니다. 또는 유효하지 않은 데이터 값입니다.

특정 데이터 유형을 사용하기 위해 머리 꼭대기에서 생각할 수있는 다른 이유는 코드 내에서 의도를 전달하려고 할 때입니다. 예를 들어 shortint를 사용하는 경우 다른 개발자에게 매우 작은 값 범위 내에서 양수와 음수를 허용한다고 말하고 있습니다.


6

scarfridge가 언급 했듯이 , 이것은

조기 최적화 의 전형적인 사례 .

메모리 사용을 최적화하려고하면 다른 성능 영역에 영향을 줄 있으며 다음 과 같은 최적화 규칙이 있습니다.

프로그램 최적화의 첫 번째 규칙 : 하지 마십시오 .

프로그램 최적화의 두 번째 규칙 (전문가에게만 해당) : 아직 수행하지 마십시오 . "

— 마이클 에이 잭슨

지금 최적화해야 할 시점인지 알기 위해서는 벤치마킹 및 테스트가 필요합니다. 비효율적 인 코드의 위치를 ​​알아야 최적화를 목표로 할 수 있습니다.

여부를 결정하기 위해 최적화 된 코드의 버전 입니다 실제로 더 나은 주어진 시간에 순진한 구현보다, 당신은 그들이 나란히 같은 데이터를 벤치 마크 할 필요가있다.

또한 주어진 구현이 현재 세대의 CPU에서 더 효율적이기 때문에 항상 그렇게 될 것이라는 것을 기억하지 마십시오 . 질문에 대한 내 대답 코딩 할 때 미세 최적화가 중요합니까? 더 이상 사용되지 않는 최적화로 인해 속도가 느려지는 개인 경험의 예를 자세히 설명합니다.

많은 프로세서에서 정렬되지 않은 메모리 액세스는 현저하게 정렬 된 메모리 액세스보다 더 많은 비용이 많이 드는. 구조체에 두 개의 반바지를 포장하면 값을 만질 때마다 프로그램이 포장 / 포장 풀기 작업을 수행해야 함을 의미 할 수 있습니다 .

이러한 이유로 현대 컴파일러는 제안을 무시합니다. 니키는 다음과 같이 말합니다 .

표준 패킹 / 정렬 컴파일러 설정을 사용하면 변수가 4 바이트 경계에 맞춰 지므로 전혀 차이가 없을 수 있습니다.

둘째, 컴파일러를 위험에 빠뜨린다.

테라 바이트 데이터 세트 또는 임베디드 마이크로 컨트롤러로 작업 할 때 이러한 최적화를위한 장소가 있지만 대부분의 경우 실제로 걱정할 필요는 없습니다.


3

주요 차이점은 short int는 2 바이트의 메모리를 사용하고 int는 4 바이트를 사용하고 short int는 더 작은 값을 갖지만 더 작게 만들기 위해 이것을 호출 할 수 있다는 것입니다.

이것은 올바르지 않습니다. char각 유형의 크기가 이전보다 크거나 같은 각 유형의 바이트 수 (바이트 당 1 바이트 이상, 8 비트 이상)를 가정 할 수는 없습니다 .

스택 변수에 대한 성능 이점은 엄청나게 미미합니다. 어쨌든 정렬 / 패딩 될 수 있습니다.

이 때문에, short그리고 long실제적으로이 없다 요즘 사용하고 사용하여 떨어져 거의 항상 더 낫다 int.


물론, 그것을 자르지 않을 stdint.h때 사용하기에 완벽하게 좋은 것도 있습니다 int. 거대한 정수 / 구조체 배열을 할당하는 intX_t경우 효율적이고 유형의 크기에 의존 할 수 있으므로 의미가 있습니다. 메가 바이트의 메모리를 절약 할 수 있기 때문에 이것은 너무 이른 것은 아닙니다.


1
실제로 64 비트 환경의 출현과는 long다를 수 있습니다 int. 컴파일러가 LP64이고 int32 비트이고 long64 비트이면 ints가 여전히 4 바이트 정렬 될 수 있음을 알 수 있습니다 (예 : 컴파일러는 예).
JeremyP

1
@JeremyP 그래, 내가 아니면 다른 말을 했니?
Pubby

짧고 긴 주장을하는 마지막 문장은 실제로 쓸모가 없습니다. 기본 유형의 경우에만 오랫동안 사용됩니다int64_t
JeremyP

@JeremyP : 당신은 int와 오랫동안 오래 살 수 있습니다.
gnasher729

@ gnasher729 : 65 천 개 이상의 값을 보유 할 수 있지만 10 억을 넘지 않는 변수가 필요하면 어떻게 사용합니까? int32_t, int_fast32_tlong모두 좋은 옵션이며, long long낭비적이고 int휴대 할 수 없습니다.
Ben Voigt

3

이것은 일종의 OOP 및 / 또는 기업 / 응용 관점에서 비롯 될 수 있으며 특정 분야 / 도메인에는 적용되지 않을 수 있지만, 나는 원시적 집착 의 개념을 제시하고 싶습니다 .

응용 프로그램의 여러 종류의 정보에 다른 데이터 형식을 사용하는 것이 좋습니다. 그러나 몇 가지 심각한 성능 문제 (측정 및 확인 등)가없는 한 내장 유형을 사용하는 것은 좋지 않습니다.

우리는 우리의 응용 프로그램에서 켈빈 모델 온도 싶은 경우에, 우리는 사용할 수 ushort또는 uint또는 표시하기 위해 유사한 "부정적인 학위의 개념을 켈빈 말도 및 도메인 논리 오류". 이것의 배후에있는 아이디어는 건전하지만, 당신은 계속 가지 않을 것입니다. 우리가 깨달은 것은 음수 값을 가질 수 없기 때문에 아무도 켈빈 온도에 음수 값을 할당하지 않도록 컴파일러를 사용할 수 있으면 편리합니다. 온도에서 비트 단위 작업을 수행 할 수 없다는 것도 사실입니다. 그리고 온도 (K)에 무게 (kg)를 추가 할 수 없습니다. 그러나 온도와 질량을 모두 uints 로 모델링 하면 바로 그렇게 할 수 있습니다.

내장 유형을 사용하여 DOMAIN 엔터티를 모델링하면 일부 복잡한 코드와 누락 된 검사 및 불변 불변이 발생할 수 있습니다. 유형이 엔티티의 일부를 캡처하더라도 (음수가 될 수 없음) 다른 유형을 놓칠 수 있습니다 (임의의 산술 표현식에 사용할 수 없으며 비트 배열로 취급 할 수 없음).

해결책은 불변량 을 캡슐화 하는 새로운 유형을 정의 하는 것입니다. 이렇게하면 돈이 돈이고 거리가 거리인지 확인할 수 있으며 돈을 더할 수 없으며 음수 거리를 만들 수는 없지만 음수 금액 (또는 부채)을 만들 수 있습니다. 물론 이러한 유형은 내장 유형을 내부적으로 사용하지만 클라이언트 에서는 숨겨져 있습니다. 성능 / 메모리 소비에 대한 질문과 관련하여, 이런 종류의 당신이 그 빌어 먹을을 찾아야한다, 도메인 엔티티에서 작동하여 기능의 인터페이스를 변경하지 않고 내부적으로 저장하는 방법을 가지 변경을 허용 할 수 있습니다, a는 short너무 망할 그냥 큰.


1

물론입니다. 그것은 사용하는 것이 좋습니다 uint_least8_t사전에, 거대한 정수 어레이, 버퍼 등이 더 잘 사용하는 것입니다 uint_fast8_t목적을 처리합니다.

uint8_least_t(저장)-> uint8_fast_t(처리)-> uint8_least_t(저장).

예를 들어 source, 8 비트 심볼 , 16 비트 코드 dictionaries및 32 비트를 사용 constants합니다. 그들과 함께 10-15 비트 연산을 처리하고 8 비트를 출력하는 것보다 destination.

2 기가 바이트를 처리해야한다고 가정 해 봅시다 source. 비트 연산량은 엄청납니다. 처리 중에 빠른 유형으로 전환하면 성능이 크게 향상됩니다. 빠른 유형은 CPU 제품군마다 다를 수 있습니다. 당신은 포함 할 수 있습니다 stdint.h사용 uint_fast8_t, uint_fast16_t, uint_fast32_t, 등

이식성 uint_least8_t대신에 사용할 수 있습니다 uint8_t. 그러나 실제로 아무도 현대 CPU 가이 기능을 사용할지 알지 못합니다 . VAC 기계는 박물관 조각입니다. 아마도 그것은 과잉 일 것입니다.


1
나열된 데이터 유형에 대한 요점이있을 수 있지만 그 유형을 나타내는 것보다는 더 나은지 설명해야합니다 . 저와 같은 데이터 유형에 익숙하지 않은 사람들을 위해, 나는 당신이 말하는 것을 이해하기 위해 구글을 사용해야했습니다.
Peter M
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.