2P by GN⁺ 9일전 | ★ favorite | 댓글 1개
  • Zig 0.15.1 출시로 인해 컴파일 속도가 전버전 대비 크게 개선됨
  • Ghostty 프로젝트에서 실제 빌드 시간을 측정한 결과, 전반적으로 구동 시간이 단축
  • 아직 LLVM을 일부 사용하고 있지만, 자체 백엔드 적용시 추가적인 속도 향상 기대감이 높음
  • 점증적 컴파일(incremental compilation)은 아직 완전히 구현되지 않았으나, 부분 빌드에서도 성능 이점이 나타남
  • 앞으로 더욱 빠른 빌드 환경과 향상된 개발 경험이 실현될 가능성이 높음

개요

Andrew Kelley의 발언인 "컴파일러가 너무 느려서 버그가 생김"에서 시작되어, Zig는 수년간 더 빠른 컴파일 시간을 목표로 다양한 구조적 개선 노력을 진행했음

  • Zig 팀은 LLVM 제거, 자체 코드 생성 백엔드 개발, 독자적인 링커 구축, 그리고 궁극적으로 점증적 컴파일 실현을 위해 노력해왔음
  • 이러한 장기적인 개발의 성과가 Zig 0.15.1 버전에서 가시적으로 드러나기 시작했고, 실제 프로젝트(Ghostty)에서 빌드 시간의 변화를 측정해 공유함

Build Script 컴파일 속도

  • Zig 0.14: 7초 167ms
  • Zig 0.15: 1초 702ms

build.zig 스크립트 자체의 빌드 시간이며, 이 시간은 신규 소스 빌드 환경에서 매번 부담하게 되는 초기 비용임

  • 빌드 스크립트 재컴파일 빈도는 낮게 유지되지만, 사용자가 처음 프로젝트를 직접 빌드하는 경험에는 직접적인 영향을 미침

전체 미캐시 바이너리 빌드(Ghostty)

  • Zig 0.14: 41초
  • Zig 0.15: 32초

빌드 스크립트 빌드 시간까지 포함한 전체 바이너리 빌드 타임임

  • Zig 0.15로 2초가량의 추가 속도 개선효과가 있으며, 실제 벽시계 시간 기준으로도 명확한 초기 차이가 있음
  • 아직 자체 x86_64 백엔드는 완전히 활용되지 못하고 있고, 대부분 LLVM을 계속 사용하는 상황임
  • 향후 Ghostty가 완전히 자체 백엔드로 빌드되면 25초 이하까지 시간 단축될 것으로 예측됨(기존 대비 절반 수준)

점증적 빌드(Ghostty 실행 파일)

  • Zig 0.14: 19초
  • Zig 0.15: 16초

의미 있는 한 줄의 변경(터미널 이뮤레이션 코드의 로그 호출 추가) 이후 다시 빌드하는데 소요되는 시간임

  • 빌드 스크립트와 의존성 그래프는 이미 캐시 상태인 상황에서 부분 빌드 임
  • 점증적 컴파일 기능이 아직 완벽하게 구현된 것은 아니나, 이미 성능 개선이 뚜렷하게 나타남
  • 사용 중인 LLVM을 제외하면 12초 가량까지 단축 가능성 있음
  • 향후 진정한 점증적 빌드가 구현되면, 밀리초 단위의 빌드 실현 가능성도 기대함

점증적 빌드(libghostty-vt)

  • Zig 0.14: 2초 884ms
  • Zig 0.15: 975ms

한 줄의 변경 이후 libghostty-vt만 부분적으로 다시 빌드한 시간 측정

  • libghostty-vt자체 x86_64 백엔드로 완전 빌드가 가능하므로, LLVM의 영향 없이 Zig의 개선점이 직접 반영됨
  • 점증적 컴파일이 아니어도 1초 미만의 빌드 시간 달성은 상당한 진전임
  • 개발자 워크플로에서 즉각적인 피드백 경험으로 효율성 향상
  • x86_64와 aarch64 백엔드가 점점 안정화되고 있으며, 수개월 내 Ghostty 전체에 적용될 가능성 있음

빌드 속도 개선의 현주소

  • Zig 0.15.1을 사용한 Ghostty 빌드는 모든 측정 구간에서 명확히 더 빨라진 현황임
  • 자체 백엔드와 점증적 컴파일이 아직 미완성임에도 현재의 성과 자체만으로도 충분히 인상적임
  • 앞으로 1~2년 내 더욱 혁신적인 속도 개선 결과가 기대되는 상황임
  • Zig를 선택한 것이 빌드 속도의 관점에서 합리적임을 실감하게 됨
Hacker News 의견
  • 1995년에 고등학교를 졸업하면서 Intel 486에서 "Borland Pascal 버전 Turbo Vision for DOS"를 재컴파일하던 속도와 맞먹을 정도로 이젠 정말 빠른 컴파일 속도를 경험함
    Turbo Vision은 TUI 윈도우 프레임워크로, Borland Pascal과 C++ IDE 개발에 사용함
    JetBrains IDE를 1000MB 대신 10MB로 구현한 캐릭터 모드라 할 만함
    Turbo Vision 위키피디아

  • LLVM은 일종의 함정임
    부트스트랩이 정말 빠르고 온갖 최적화 패스와 다양한 플랫폼 지원을 공짜로 얻지만, 마지막 최적화 단계나 링킹 단계의 성능을 세밀하게 조정하는 능력을 잃게 됨
    Cranelift가 Rust에서 곧 활성화될 것이라고 생각함
    하지만 Rust가 초기에는 LLVM을 선택했기에 지금의 위상을 갖게 된 것임
    Go는 코드 생성과 링크를 외부에 맡기지 않고 자체 관리하기로 예전부터 결정했고, 그 선택 덕을 톡톡히 보고 있음

    • LLVM이 함정이라는 주장에 동의하기 어렵고, Rust가 오히려 좋은 사례임
      실제로 LLVM으로 코드 생성하는 부분은 컴파일러에서 매우 적은 부분을 차지하고, 대체하려면 codegen_cranelift나 codegen_gcc 등으로 바꿀 수도 있음
      SIMD 벤더 인트린식 의존성이 ‘락인’ 문제이긴 하나, 이는 언어 구조의 문제임
      대부분의 언어는 LLVM 백엔드로 시작하는 게 합리적임
      C/C++과 유사한 언어엔 LLVM 기본 파이프라인만으로도 최적화가 잘 되지만, 특징이 다른 언어일수록 직접 최적화 파이프라인을 작성함
      Go처럼 초기부터 자체 백엔드를 통합해온 사례가 성공적으로 보이긴 하지만, 특별한 차별화 요소가 되는 건 아니고, 직접 구현에는 꽤 많은 기회비용이 따름

    • Go와 Ocaml 컴파일러는 정말 빠름
      처음부터 자체 라이브러리를 제대로 구축했고, 이제는 속도 측면에서 손해를 볼 일이 없음
      앞으로 1분 이상 걸리는 컴파일 환경에서는 일하고 싶지 않음
      프로젝트마다 ‘dev’ 전용 컴파일러를 두고, 최종 빌드에만 llvm 같은 무거운 걸 사용했으면 함

    • LLVM 기반 언어가 C++ 대체를 목표로 한다면, 여전히 C++에 의존하게 됨
      언어는 자기 자신만으로 부트스트랩해야 함
      초기에는 편리한 도구들이 있지만, 단지 시간절약 수단이고 필수품은 아님
      Go 팀이 내린 선택에 완전히 공감함

    • Cranelift의 지속적인 성장을 바람
      지금의 LLVM은 CPU 벤더별로 포크가 쏟아져나와, 각각이 비공개 패키지에 CPU별 개선이 묶여 있음
      다른 언어 프론트엔드를 쓰거나 컴파일러 버그가 나오면 매우 험난한 상황임

    • 언어들이 자유롭게 다른 백엔드로 이동할 수 있는데 어떻게 함정일 수 있냐는 의문 제기함

  • 컴파일 속도가 개발을 방해한다면, 왜 인터프리터를 만들지 않느냐는 의문
    실행 속도와 컴파일 속도가 필연적으로 독립적 관계임
    인터프리터를 쓰면 코드 인스트루먼트나 런타임 제어 등 추가적인 개발 도구도 쉽게 만들 수 있음
    최적화된 RELEASE 바이너리만 디버깅해야 하는 극소수 케이스가 있지만, 대부분의 경우에는 인터프리터나 DEBUG 빌드로 충분함

    • Rust는 안전성, 성능, 사용성 순이고 Zig는 성능, 사용성, 안전성 순으로 분류된다고 들음
      이런 관점에서 빌드 속도 개선은 설득력이 있지만, 인터프리터는 사용성이 우선일 때 더 적합한 대안임

    • Julia의 접근이 마음에 듦
      풀 인터랙티브 환경에서 인터프리터가 실은 코드를 컴파일 후 바로 실행함
      SBCL 같은 Common Lisp 환경도 마찬가지임

    • 실행 속도와 컴파일 속도는 극단적으로 보면 독립적임
      그 사이 "타협 가능한 영역"이 있어서, 실행 파일의 성능 손실 없이 컴파일러를 더 빠르게 만들 수도 있음

    • 게임 분야는 특별한 경우가 아니라고 주장함

  • 지금까지 최고의 컴파일러 속도는 TCC (Fabrice Bellard 작품)임
    멀티스레드나 복잡한 최적화 없이도 압도적으로 빠름
    릴리즈에는 Clang을 쓰지만 TCC의 코드 생성 성능도 나쁘지 않음

    • Delphi가 훨씬 더 표현력 있고 안전하면서도 매우 빠른 컴파일 속도를 제공한다고 생각함

    • Go 컴파일러도 꽤 빠름

    • DMD가 C와 D 모두 컴파일할 수 있으면서도 "골드 스탠다드"라 생각했음

    • TCC가 C23을 지원해줬으면 좋겠다고 바람을 밝힘

    • 한 가지가 "진짜 골드 스탠다드"란 건 없음
      vlang도 재컴파일이 몇 초면 끝날 정도로 빠르고, Go의 컴파일러 역시 굉장히 빠름
      빌드 캐싱 등 재컴파일 자체를 아예 피하는 기술도 있기에, 누구만의 전유물이 아님

  • zig로 앱을 빌드하는 과정에서 인크리멘탈 빌드에 만족도가 컸음
    SQLite, luau 등 다양한 라이브러리를 쓰는 단일 정적 바이너리를 Go만큼 빨리 빌드 가능함
    다만 아직까지 self-hosted 컴파일러에는 꽤 버그가 남아 있음
    예로 SQLite는 llvm을 써야 하며, 관련 이슈는 여기에서 확인 가능함

  • Zig가 Bazel, Buck2 같은 빌드 시스템과 잘 연계될 수 있는지 궁금함
    Zig는 빌드 스크립트가 튜링 완전해서, 이런 시스템에서는 캐싱과 빌드 자동화가 쉽지 않을까 걱정임
    Rust에서 build.rs 없이 쓸 수 있는 라이브러리가 더 선호되는 이유와 같음
    Zig 라이브러리도 커스텀 빌드가 많은지 궁금함

    • Zig의 빌드 스크립트는 완전히 옵션임
      build.zig 없이도 개별 소스 파일을 바로 빌드/실행 가능함
      GCC나 Clang이 들어가는 워크플로우 어디에나 Zig를 연동할 수 있음
      참고로 Zig는 C 컴파일러 대체품으로도 동작함
      관련 글

    • Bazel과 Zig 연동 예시로 rules_zig 소개
      실제 프로젝트 ZML도 이를 활용함
      ZML 프로젝트

  • Zig의 컴파일 및 코드 생성이 TPDE와 비교해 어떤지 궁금함
    LLVM -O0 보다 10~20배 빠르다고 하지만 한계는 있는 듯함

  • Zig의 전략은 대담하다고 봄
    하지만 아직까지 LLVM 백엔드를 쓰는 게 맞는지 궁금함
    LLVM이 컴파일 속도와 플랫폼 지원 면에선 경쟁력이 있지만, 최적 기계어 생산에서는 따라올 곳이 없음

    • Release 빌드에만 LLVM 백엔드를 쓰고, debug 빌드는 지원되는 플랫폼에 한해 self-hosted 방식이 기본임
      디버그 빌드는 실제 테스트 과정에서 여러 번 반복 빌드하기 때문에 이 방식이 더 합리적임

    • 컴파일러 성능 집착에는 공감 못함
      결국 최적화 패스(인라이닝, dead code 제거 등)와 컴파일 시간 중 하나를 선택하는 트레이드 오프임
      최적화 없는 컴파일러는 선형적으로 빨라질 뿐이며, 그 이상 최적화를 하려면 언제나 시간 소모가 커질 수밖에 없음

    • "뛰어난 어셈블리 출력"은 실제로 중요한 이슈는 아님
      Proebsting's Law에 따르면 컴파일러 기술 발전이 기계 성능 상승보다 훨씬 느림
      쉽고 빠른 최적화만으로도 실용적으로 충분하다는 결론임
      LLVM이 절대적 최적화도, 컴파일 속도와 비교했을 때 금방 한계에 부딪히게 됨

  • JNI로 C 라이브러리를 감싸는 Java 라이브러리를 만드는 데, 플랫폼별 다이나믹 라이브러리 빌드가 너무 번거로워서 zig로 멀티 플랫폼 빌드를 고민 중임

    • 단순 shim이라면 최신 Java에서 Panama와 jextract 활용이 더 나을 것임

    • Zig는 헤더, libc 소스, 자체 LLVM을 모두 내장하며 크로스 컴파일이 정말 쉬움
      정말 아무것도 신경 안 써도 될 정도임

  • Ghostty가 지금 self-hosted x86_64 백엔드로 빌드가 안 되는 이유를 궁금해함

    • Zig 컴파일러가 버그로 크래시 남
      아직 신기술이라 복잡하지만 곧 해결될 것임

    • 대부분 Ghostty 유저가 aarch64 플랫폼임