LLVM의 PGO(프로파일 기반 최적화)와 FDO(피드백 기반 최적화)는 이름은 다르지만, 이 글에서는 서로 다른 구현을 가리키는 용어로 설명한다. 2023년 7월 기준으로 LLVM에서 쓰이는 주요 방식은 프로파일을 계측으로 수집하는지, 실행 중 샘플링하는지, 최종 바이너리에서 최적화하는지에 따라 구분된다.
프로파일은 계측, 샘플링, 추적으로 수집할 수 있다. 계측은 프로그램에 기록 코드를 넣어 실행 중 데이터를 모은다. 정적 계측은 동적 계측보다 오버헤드가 낮지만, 공유 라이브러리나 생성된 코드를 볼 수 없고 시간에 민감한 프로그램의 동작에도 영향을 줄 수 있다. 샘플링은 프로그램을 수정하지 않고 운영체제나 하드웨어의 기능을 이용한다. 리눅스에서는 perf가 주기적으로 실행을 멈추고 현재 명령 주소나 호출 스택을 기록한다. 일부 CPU는 분기 정보를 추가로 모아 프로파일의 양과 품질을 높인다. 추적은 실행 흐름을 기록해 프로파일을 만들며 유연하지만, 실행 비용이 커서 하드웨어 지원이 있어도 운영 환경에서는 드물게 쓰인다.
LLVM에서 PGO는 대체로 계측 기반 방식을 뜻한다. Clang 프런트엔드 PGO는 소스 수준에서 계측하고 디버그 줄 정보를 이용해 프로파일을 소스에 연결한다. 인라인 처리나 제어 흐름 변환을 충분히 반영하지 못해 정확도가 떨어질 수 있다. 관련 옵션은 -fprofile-instr-generate/-fprofile-instr-use이며, LLVM CMake 옵션은 LLVM_BUILD_INSTRUMENTED다. 자세한 내용은 Clang Compiler User’s Manual에 있다.
IR PGO는 LLVM IR 수준에서 계측한다. 프런트엔드 PGO보다 컴파일 과정 뒤쪽에서 계측하므로 최적화 과정에서 프로파일이 더 잘 보존된다. 다만 계측 단계에서 수행한 변환을 최적화 빌드에서도 반복해야 프로파일을 맞출 수 있다. 옵션은 -fprofile-generate/-fprofile-use이며 CMake 설정은 LLVM_BUILD_INSTRUMENTED=IR, LLVM_ENABLE_IR_PGO다.
CSIR PGO는 인라인 처리 이후에 계측해 복제된 코드의 문맥 정보를 더 잘 반영한다. 대신 계측 빌드와 최적화 빌드에서 인라인 결정이 같아야 프로파일을 맞출 수 있다. 옵션은 -fcs-profile-generate/-fprofile-use이며 CMake 설정은 LLVM_BUILD_INSTRUMENTED=CSIR다. 사용법은 Clang 사용자 안내서에 설명돼 있다. IR PGO와 CSIR PGO를 함께 쓰는 예시도 있다.
CSSPGO(Context-Sensitive Sample PGO)는 샘플링 정보를 계측으로 보완하는 혼합 방식이다. 제안 문서는 LBR과 호출 스택 샘플을 함께 활용해 인라인된 호출과 인라인되지 않은 호출의 문맥을 복원하는 접근을 설명한다. 이 방식은 별도의 사후 인라인 프로파일 없이 입력 프로파일을 인라인 결정에 맞춰 조정하고, 문맥 정보가 풍부한 프로파일로 인라인 결정을 개선하는 것을 목표로 한다. 프로파일 생성과 사용 방법은 각각 LLVM 개발자 메일링 리스트 안내와 사용 안내에 있다.
LLVM에서 FDO는 구글이 처음 GCC용으로 개발한 샘플 기반 최적화 절차인 AutoFDO를 가리킨다. 이름은 LLVM에 이식된 뒤에도 유지됐다. AutoFDO는 샘플 정보를 이용해 프로파일을 IR 수준에 연결하며, 구현은 google/autofdo에서 확인할 수 있다. 사용 방법은 Clang의 샘플링 프로파일러 안내에 있다. FS-AFDO는 샘플 프로파일을 더 낮은 MachineIR 수준에 붙이는 방식이며, 관련 내용은 LLVM 개발자 메일링 리스트와 Clang 사용자 안내서에 나온다.
BOLT와 Propeller는 링크 이후 최종 바이너리를 대상으로 하는 최적화 도구다. 최종 바이너리에서 작동하므로 인라인과 변환이 끝난 코드의 문맥을 활용한다. 글은 CSSPGO의 정확도가 링크 이후 최적화에 쓰는 프로파일보다 낮고, CSIR PGO도 재컴파일이 한 번 더 필요한데도 BOLT가 추가 최적화 기회를 찾을 수 있다고 설명한다. 링크 이후 최적화는 PGO와 함께 쓰거나 CSIR PGO보다 빠른 대안으로 쓸 수 있다.
BOLT는 샘플 프로파일과 계측 프로파일을 모두 지원하며, 프로파일링에 사용한 바이너리에 프로파일을 연결해 바이너리 수준에서 분기와 호출 간 거리를 고려한 배치 최적화를 수행한다. 프로젝트는 LLVM 저장소에 있으며, 관련 연구는 CGO’19 논문과 Lightning BOLT 논문에서 볼 수 있다. Clang을 CMake로 빌드할 때의 사용법은 Advanced Builds에 안내돼 있다. Propeller는 BOLT의 확장성 문제를 다루기 위해 설계된 링크 시점 레이아웃 최적화 도구다. 함수 재배치를 링크 시점에 수행하며, 링커가 섹션만 볼 수 있으므로 함수 분할은 컴파일러에 의존한다. Propeller는 Clang/LLVM에 통합돼 있다. 자세한 내용은 프로젝트 소개를 참고하면 된다.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요