성능 최적화
1. 메모리 정렬, 캐시 친화적 설계
성능 최적화의 중요한 요소 중 하나는 메모리 접근 패턴을 최적화하여 CPU 캐시 효율을 높이는 것입니다. 캐시 친화적인 설계를 통해 데이터를 메모리에서 효율적으로 접근하고, 프로세서 캐시를 효과적으로 사용할 수 있습니다.
1.1. 메모리 정렬
메모리 정렬을 통해 데이터가 연속적으로 메모리에 배치되도록 하여 캐시 히트를 극대화할 수 있습니다. 예를 들어, 구조체의 멤버를 정렬하여 캐시 성능을 향상시킬 수 있습니다.
#include <iostream>
struct alignas(64) AlignedStruct {
int a;
int b;
};
int main() {
AlignedStruct data;
std::cout << "Aligned structure size: " << sizeof(data) << std::endl;
return 0;
}
1.2. 캐시 친화적 설계
캐시 친화적인 설계를 위해서는 데이터를 메모리 상에 근접하게 배치하고, 데이터 접근 시 연속적인 메모리 블록을 읽는 것이 좋습니다. 예를 들어, 다차원 배열을 처리할 때 행 우선 접근(row-major access)을 활용할 수 있습니다.
2. inlining, loop unrolling
inlining과 loop unrolling은 코드 실행 속도를 향상시키기 위한 두 가지 대표적인 기법입니다.
2.1. inlining
inlining은 함수 호출을 함수 본문으로 대체하는 최적화 기법입니다. 이 방법은 함수 호출 오버헤드를 줄이고, 코드 크기를 늘릴 수 있지만 성능 향상에 기여할 수 있습니다.
#include <iostream>
inline int add(int a, int b) {
return a + b;
}
int main() {
std::cout << add(5, 3) << std::endl; // 함수 호출 대신 인라인 코드 삽입
return 0;
}
2.2. Loop Unrolling
loop unrolling은 반복문에서 반복 횟수를 줄이기 위해 루프 본문을 여러 번 복사하여 성능을 개선하는 기법입니다. 이 방법은 반복문 실행 횟수를 줄여, CPU 파이프라인에서 더 효율적인 실행을 도와줍니다.
#include <iostream>
int main() {
int sum = 0;
for (int i = 0; i < 1000; i+=4) { // Loop Unrolling
sum += i;
sum += i+1;
sum += i+2;
sum += i+3;
}
std::cout << "Sum: " << sum << std::endl;
return 0;
}
3. 프로파일링 도구 사용
성능 최적화는 측정과 분석을 통해 이루어집니다. 프로파일링 도구는 프로그램의 실행 성능을 분석하고, 병목 지점을 찾아 최적화를 돕습니다.
3.1. gprof
gprof는 프로그램의 실행 시간을 분석하고, 각 함수의 호출 횟수와 시간을 측정하여 최적화할 수 있는 부분을 찾는 도구입니다.
g++ -pg my_program.cpp -o my_program
./my_program
gprof my_program gmon.out > analysis.txt
3.2. Valgrind
Valgrind는 메모리 관리 및 성능 분석 도구로, 메모리 누수나 잘못된 메모리 접근을 감지하고 최적화를 위한 통찰을 제공합니다.
valgrind --tool=callgrind ./my_program
3.3. 기타 도구
그 외에도 perf, Intel VTune Profiler, Google Benchmark 등 다양한 프로파일링 도구를 사용하여 성능을 분석하고 최적화할 수 있습니다.