C/C++ 코드, CPU 아키텍처에 맞춰 속도 2배 빠르게!
CPU 아키텍처(CPU Architecture)에 따라 C/C++ 소프트웨어 성능을 최적화하는 다양한 기법 소개
컴파일러(Compiler)를 활용하여 특정 CPU에 맞게 코드를 빌드하거나, IFUNC를 사용하여 런타임에 최적의 함수 선택
인스트린식(Intrinsics)을 직접 사용하여 AVX2와 같은 명령어 집합을 활용하는 방법 제시
GCC/Clang 컴파일러의 `target_clones` 속성을 활용하여 여러 버전의 함수를 생성하고, 런타임에 적합한 버전을 선택하는 방법 설명
IFUNC를 활용한 런타임 최적화
저자는 IFUNC(Indirect Function)를 사용하여 런타임에 CPU의 기능을 감지하고, 최적의 함수 버전을 선택하는 방법을 제시한다. 특히, `__attribute__((ifunc("resolve_my_func")))` 구문을 통해 컴파일러가 링킹 시점에 적절한 함수를 선택하도록 한다. 이를 통해, 개발자는 CPU 아키텍처(CPU Architecture)의 변화에 유연하게 대응하며, 성능 저하(Performance Degradation) 없이 최적의 코드를 실행할 수 있다. 또한, MUSL libc는 IFUNC를 아직 지원하지 않는다는 점을 언급하며, 플랫폼별 호환성 문제를 강조한다.
컴파일러 최적화 옵션 활용
게시물에서는 컴파일러의 `-march=native` 옵션을 사용하여 특정 CPU 마이크로아키텍처에 맞게 코드를 최적화하는 방법을 소개한다. 또한, `target_clones` 속성을 사용하여 여러 버전의 함수를 생성하고, 런타임에 적합한 버전을 선택하는 방법을 설명한다. 이러한 기법은 개발자가 다양한 CPU 환경(CPU Environment)에서 최적의 성능을 유지하면서, 코드의 이식성(Code Portability)을 확보하는 데 도움을 준다. 특히, C23 표준의 `[[gnu::target_clones("avx2,default")]]` 구문을 통해 간결하게 구현할 수 있음을 강조한다.
인스트린식(Intrinsics)을 이용한 수동 최적화
저자는 인스트린식(Intrinsics)을 사용하여 AVX2와 같은 특정 명령어 집합을 직접 활용하는 방법을 설명한다. 이를 위해, `#ifdef __AVX2__`와 같은 전처리기 지시어를 사용하여 컴파일러가 지원하는 기능을 확인하고, 해당 기능을 사용하는 코드를 작성한다. 이러한 방식은 개발자가 세밀한 제어(Fine-grained Control)를 통해 성능을 극대화할 수 있게 해주지만, 코드의 복잡성(Code Complexity)을 증가시키고, 유지 보수(Maintenance)를 어렵게 만들 수 있다. 또한, GCC와 Clang 컴파일러에서 `pragma` 지시어를 사용하여 인스트린식을 활성화하는 방법을 제시한다.
x86-64 마이크로아키텍처 레벨
게시물에서는 x86-64 아키텍처의 마이크로아키텍처 레벨(Microarchitecture Levels)을 소개하며, 각 레벨이 지원하는 명령어 집합을 설명한다. x86-64-v1부터 x86-64-v4까지의 레벨을 제시하며, 각 레벨이 지원하는 기능(예: POPCNT, SSE4.2, AVX2, AVX-512)을 구체적으로 언급한다. 이러한 정보는 개발자가 타겟 CPU(Target CPU)의 기능을 파악하고, 최적화 전략(Optimization Strategy)을 수립하는 데 도움을 준다. 또한, 인텔과 AMD의 CPU 간의 기능 차이와, 특정 기능의 느린 구현(예: AMD의 BMI2)에 대한 주의를 강조한다.