[c++] uint_fast32_t보다 uint32_t가 선호되는 이유는 무엇입니까?

(나는 이것이 일화적인 증거라는 것을 알고 있습니다) uint32_t보다 훨씬 더 널리 퍼져있는 것 같습니다 uint_fast32_t. 하지만 그것은 나에게 반 직관적 인 것 같습니다.

거의 항상 구현 사용을 볼 때 uint32_t실제로 원하는 것은 최대 4,294,967,295 값을 보유 할 수있는 정수뿐입니다 (일반적으로 65,535에서 4,294,967,295 사이의 훨씬 낮은 범위).

‘정확히 32 비트’ 보장이 필요하지 않고 ‘가장 빠른> = 32 비트’ 보장 이 정확히 올바른 아이디어 인 것처럼 보이 uint32_t므로 를 사용하는 것이 이상해 보입니다. 또한 일반적으로 구현되지만 실제로 존재한다는 보장은 없습니다.uint_fast32_tuint32_t

그렇다면 왜 uint32_t선호 될까요? 단순히 더 잘 알려져 있습니까 아니면 다른 것에 비해 기술적 이점이 있습니까?



답변

uint32_t지원하는 모든 플랫폼에서 거의 동일한 속성을 갖도록 보장됩니다. 1

uint_fast32_t 비교에서 다른 시스템에서 어떻게 작동하는지에 대한 보장이 거의 없습니다.

uint_fast32_t크기가 다른 플랫폼으로 전환하는 경우 사용하는 모든 코드를 uint_fast32_t다시 테스트하고 유효성을 검사해야합니다. 모든 안정성 가정은 창 밖으로 나올 것입니다. 전체 시스템은 다르게 작동 할 것입니다.

코드를 작성할 때, 당신도 없을 수 있습니다 액세스 A와 uint_fast32_t크기가 32 비트되지 않습니다 시스템.

uint32_t 다르게 작동하지 않습니다 (각주 참조).

속도보다 정확성이 더 중요합니다. 따라서 조기 정확성은 조기 최적화보다 더 나은 계획입니다.

uint_fast32_t64 비트 이상인 시스템에 대한 코드를 작성 하는 경우 두 경우 모두 코드를 테스트하고 사용할 수 있습니다. 필요와 기회를 제외하고 그렇게하는 것은 나쁜 계획입니다.

마지막으로, uint_fast32_t임의의 시간 동안 저장하거나 인스턴스 수 uint32를 저장하면 단순히 캐시 크기 문제 및 메모리 대역폭으로 인해 속도가 느려질 수 있습니다 . 오늘날의 컴퓨터는 CPU 바운드보다 메모리 바운드가 훨씬 더 많으며 uint_fast32_t격리 상태에서는 더 빠를 수 있지만 메모리 오버 헤드를 고려한 후에는 그렇지 않습니다.


1 @chux가 주석에서 언급했듯이 unsigned이보다 크면 uint32_t산술 uint32_t은 일반적인 정수 승격을 거치고 그렇지 않은 경우 uint32_t. 이로 인해 버그가 발생할 수 있습니다. 완벽한 것은 없습니다.


답변

왜 많은 사람들이 uint32_t대신 사용 uint32_fast_t합니까?

참고 : 잘못된 이름 uint32_fast_tuint_fast32_t.

uint32_t보다 엄격한 사양을 가지고 uint_fast32_t있으므로보다 일관된 기능을 제공합니다.


uint32_t 장점 :

  • 다양한 알고리즘이이 유형을 지정합니다. IMO-사용하는 가장 좋은 이유.
  • 정확한 너비와 범위가 알려져 있습니다.
  • 이 유형의 어레이는 낭비가 없습니다.
  • 오버플로가있는 부호없는 정수 수학이 더 예측 가능합니다.
  • 다른 언어의 32 비트 유형의 범위와 수학에서 더 가깝게 일치합니다.
  • 패딩되지 않았습니다.

uint32_t 단점 :

  • 항상 사용할 수있는 것은 아닙니다 (그러나 2018 년에는 드뭅니다).
    예 : 8 / 16 / 32- 비트 정수 (9 / 1018 / 부족 플랫폼 36 비트, 기타 ).
    예 : 2가 아닌 보수를 사용하는 플랫폼. 오래된 2200

uint_fast32_t 장점 :

  • 항상 사용 가능합니다.
    이렇게 하면 새 플랫폼과 이전 플랫폼 모두 항상 빠른 / 최소 유형을 사용할 수 있습니다.
  • 32 비트 범위를 지원하는 “가장 빠른” 유형.

uint_fast32_t 단점 :

  • 범위는 최소한으로 만 알려져 있습니다. 예를 들어 64 비트 유형일 수 있습니다.
  • 이 유형의 배열은 메모리에서 낭비 일 수 있습니다.
  • 모든 답변 (처음에는 내 것), 게시물 및 댓글에 잘못된 이름이 사용되었습니다 uint32_fast_t. 많은 사람들이이 유형을 필요로하지 않고 사용하는 것 같습니다. 우리는 올바른 이름도 사용하지 않았습니다!
  • 패딩 가능-(희귀).
  • 일부 경우에는 “가장 빠른”유형이 실제로 다른 유형일 수 있습니다. 따라서 uint_fast32_t1 차 근사치입니다.

결국 가장 좋은 것은 코딩 목표에 달려 있습니다. 매우 광범위한 이식성 또는 일부 틈새 성능 기능을위한 코딩이 아니라면 uint32_t.


이러한 유형을 사용할 때 또 다른 문제가 있습니다. int/unsigned

아마도 uint_fastN_t순위가 될 수 있습니다 unsigned. 이것은 지정되지 않았지만 특정하고 테스트 가능한 조건입니다.

따라서, uintN_t보다 많은 가능성 uint_fastN_t을 좁게 unsigned. 이것은 uintN_t수학 을 사용하는 코드가 uint_fastN_t이식성에 관한 것보다 정수 승진의 대상이 될 가능성이 더 높다는 것을 의미 합니다.

이 문제는 uint_fastN_t수학 연산을 선택할 때 이식성 이점이 있다는 것 입니다.


int32_t대신 에 대한 참고 사항 int_fast32_t: 희귀 한 컴퓨터에서는 INT_FAST32_MIN-2,147,483,648이 아닌 -2,147,483,647이 될 수 있습니다. 더 큰 요점 (u)intN_t은 유형이 엄격하게 지정되어 이식 가능한 코드로 이어진다는 것입니다.


답변

왜 많은 사람들이 uint32_t대신 사용 uint32_fast_t합니까?

어리석은 대답 :

  • 표준 유형이 없으며 uint32_fast_t올바른 철자는 uint_fast32_t입니다.

실용적인 대답 :

  • 많은 사람들이 실제로 사용 uint32_t하거나 int32_t정확한 의미를 위해 산술 ( uint32_t) 또는 2의 보수 표현 ( int32_t) 을 부호없는 랩 어라운드로 정확히 32 비트를 사용 합니다. xxx_fast32_t유형이 크고 포장 배열과 구조에 바이너리 파일, 사용에 가게에 따라서 적절하지, 또는 네트워크를 통해 보낼 수 있습니다. 또한 더 빠르지 않을 수도 있습니다.

실용적인 대답 :

  • 많은 사람들 uint_fast32_t이 댓글과 답변에서 보여준 것처럼 에 대해 알지 못합니다 (또는 단순히 신경 쓰지 않습니다). unsigned int많은 현재 아키텍처에는 여전히 16 비트 int가 있고 일부 희귀 박물관 샘플에는 32보다 작은 다른 이상한 int 크기.

UX 답변 :

  • 아마도보다 빠른 있지만 uint32_t, uint_fast32_t느린 사용하는 것입니다 : 그것은 특히 C 문서에서 철자와 의미를 찾는를 차지, 입력 오래 걸립니다 😉

우아함이 중요합니다 (분명히 의견 기반) :

  • uint32_t많은 프로그래머가 자신의 유형 u32이나 uint32유형 을 정의하는 것을 선호 할만큼 나쁘게 보입니다. 이 관점에서 uint_fast32_t보면 수리 할 수 ​​없을 정도로 서투른 것처럼 보입니다. 친구들 uint_least32_t과 함께 벤치에 앉아있는 것은 놀라운 일이 아닙니다 .


답변

한 가지 이유는 unsigned int특별한 typedef가 필요하지 않거나 무언가를 포함 할 필요없이 이미 “가장 빠르다”는 것입니다. 따라서 빠르게 필요하면 기본 int또는 unsigned int유형을 사용하십시오 .
표준은 그것이 가장 빠르다는 것을 명시 적으로 보장하지는 않지만, 3.9.1에서 “Plain int는 실행 환경의 아키텍처에서 제안한 자연적인 크기를 갖는다” 라고 명시함으로써 간접적으로 그렇게합니다 . 즉, (또는 서명되지 않은 대응 물) 프로세서가 가장 편한 것입니다.int

물론 이제 어떤 크기 unsigned int가 될지 모릅니다. 당신은 단지 그것을 알고 적어도 한 큰 short(그리고 나는 그 기억 보인다 short지금은 표준에서 그것을 찾을 수 있지만, 적어도 16 비트해야합니다!). 일반적으로 단순히 4 바이트 일 뿐이지 만 이론적으로는 더 클 수도 있고 극단적 인 경우에는 더 작을 수도 있습니다 ( 개인적으로 1980 년대 8 비트 컴퓨터에서도 이런 아키텍처를 접한 적이 없지만). .. 아마도 내가 치매로 고통받는 것을 아는 일부 마이크로 컨트롤러int 는 당시 16 비트였다.)

C ++ 표준은 <cstdint>유형이 무엇인지 또는 무엇을 보장하는지 지정하지 않고 “C에서와 동일”을 언급 할뿐입니다.

uint32_t, C 표준에 따라, 보장 정확히 32 비트를 얻을. 다르지 않고 그 이하도없고 패딩 비트도 없습니다. 때로는 이것이 정확히 필요한 것이므로 매우 가치가 있습니다.

uint_least32_t크기가 무엇이든간에 32 비트보다 작을 수는 없지만 더 클 수 있음을 보장합니다. 때때로, 그러나 정확한 witdh 또는 “상관 안 함”보다 훨씬 드물게 이것이 당신이 원하는 것입니다.

마지막으로 uint_fast32_t의도 문서화 목적을 제외하고는 내 의견 으로 는 다소 불필요합니다. C 표준은 “보통 가장 빠른 정수 유형을 지정” ( “보통”이라는 단어에 유의)을 명시하고 모든 목적에 대해 가장 빠를 필요는 없음을 명시 적으로 언급합니다. 즉, uint_fast32_t단지와 거의 같다 uint_least32_t없다, 이는 일반적으로 가장 빠른 너무 만 더 주어진 보장한다 (하지만 보증 어느 쪽이든).

대부분의 경우 정확한 크기에 신경 쓰지 않거나 정확히 32 (또는 64, 때로는 16) 비트를 원하기 때문에 “관심 없음” unsigned int유형이 가장 빠르기 uint_fast32_t때문에 그렇지 않은 이유 가 설명됩니다. 자주 사용되는.


답변

나는 범위에 uint32_t사용되는 증거를 보지 못했습니다 . 대신, 그 대부분의 시간을 내가 한 볼 을 사용, 그것은 보장 빙 둘러 시프트 의미와 함께, 다양한 알고리즘의 데이터 정확히 4 옥텟을 유지하는 것입니다!uint32_t

uint32_t대신 사용할 다른 이유도 있습니다 uint_fast32_t. 종종 안정적인 ABI를 제공하기 때문입니다. 또한 메모리 사용량을 정확하게 알 수 있습니다. 속도 이득에서 일한다 무엇 이건이 매우 오프셋은 uint_fast32_t, 때마다 그 유형의 구별이 될 것입니다 uint32_t.

값이 65536 미만이면 이미 편리한 유형이 있으며 호출됩니다 unsigned int( unsigned short최소한 해당 범위도 있어야하지만 unsigned int기본 단어 크기 임). 값이 4294967296 미만이면라는 또 다른 유형이 unsigned long있습니다.


그리고 마지막으로 uint_fast32_t입력하는 것이 귀찮고 길고 잘못 입력하기 쉽기 때문에 사람들은 사용하지 않습니다 .


답변

몇 가지 이유.

  1. 많은 사람들은 ‘빠른’유형이 존재한다는 것을 모릅니다.
  2. 입력하는 것이 더 장황합니다.
  3. 유형의 실제 크기를 모르면 프로그램 동작에 대해 추론하기가 더 어렵습니다.
  4. 표준은 실제로 가장 빠르게 고정되지 않으며 실제로 어떤 유형이 실제로 가장 빠른지도 컨텍스트에 따라 달라질 수 있습니다.
  5. 나는 플랫폼 개발자가 플랫폼을 정의 할 때 이러한 유형의 크기를 고려한다는 증거를 보지 못했습니다. 예를 들어 x86-64 Linux에서 “fast”유형은 x86-64가 32 비트 값에 대한 빠른 작업을위한 하드웨어 지원을 제공하더라도 모두 64 비트입니다.

요약하면 “빠른”유형은 쓸모없는 쓰레기입니다. 특정 응용 프로그램에 대해 어떤 유형이 가장 빠른지 파악해야하는 경우 컴파일러에서 코드를 벤치마킹해야합니다.


답변

정확성과 코딩의 용이성의 관점 에서, 위의 많은 사용자가 지적했듯이 더 정확하게 정의 된 크기와 산술 의미로 인해 특히 uint32_t많은 이점 uint_fast32_t이 있습니다.

아마도 놓친 것은 하나의 장점더 빠를uint_fast32_t 수 있고 의미있는 방식으로 결코 구체화되지 않았다는 것입니다. 64 비트 시대를 장악 한 대부분의 64 비트 프로세서 (대부분 x86-64 및 Aarch64)는 32 비트 아키텍처에서 발전 했으며 64 비트 모드에서도 빠른 32 비트 기본 작업을 수행합니다. 따라서 해당 플랫폼 에서와 동일 합니다.uint_fast32_tuint32_t

POWER, MIPS64, SPARC와 같은 “또한 실행되는”플랫폼 중 일부가 64 비트 ALU 작업 만 제공하더라도 대부분 의 흥미로운 32 비트 작업은 64 비트 레지스터에서 잘 수행 할 수 있습니다. 하위 32 비트는 원하는 결과를 얻을 수 있습니다 (그리고 모든 주류 플랫폼은 적어도 32 비트를로드 / 저장할 수 있습니다). 왼쪽 시프트가 주요 문제이지만 컴파일러의 값 / 범위 추적 최적화를 통해 많은 경우에 최적화 될 수 있습니다.

나는 때때로 약간 느린 왼쪽 시프트 또는 32×32-> 64 곱셈이 그러한 값에 대한 메모리 사용의 두 배 를 능가 할 것이라고 의심합니다 .

마지막으로, 트레이드 오프는 대체로 “메모리 사용 및 벡터화 잠재력”(에 찬성 uint32_t) 대 명령 수 / 속도 (에 찬성 uint_fast32_t) 로 특징 지워졌지만 나에게 분명하지 않습니다. 예, 일부 플랫폼에서는 일부 32 비트 작업에 대한 추가 지침이 필요 하지만 다음과 같은 이유로 몇 가지 지침 도 저장 해야합니다.

  • 더 작은 유형을 사용하면 컴파일러가 하나의 64 비트 연산을 사용하여 두 개의 32 비트 연산을 수행함으로써 인접한 연산을 영리하게 결합 할 수 있습니다. 이러한 유형의 “가난한 사람의 벡터화”의 예는 드물지 않습니다. 예를 들어 상수 struct two32{ uint32_t a, b; }raxlike 로 생성 하는 것은 단일 two32{1, 2} 로 최적화 할 수 있지만mov rax, 0x20001 64 비트 버전은 두 가지 명령이 필요합니다. 원칙적으로 이것은 인접한 산술 연산 (동일한 연산, 다른 피연산자)에도 가능해야하지만 실제로는 보지 못했습니다.
  • “메모리 사용”이 낮 으면 메모리 또는 캐시 풋 프린트가 문제가되지 않더라도 이러한 유형의 구조 또는 배열이 복사되기 때문에 레지스터 복사 당 비용이 두 배가됩니다.
  • 더 작은 데이터 유형은 종종 데이터 구조 데이터를 레지스터에 효율적으로 압축하는 SysV ABI와 같은 더 나은 최신 호출 규칙을 활용합니다. 예를 들어 registers에서 최대 16 바이트 구조를 반환 할 수 있습니다 rdx:rax. 4 개의 uint32_t값 (상수에서 초기화 됨)을 가진 구조를 반환하는 함수의 경우 다음과 같이 변환됩니다.

    ret_constant32():
        movabs  rax, 8589934593
        movabs  rdx, 17179869187
        ret
    

    64 비트 uint_fast32_t가 4 개인 동일한 구조 는 동일한 작업을 수행하기 위해 레지스터 이동과 메모리에 4 개의 저장소 가 필요합니다 (호출자는 반환 후 메모리에서 값을 다시 읽어야합니다).

    ret_constant64():
        mov     rax, rdi
        mov     QWORD PTR [rdi], 1
        mov     QWORD PTR [rdi+8], 2
        mov     QWORD PTR [rdi+16], 3
        mov     QWORD PTR [rdi+24], 4
        ret
    

    마찬가지로 구조 인수를 전달할 때 32 비트 값은 매개 변수에 사용할 수있는 레지스터에 약 2 배 조밀하게 압축되므로 레지스터 인수가 부족하여 스택 1 로 넘쳐 야 할 가능성이 줄어 듭니다 .

  • uint_fast32_t“속도가 중요한”장소에서 사용 하기로 선택하더라도 고정 크기 유형이 필요한 장소도 종종 있습니다. 예를 들어 외부 출력, 외부 입력, ABI의 일부로, 특정 레이아웃이 필요한 구조의 일부로 또는 uint32_t메모리 공간을 절약하기 위해 값의 대규모 집계에 현명하게 사용 하기 때문에 값을 전달할 때 . 사용자 uint_fast32_t와“uint32_t` 유형이 인터페이스해야하는 곳에서 (개발 복잡성 외에도) 불필요한 부호 확장 또는 기타 크기 불일치 관련 코드를 찾을 수 있습니다. 컴파일러는 많은 경우에 이것을 최적화하는 데 OK 작업을 수행하지만 다른 크기의 유형을 혼합 할 때 최적화 된 출력에서 ​​이것을 보는 것은 여전히 ​​드문 일이 아닙니다.

위의 일부 예제와 godbolt에서 더 많은 것을 재생할 수 있습니다 .


1 명확하게 말하면, 구조를 레지스터에 단단히 묶는 규칙이 작은 값에 대해 항상 확실한 승리는 아닙니다. 이는 더 작은 값을 사용하기 전에 “추출”해야 할 수도 있음을 의미합니다. 예를 들어 두 구조체 멤버의 합을 함께 반환하는 간단한 함수 mov rax, rdi; shr rax, 32; add edi, eax는 64 비트 버전의 경우 각 인수가 자체 레지스터를 가져오고 하나의 add또는 lea. 그래도 “통과하는 동안 구조를 단단히 묶는”디자인이 전체적으로 합리적이라는 점을 받아 들인다면 값이 작을수록이 기능을 더 많이 활용할 수 있습니다.