본문으로 건너뛰기
피드

Haskell 바이너리 크기 줄이기 — 링크 타임 최적화 두 가지 전략

backend 약 4분

GHC 9.2.5 환경에서 pandoc 바이너리를 대상으로 -split-sections/--gc-sections와 Identical Code Folding(ICF) 두 가지 링크 타임 기법을 적용해 113M → 64M(-43%)으로 줄인 실험 공유.

  • 1

    -split-sections + --gc-sections로 데드 코드 제거: 113M → 83M (-27%)

  • 2

    lld의 ICF(--icf=all)로 동일 섹션 병합: 83M → 64M (-23% 추가)

  • 3

    ICF로 접힌 120K 섹션 중 절반이 pandoc 단일 ghc 호출에서 발생

  • 4

    -fdistinct-constructor-tables와 ICF가 충돌해 디버깅용 info table이 제거될 수 있음

  • 5

    중복 섹션은 바이너리 크기뿐 아니라 컴파일 시간도 낭비 — GHC 캐싱 개선 여지 있음

Haskell 프로젝트는 의존성이 쌓이다 보면 바이너리가 100MB를 훌쩍 넘기기 일쑤임. GHC 9.2.5 환경에서 pandoc의 test-pandoc 바이너리로 실험한 두 가지 링크 타임 최적화 전략 공유.

전략 1: -split-sections + --gc-sections (데드 코드 제거)

GHC에 -split-sections 옵션을 주면 코드를 개별 섹션 단위로 쪼개서 emit함. 그러면 링커가 실제로 참조되지 않는 섹션(데드 코드)을 쉽게 찾아 제거할 수 있음.

cabal.project 설정:

package *
  ghc-options: -split-sections
  gcc-options: -fdata-sections -ffunction-sections
package pandoc
  ld-options: -fuse-ld=lld -Wl,--gc-sections,--build-id
  • lld를 쓰는 이유는 빠르고, 아래 전략 2도 지원하기 때문
  • 결과: 113M → 83M (-27%) 감소

전략 2: Identical Code Folding (ICF)

gold와 lld 모두 ICF를 지원하는데, 링크 타임에 기능적으로 동일한 섹션을 찾아서 하나로 합쳐버리는 방식임. lld의 구현이 더 효과적이라고 함.

전략 1 설정에 추가:

ld-options: -Wl,--icf=all,--ignore-data-address-equality,--ignore-function-address-equality,--print-icf-sections
  • 결과: 83M → 64M (추가 -23%)
  • 최종 총 감소: 113M → 64M (-43%)

왜 Haskell에서 중복 코드가 많나?

같은 함수가 여러 모듈에 인라인·특수화되면서 결과적으로 동일한 바이트코드가 반복 생성되는 경우가 많음. 실험에서 ICF로 접힌 120K개 섹션 중 절반이 pandoc 자체에서 나왔음 — 단 하나의 ghc 호출에서 비롯된 것.

주의할 점 및 추가 생각

  • -fdistinct-constructor-tables와 충돌: 프로파일링·디버깅용 info table 중복본이 ICF에 의해 제거돼버림. 디버깅 목적으로 쓸 때는 주의 필요.
  • 컴파일 시간 낭비 문제: 이 중복 섹션들은 바이너리 크기만 낭비하는 게 아니라 컴파일 시간(코드 생성, simplifier 등)도 낭비하는 것. GHC가 동일하게 emit될 컴파일 유닛을 캐싱할 수 있다면 빌드 속도도 개선될 여지가 있음.
  • bloaty 툴로 바이너리 구성을 분석하려 했지만 Haskell 코드에서 오동작함.

Haskell 특성상 인라인·특수화로 동일 코드가 여러 모듈에 반복 생성되는 구조적 원인이 있어 ICF 효과가 특히 큼. 단순 옵션 추가만으로 43% 감소는 인상적이며, GHC 컴파일러 레벨의 중복 캐싱 최적화로 이어질 수 있는 흥미로운 방향을 제시함.

댓글

댓글

댓글을 불러오는 중...

backend

Cloudflare가 잡아낸 QUIC CUBIC 버그, ‘idle’ 한 줄 오판이 다운로드를 죽였다

Cloudflare의 QUIC 구현체 quiche에서 CUBIC 혼잡 제어가 최소 윈도우에 갇혀 회복하지 못하는 버그가 발견됐다. Linux 커널의 idle 최적화를 QUIC에 옮기는 과정에서 TCP와 QUIC의 이벤트 타이밍 차이를 놓쳤고, 결국 ACK 시점을 기준으로 idle 시간을 재도록 고쳐 100% 테스트 통과를 회복했다.

backend

삼성전자가 반도체 개발 조직에 오라클 자바를 공식 채택한 이유

삼성전자 DS 부문이 글로벌 반도체 개발 환경에 오라클 자바 SE 유니버설 서브스크립션을 공식 채택했다. 서로 다른 자바 배포판과 버전이 섞이면서 생길 수 있는 보안, 컴플라이언스, 라이선스 리스크를 줄이고 개발 환경을 표준화하려는 결정이다.

backend

네이버클라우드, 트래픽 따라 알아서 줄고 느는 서버리스 데이터베이스 출시

네이버클라우드가 사용량에 따라 CPU, 메모리, 스토리지를 자동 조절하는 완전관리형 서버리스 데이터베이스 서비스를 내놨다. 기존 가상머신 기반 관리형 데이터베이스처럼 피크 트래픽에 맞춰 서버를 과하게 잡아두는 방식에서 벗어나, 사용량 기반 과금과 오토스케일링으로 비용 낭비를 줄이겠다는 방향이다.

backend

네이버클라우드, 사용량 따라 늘고 줄어드는 서버리스 데이터베이스 출시

네이버클라우드가 완전관리형 서버리스 데이터베이스 서비스인 Cloud DB Serverless를 출시했다. VM 기반 관리형 데이터베이스의 고정 비용과 과잉 프로비저닝 문제를 줄이고, 트래픽에 따라 CPU·메모리·스토리지를 자동 조절하는 구조를 내세운다.

backend

네이버클라우드, 사용량 따라 자동 확장되는 서버리스 데이터베이스 출시

네이버클라우드가 사용량에 따라 컴퓨팅 자원을 자동 조절하는 서버리스 기반 클라우드 데이터베이스를 출시했음. 기존 가상머신 기반 관리형 데이터베이스의 고정 비용과 운영 부담을 줄이고, 국내 데이터 규제 요구까지 맞추겠다는 전략임.