CPU마다 지원하는 instruction set(명령어 집합)이 있는데 특정한 instruction을 사용하기 위해서는 컴파일러 별 내장 함수(intrinsic function, builtin function)을 사용해야 합니다.
예를 들어 SSE4 instruction set에는 특정 수의 1bit 개수를 세는 POPCNT(population count) instruction이 있는데 이걸 사용하려면 gcc에서는 __builtin_popcnt
를, msvc에서는 __popcnt
를 사용해야 합니다. 참고로 msvc에서 intrinsic function을 사용하려면 <intrin.h>
헤더를 포함해야 합니다.
CPU마다 지원하는 instruction set이 다른데 이걸 확인해서 intrinsic function을 사용해야 합니다.
옛 CPU는 intruction set이 추가되지 않아 해당 instruction을 사용할 수 없을 수도 있습니다. 예를 들어 Intel Core 2 Duo E6550은 MMX, SSE, SSE2, SSE3, SSSE3 intruction set이 사용되는데, POPCNT instruction이 없습니다. 개발시 호환성 체크를 잘 해줘야 합니다.
intrinsic function중에서 아래 세 개만 알아보겠습니다.
- population count
- 1비트의 수
- gcc:
__builtin_popcount(unsigned int)
- msvc:
__popcnt(unsigned int)
- count leading zeros
- 앞에서부터(msb부터) 1이 나오기 전 0의 개수
- gcc:
__builtin_clz(unsigned int)
- msvc:
__lzcnt(unsigned int)
- count trailing zeros
- 뒤에서부터(lsb부터) 1이 나오기 전 0의 개수
- gcc:
__builtin_ctz(unsigned int)
- msvc: X
기본적으로 unsigned int
형을 기준으로 계산됩니다.
gcc의 경우 unsigned long
타입의 함수를 사용하려면 함수 끝에 l
을 붙이고, unsigned long long
타입의 함수를 사용하려면 함수 끝에 ll
을 붙입니다.
msvc는 <intrin.h>
헤더를 포함해야 합니다. msvc에 기본적으로 ctz가 없는데 _BitScanForward
을 사용하거나 아키텍처별 intrinsics list를 확인해서 써야 합니다. 제 CPU는 AMD Ryzen 3600인데, BMI를 지원하니 _tzcnt_u32
, _tzcnt_u64
를 쓰면 되겠네요. (ex. x64(amd64) intrinsics list)
* msvc documentation: __lzcnt
If you run code that uses this intrinsic on hardware that does not support the lzcnt instruction, the results are unpredictable.
msvc에서 __lzcnt
를 사용할 때는 주의할 점이 있는데, lzcnt instruction이 지원되지 않는 cpu에서는 bsr이 실행된다고 합니다. microsoft document에는 unpredictable이라고 써있습니다. (참고링크)
아래는 예시입니다
// gcc
unsigned int x = 16; // 00000000 00000000 00000000 00010000
cout << __builtin_popcount(x) << '\n'; // 1
cout << __builtin_clz(x) << '\n'; // 27
cout << __builtin_ctz(x) << '\n'; // 4
// msvc
#include <intrin.h>
unsigned int x = 16; // 00000000 00000000 00000000 00010000
cout << __popcnt(x) << '\n'; // 1
cout << __lzcnt(x) << '\n'; // 27
// count trailing zeros -> _tzcnt_u32, _tzcnt_u64
글이 좀 길어졌는데요, c++20에는 std::popcount
, std::countl_zero
, std::countr_zero
등이 추가될 예정이니 instruction set으로 머리가 아파질 일이 덜어질 것 같습니다.
'프로그래밍 > C++' 카테고리의 다른 글
C++11 implicit narrowing conversion (축소 변환) 방지하는 법 (0) | 2020.06.23 |
---|---|
C++ 배열의 값이 전부 같은지 확인하는 방법 (std::equal) (0) | 2020.06.20 |
C++ memset으로 배열 초기화 시 주의점 (0) | 2020.05.22 |
C++ regex syntax_option_type (0) | 2020.05.08 |
C++ const, constexpr 키워드 차이점 (0) | 2020.05.06 |