실시간 C/C++/Rust 빌드 시각화 도구를 만들었음
(danielchasehooper.com)- What the Fork은 실시간으로 C/C++/Rust 등 다양한 빌드 과정을 시각화하는 크로스플랫폼 도구임
- 기존 빌드 시스템의 병렬 처리 부족, 비효율적 프로세스 등 구조적 문제를 쉽게 파악할 수 있음
- 모든 빌드 시스템 및 프로그래밍 언어에서 동작하며, make, ninja, gradle, zig, cargo 등 다양한 빌드 툴 지원 가능함
- 시스템 호출 모니터링을 통해 각 프로세스의 실행 시간, 명령어, 종속 관계를 박스 형태로 시각화함
- 빌드 최적화, 병목 현상 분석, CI 성능 개선 등에 매우 유용한 도구임
소개 및 배경
- What the Fork은 빌드가 느려지는 원인을 시각적으로 진단하기 위해 개발된 실시간 빌드 시각화 도구임
- LLVM 프로젝트처럼 코드량 자체가 많아서 컴파일 속도가 느릴 수 있으나, 대부분의 빌드는 비효율적인 설정 탓에 불필요하게 오래 걸리는 경우가 많음
- 기존에는 빌드의 문제점을 직접 확인하거나 구조적 문제를 한눈에 보기 어려웠기 때문에 이런 도구가 필요했음
- 이 도구는 크로스플랫폼으로 설계되었고, 모든 빌드 시스템과 언어에 적용 가능함
주요 기능 및 사용법
- What the Fork은 단순한 시스템 프로파일러가 아닌, 빌드 특화 문제를 진단하는 도구임
- 예시로는 make 사용 시
-j
플래그 미사용, 특정 파일 혹은 컴파일 단계에서의 시간 쏠림, 병렬 처리가 가능한데도 순차적으로 실행되는 명령어 탐지 등이 있음 - 특히 CI 환경의 clean build 성능 분석 및 최적화에 효과적임
- 사용 방법은
wtf
명령어를 빌드 명령 앞에 붙여 실행 (예:wtf make
,wtf cargo build
,wtf npm run build
등) - 빌드가 시작되면 UI가 실행되어 각 프로세스의 진행 상황을 실시간으로 갱신함
UI 및 시각화 방식
- 각 빌드 프로세스는 박스 형태로 타임라인 상에 표시되며, 색상으로 유형 구분
- 프로세스의 부모-자식 관계는 중첩 구조로 표현
- 하단 패널에서는 선택된 프로세스의 실행 시간, 작업 디렉토리, 전체 명령어 인자 정보를 표시
작동 원리
- 빌드는 여러 프로세스(예:
bash
,clang
,ld
)의 조합임 - 대규모 빌드는
cargo
,make
,bazel
,gradle
,xcodebuild
등 다양한 빌드 도구를 사용하며, 이들은 실제로 많은 명령어와 종속성, 캐시, 스케줄링 작업을 수행함 - 터미널 출력만으로는 중첩된 프로세스(예: clang이 내부적으로 호출하는 ld 등)와 세부 타이밍 구조를 파악 불가
- 이를 위해 OS별 프로세스 시작, 종료를 감지하는 시스템 호출(macOS: Endpoint Security API, Linux: ptrace(), Windows: Event Tracing for Windows 등)을 활용함
- 이런 방식으로 빌드 전 과정 및 타임라인 복원, 각 단계의 실행 경로와 시간 식별 가능함
- 빌드 외에도 다양한 서브프로세스 트래킹에 활용할 수 있음
실제 사례 및 관찰 결과
- 여러 엔지니어(Delta, Mozilla, Apple 소속)가 실제로 프로젝트에 적용 후 예상치 못한 이슈를 발견함
- 예시 1: Cargo를 사용하는 오픈소스 프로젝트에서 파일들이 순차적으로 컴파일되어 병렬성 부족 확인 (10코어 CPU에서 10배 이상 속도 개선 가능성 확인)
- 예시 2: Ninja를 활용한 LLVM 빌드에서는 모든 CPU 코어가 효율적으로 병렬 작업을 수행, 이상적인 빌드 효율 달성
- 예시 3: CMake 기반 프로젝트에서 cmake/make/clang의 중첩 실행 및 Xcode/OS 버전 재확인이 85회 반복되는 비효율적 구조 발견(실제 작업은 극히 일부)
- 예시 4: xcodebuild를 이용한 대형 Objective-C 프로젝트에서 빌드 후반부 병렬 처리 부족 및 빌드 시작 전 6초간 비활성 상태 존재(비교적 ninja는 0.4초 후 바로 컴파일 시작)
- 예시 5: Zig가 Orca Project를 컴파일할 때 종속성 빌드 순서가 무작위로 정해져, 운에 따라 병렬 처리 효율이 바뀜. 일부 종속성이 마지막에 실행되어 병렬성 저하되는 현상 관찰
- 예시 6: make/go를 활용한 GitHub CLI 프로젝트에서 종속성 다운로드 시간이 큼. 종속성 축소 시 빌드 속도 개선 기대
활용 효과 및 한계
- 시각적 타임라인 분석을 통해 예상치 못한 병목 구간, 불필요한 의존성 반복, 병렬성이 부족한 영역 확인 가능
- 종속성 문제, 불필요한 재작업, 특정 툴의 비효율 등 구조적 개선점을 빠르게 파악하여 빌드 성능 최적화에 직접적 활용 가능
- 프로세스의 전체 명령어 확인으로 더 세밀한 분석 가능
베타 프로그램
- What the Fork은 Windows, Linux, macOS에서 동작
- 피드백을 원하는 개인 및 팀은 프라이빗 베타 신청 가능 (구글 폼 링크 제공)
Hacker News 의견
-
현재 CMake, GCC, Unix Make만 사용 가능한 환경에 갇혀 있어서 빌드가 왜 느린지에 대한 상세 정보를 얻는 게 거의 불가능함, 소스에서 빌드 폴더로 파일 복사 같은 복잡한 빌드 단계, 여러 언어(C, C++, Fortran, Python), 커스텀 CMake 스텝 등으로 엉망인 빌드임, 만약 이 툴이 이런 혼란스러운 환경에서도 잘 동작한다면 정말 많은 것을 배울 수 있을 것 같음
-
tsoding이 https://github.com/tsoding/nob.h에서 크로스 플랫폼 빌드를 위한 싱글 헤더 C 라이브러리를 작성했음, 오직 cc만 필요함, GDB 프로파일링 툴을 사용하여 빌드 단계를 살펴볼 수 있음, 멋진 아이디어라고 생각함, 아마 이 글쓴이에게는 맞지 않을 수도 있지만, 여러 언어를 다뤄야 한다면 Nix가 훌륭한 빌드 툴임
-
내가 GCC 플러그인으로 컴파일 시간 트레이싱/프로파일링 도구를 직접 만들었음, 혹시 관심 있다면 참고: https://github.com/royjacobson/externis
-
내 게임 엔진의 컴파일 시간을 줄여보려고 시도할 때, 컴파일된 결과물의 크기를 대체 척도로 썼었음, 워낙 벽시계 기준 시간이 불안정해서 빌드마다, 심지어 다른 머신에서도 결과가 동일한 바이너리 크기 측정이 오히려 다루기 더 쉬웠음, 100% 일치하지는 않지만 실질적으로 도움이 되었음
-
비슷한 문제를 겪고 있음, 종종 CMake가 수정하지도 않은 파일까지 다시 컴파일하는 걸 본 경험이 있음, 예를 들어 인터페이스 변경 없이 .cpp를 조금만 고쳐도 전혀 독립적인 오브젝트까지 재컴파일 되는 현상임, CMake가 실제 파일들보다 더 강하게 종속성을 만드는 게 아닌지 가끔 궁금해짐, 그래서 빌드 시간이 괜히 길어지는 것 같다는 의문 있음
-
-
블로그 작성자에게 제안하겠음, macOS 앱 빌드를 기록한 gif 이미지를 페이지 상단, 헤더 바로 아래에 보여주면 좋겠음, 만든 결과물을 먼저 보여주고 그 뒤에 설명을 붙이는 흐름이 보기 좋음
- 좋은 제안임, 바로 반영해서 블로그 업데이트했음
-
이 프로젝트 정말 마음에 듦, 비슷한 시도를 2018년에 strace와 dtruss, https://buildinfer.loopperfect.com/를 써서 BUCK 파일 자동 생성 같은 일을 해봤음, graphviz, perfetto.dev 등으로 시각화함, 정식 제품으로 패키징하지 못한 게 아쉽지만 컨설팅에서 원인 진단과 BUCK/Bazel 전환에 큰 도움 받았음, 최근에도 더 넓은 적용을 고민하며 다시 돌아보고 있음, 이 방식에 본질적인 기술적 도전 과제도 있긴 함: 시스템콜 로그가 디스크에 기록되면 수십~수백 GB 정도 크기가 커짐(예: llvm은 50GB, 어떤 경우 100GB 넘음), https, IPC 같은 빌드 단계까지 전부 잘 처리해야 함(예전에 어떤 고객은 Perl로 Firebird DB에서 코드를 빌드할 때마다 끌어오기도 했음), 런타임 분석이라는 특성 때문에 빌드 설정마다 분석을 반복해야 한다는 점도 있음
- 궁금해서 묻겠음, 시스템콜 로깅은 어떻게 했음? LD_PRELOAD 같은 트릭이나 eBPF 필터링 썼는지 궁금함
-
멋지다고 생각함, 이런 시각화 없어서 놓치는 문제들이 정말 많다고 느꼈음, 10년 전에 Mozilla 빌드 시스템 최적화할 때 이런 툴이 있었으면 정말 도움이 됐을 것 같음, 글에서는 실제로 어떤 문제를 찾았는지 좀 더 알려줬으면 더 좋았을 것 같음
- (작성자) 고마움, Mozilla 엔지니어와의 통화가 갑자기 끝나서 어떤 문제 찾았는지 자세하게 듣지는 못했음, 직접 확인해보고 싶음
-
CMake로 관리하는 C++ 프로젝트에서 빌드 퍼포먼스 시각화에 ninjatracing과 Clang의
-ftime-trace
를 성공적으로 사용해본 경험 있음, 추가로 ClangBuildAnalyzer를 쓰면 컴파일러가 어디서 시간을 쓰는지 더 세밀히 분석할 수 있음 -
정말 멋지다고 생각함, 혹시 오픈소스 계획이 있는지 궁금함, 비슷한 걸 만들고 있어서 협력하고 싶음
-
Windows에서 Visual C++ 컴파일러를 사용한다면 vcperf도 추천함, VS2022에 기본 포함되어 있거나 깃허브에서 직접 빌드해서 사용할 수 있음, UBT나 CMake로 생성된 프로젝트에도 적용해봤음, 빌드 병렬화 품질을 직접 판별할 수 있는지는 기억 안 나지만, 컴파일러 프론트엔드 정보를 쉽게 볼 수 있음, 특히 자주 포함되거나 본질적으로 무거운 헤더 파일을 쉽게 찾을 수 있음
- Incredibuild도 추천함, 무료 버전만으로도 빌드를 시각화해서 병목 구간을 파악하기에 충분함
-
일반적으로 간과되지만, 빌드 시스템의 “baked in”된(사전 계산된) 빌드 로직이 실제로 어떤 변경에 영향을 받는지 세밀하게 추적되지 않는다는 점이 중요한 관찰임, 예를 들어 ninja에서는 일부 빌드 로직이 미리 입력되어 있어서 속도가 빠름, 나는 Xerces-C++를 ninja(CMake로 설정)와 build2(설정 및 변경 추적을 빌드에서 다루는 툴)로 각각 풀빌드 벤치마크를 했는데 ninja는 3.23초, build2는 3.54초임, CMake가 만드는 파일 일부를 유지한 채로 반복해서 빌드하면 3.28초로 다운됨, 참고로 CMake만의 설정 단계는 4.83초임, 풀스택의 CMake+ninja 빌드는 실제로 8초 정도 소요됨, 보통 이걸 라이브러리로 쓸 때 실제로 겪게 되는 시간임
- kbuild가 Make에서 각 타겟을 더미 파일에 의존하도록 해 CFLAGS같은 옵션 변경 시 제대로 반영함, Make를 ninja처럼 쓰기 위해 전체 빌드 그래프를 각 Make 프로세스에 안 넣도록 설계되어 있음, 실제로 어떻게 비교될지 궁금함
-
Instruments를 빌드하는 동안 실행해서 어떤 프로세스가 언제 뭘 하는지 파악하는 식으로 비슷한 시도를 한 경험 있음, 단점은 빌드가 오래 걸리면 Instruments가 버벅임, 프로세스 트리 필터링이 안돼서 불편하지만 Twitter의 iOS 코드 빌드 시간 대폭 줄일 때 큰 도움이 됐음, 최근에는 Instruments의 “All Processes” 트레이싱이 깨져서 더 이상 이 방법이 불가능해진 상태임
-
정말 멋지게 느껴짐, 혹시 지금 바로 테스트할 수 있는 macOS 버전 있는지 궁금함, Rust나 C++/Swift 작업에도 써보고 싶음
-
버그 픽스 후에 다음 베타 테스트 유저로 macOS 버전을 배포 계획 없음, (아티클 하단에서) 신청하면서 이 댓글 언급해주면 꼭 베타 그룹에 포함해주겠음
-
아직 어떤 OS도 퍼블릭 릴리즈는 없는 것 같고, 얼리 억세스 신청만 가능한 상태임
-