투명한 원통 안 위쪽에 녹색·주황색 원판이 여러 겹 쌓이고 아래쪽에는 두 장만 남은 일러스트로, 옆에 바늘 계기판과 자물쇠, 메모 카드가 놓여 빌드 대상 패키지를 줄이고 플래그를 고정하는 과정을 나타낸다

colcon build가 너무 느릴 때: overlay·COLCON_IGNORE·–symlink-install로 다시 빌드할 패키지를 줄이는 순서 계산하기


Read in English

코드를 한 줄 고쳤을 뿐인데 colcon build가 한참 돌고, 아무것도 바꾸지 않고 다시 실행해도 시간이 걸린다. Robotics Stack Exchange에는 2023년 11월 “catkin이나 cmake보다 눈에 띄게 느린데 어떻게 빠르게 하고, 원인은 어떻게 찾느냐”는 질문이 올라왔고, 2026년 10월 4일 확인 시점까지 채택된 답이 없다. colcon build가 너무 느릴 때 overlay·COLCON_IGNORE·--symlink-install 중 무엇부터 손댈지 고르려면, 옵션을 하나씩 켜 보기 전에 이번 실행에서 무엇이 다시 도는지부터 세어 봐야 한다.

계산의 뼈대는 colcon이 선택된 패키지마다 빌드 작업을 되풀이한다는 사실이다. overlay와 COLCON_IGNORE는 패키지 수를, --symlink-install은 Python 파일을 고친 뒤 다시 빌드할 필요를, -DBUILD_TESTING=0은 CMake 패키지 안의 테스트 몫을 줄인다. ROS 2 Jazzy 컨테이너에서 최소 패키지로 5회 잰 결과는 이 구분과 맞았지만, 제목이 말하는 순서를 한 칸 앞당긴다. 무변경 재빌드는 패키지 수에 비례하지 않았고, 플래그를 직전 실행과 다르게 준 순간 모든 CMake 패키지가 재구성되면서 패키지 두 개를 빼서 아낀 시간보다 더 많이 썼다. 이 조건에서 순서의 첫 칸은 패키지를 줄이는 일이 아니라 플래그를 고정하는 일이었다.

따라오는 데 필요한 것은 터미널에서 source와 colcon build를 실행해 본 경험, ament_cmake·ament_python이라는 이름, 초 단위 곱셈 정도다. 측정은 로봇이나 시뮬레이터를 움직이지 않는 빌드 도구 실험이며, 패키지 안에 한 줄짜리 C++ 파일만 있어서 실제 프로젝트의 컴파일 시간은 재지 않았다.

바꾼 것이 없어도 패키지마다 빌드와 설치가 돈다

작업공간(workspace)은 src 아래에 패키지 소스를 두고, colcon이 그 옆에 build·install·log를 만드는 디렉터리다. colcon build를 치면 colcon은 src에서 패키지를 찾고, 패키지 선택 인자(package selection arguments, 이번 실행에서 처리할 패키지 집합을 정하는 옵션)로 대상을 고른 뒤, 실행기(executor, 여러 패키지 작업을 하나씩 또는 동시에 처리하는 방식)에 넘긴다. 패키지마다 붙은 빌드 유형(build type)이 그다음 동작을 정한다. ament_cmake는 CMake로, ament_python은 Python의 setuptools로 빌드한다.

ament_cmake 패키지 네 개가 있는 작업공간을 떠올려 보자. 아무 파일도 바꾸지 않고 colcon build를 다시 실행해도 colcon은 네 패키지 각각에 대해 같은 순서를 밟는다. colcon-cmake 소스를 보면 순서는 재구성 필요 여부 판단 → cmake --build → install 타깃이 있으면 설치 → 환경 스크립트 생성이다. 바뀐 것이 없으면 컴파일러는 거의 일하지 않지만, 빌드 도구가 최신 여부를 확인하고 설치 단계를 거치는 시간은 남는다. 그래서 무변경 빌드도 0초가 아니고, 선택된 패키지가 많을수록 늘어난다.

재구성(configure)은 CMake가 CMakeLists.txt를 읽어 빌드 파일을 새로 만드는 단계다. colcon-cmake는 이 단계를 매번 돌리지 않는다. --cmake-force-configure를 줬을 때, CMake가 이전 구성값을 저장해 두는 CMakeCache.txt가 없을 때, 빌드 파일이 없을 때, 그리고 직전 실행과 cmake 인자가 다를 때만 다시 돈다(L121-L140). 바뀐 파일이 없는 재빌드라면 컴파일러가 할 일이 거의 없으니, 이 단계가 다시 도는지가 실행 시간을 가를 것으로 예상할 수 있다. 마지막 조건이 뒤에서 실측의 중심이 된다.

catkin과 비교하면 colcon에는 devel 공간이 없고, ament_cmake처럼 devel 공간 개념이 없는 빌드 유형은 설치가 필요하다고 ROS 2 Jazzy colcon 튜토리얼은 설명한다. 이 구조 차이가 원 질문의 “catkin보다 느리다”는 체감의 원인인지는 이 글에서 판정하지 않았다. 질문자의 Jetson·Docker·--merge-install 환경도 재현하지 않았다.

네 수단은 빌드 시간의 서로 다른 몫을 덜어낸다

공식 튜토리얼은 overlay와 --symlink-install을 본문에서, COLCON_IGNORE와 -DBUILD_TESTING=0은 끝의 Tips에서 따로 소개한다(2026-10-04 확인). 따로 읽으면 넷 다 “빌드를 빠르게 하는 옵션”으로 보이지만, 줄이는 대상이 다르다. 패키지 수, 다시 빌드할 필요, 패키지 안의 테스트 몫이다.

패키지 수를 줄이는 쪽: overlay와 COLCON_IGNORE

아래층 환경(underlay)은 미리 source해 둔 기존 ROS 2 설치나 다른 colcon 작업공간이고, 위층 작업공간(overlay)은 그 위에 얹은 내 작업공간이다. 공식 튜토리얼은 “소수의 패키지를 반복해 고칠 계획이면 모든 패키지를 한 작업공간에 넣기보다 overlay를 권장한다”고 쓴다(rolling 원문 L155-L163, Jazzy 문서도 같은 문장). 거의 고치지 않는 의존 패키지를 underlay로 한 번 빌드해 두면, 매번 돌리는 overlay에는 내가 고치는 패키지만 남는다.

overlay를 투명 필름을 겹치는 그림으로 떠올리면 편하지만, 실제로 겹쳐지는 것은 파일이 아니다. 작업공간을 source하는 순서대로 환경변수의 검색 경로 앞쪽에 경로가 추가될 뿐이다. 그래서 underlay에 있는 같은 이름의 패키지를 overlay에서 다른 버전으로 덮어쓰는 일(override)은 colcon 문서가 밝히듯 모든 경우에 동작하지 않는다. 체인을 만들 때는 underlay를 먼저 빌드하고, 새 터미널에서 underlay를 source한 뒤 overlay를 빌드한다. overlay 패키지는 underlay 패키지에 의존할 수 있지만 반대는 안 되며, 이후에는 체인의 마지막 작업공간만 source하면 된다.

COLCON_IGNORE는 같은 작업공간 안에서 패키지를 빼는 방법이다. 패키지 디렉터리에 빈 COLCON_IGNORE 파일을 두면 colcon이 그 경로를 색인하지 않는다. colcon-core의 구현은 다른 식별 확장보다 높은 우선순위로 이 파일이 있는지 먼저 보고, 있으면 그 경로를 통째로 건너뛴다. 파일을 지우기 전까지 모든 colcon 실행에 적용된다는 점이 --packages-skip이나 --packages-select와 다르다. 선택 인자는 그 실행 한 번에만 적용되고, 공식 how-to에 따르면 --packages-select는 선택한 패키지의 의존성이 이미 빌드돼 있다고 가정한다. 의존성까지 함께 빌드하려면 --packages-up-to, 고친 패키지에 의존하는 패키지까지 다시 빌드하려면 --packages-above를 쓴다.

무시한 패키지에 다른 패키지가 의존한다면 그 의존성은 underlay가 제공해야 한다. 그렇지 않으면 의존하는 쪽의 빌드가 실패할 것으로 보는데, 이는 문서 동작에서 끌어낸 추론이며 실험하지 않았다. COLCON_IGNORE를 저장소에 커밋하면 동료의 작업공간에도 적용된다는 점도 함께 판단해야 한다.

다시 빌드할 필요를 없애는 쪽: –symlink-install

심볼릭 링크(symbolic link)는 파일을 복사하지 않고 원본을 가리키는 바로가기 파일이다. --symlink-install을 주면 colcon은 가능한 곳에서 설치 파일을 복사 대신 링크로 둔다(build verb 정의). ament_python 패키지는 setuptools의 develop --editable 방식으로 설치되고(colcon-core L104-L119), 이미 링크된 Python 모듈을 고치면 다시 빌드하지 않아도 설치본에 반영된다. ament_cmake 패키지에는 colcon-ros가 cmake 인자 -DAMENT_CMAKE_SYMLINK_INSTALL=1을 덧붙이고(colcon-ros L50-L53), ament_cmake가 CMake의 install()을 링크 버전으로 바꾼다(install.cmake L16-L37). launch 파일이나 YAML 같은 설치 자원이 여기에 해당한다.

이 수단이 0초로 만드는 것은 “이미 설치된 비컴파일 파일의 내용 수정”뿐이다. C++ 소스와 헤더는 컴파일해야 하므로 여전히 다시 빌드한다. 새 파일을 추가하거나 setup.py의 entry_points·data_files 목록을 바꿨을 때도 다시 빌드가 필요한데, colcon-core가 링크 대상을 빌드 시점에 계산하는 코드에서 끌어낸 추론이며 공식 문서 문장으로 확인한 것은 아니다. Windows에서는 관리자 권한이나 개발자 모드가 필요해 튜토리얼이 기본으로 쓰지 않는다.

ament_cmake에 cmake 인자를 덧붙인다는 사실은 앞의 재구성 조건과 이어진다. 어떤 실행에는 --symlink-install을 주고 다음 실행에는 빼면 cmake 인자가 달라지므로, 모든 ament_cmake 패키지가 재구성된다는 예측이 나온다.

패키지 안의 테스트 몫만 줄이는 쪽: -DBUILD_TESTING=0

BUILD_TESTING은 CMake에서 테스트 트리를 구성하고 빌드할지 정하는 스위치로, CMake CTest 모듈과 ament_cmake_test 모두 기본값을 ON으로 둔다. ros2 pkg create로 만든 ament_cmake 패키지는 린트 테스트를 if(BUILD_TESTING) 블록 안에 넣는다(ros2cli 템플릿 L79-L88). --cmake-args -DBUILD_TESTING=0이 줄이는 시간은 그 블록 안에 테스트 타깃이 얼마나 있느냐에 달렸다. CMake 인자이므로 setup.py로 빌드하는 ament_python 패키지에는 효과가 없고, colcon test를 돌릴 작업공간이라면 테스트가 빌드돼 있어야 한다.

작업공간 패키지를 자주 고침/거의 안 고침과 ament_python/ament_cmake로 나눠 underlay·COLCON_IGNORE, --symlink-install, -DBUILD_TESTING=0으로 이어지는 분류 흐름도, 모든 경로 앞에 플래그 고정 단계가 있다

가상 작업공간 하나로 340초를 나눠 보기

몫이 어떻게 갈리는지 보려고 숫자를 하나 정해 둔다. 아래 값은 측정이 아니라 설명용 가정이다. 거의 고치지 않는 의존 패키지 10개가 각각 30초, 내가 고치는 패키지 2개가 각각 20초 걸린다고 하자. A는 ament_python, B는 ament_cmake다. 합계는 모두 “build와 install을 지운 클린 빌드를 --executor sequential로 하나씩 처리했을 때의 시간 합”으로 정의한다. 기본 병렬 빌드의 실제 대기 시간이나 증분 빌드 시간이 아니라, 패키지를 하나씩 더한 상한이다.

단계 식 값 덜어낸 몫
전체 작업공간 10×30 + 2×20 340초 기준
overlay에 내 패키지 2개만 2×20 40초 패키지 수 (340÷40 = 8.5배)
COLCON_IGNORE로 의존 3개 제외 340 − 3×30 250초 패키지 수 (90초)
A의 Python 파일만 수정, symlink 없음 1×20 20초 없음, 반영하려면 다시 빌드
A의 Python 파일만 수정, symlink 있음 0 0초 다시 빌드할 필요
B를 BUILD_TESTING=0으로 (테스트 비중 25% 가정) 20×(1−0.25) 15초 B 안의 테스트 몫
overlay + A symlink + B 테스트 생략, 반복 1회 0 + 15 15초 세 몫의 조합

overlay가 가장 큰 몫을 덜어내는 이유는 단순하다. 300초짜리 의존 패키지 묶음을 매번 다시 돌릴 대상에서 통째로 빼기 때문이다. 하루 10번 모두 클린 빌드를 한다는 극단적 가정이면 (10×340 − 10×40)÷60 = 50분이지만, 실제로는 매번 클린 빌드를 하지 않으므로 이 숫자를 “colcon이 8.5배 빨라진다”로 읽으면 안 된다. 여러 패키지가 동시에 빌드되는 기본 설정에서 몇 초가 줄어드는지는 의존 관계·코어 수·메모리에 따라 달라서 이 산술로는 답할 수 없다.

열 개의 식은 example.py에 assert로 적어 Python 3.12.3에서 검산했고, 실행 기록은 verification.json에 남겼다. 산술 검산일 뿐 colcon이나 CMake를 실행한 기록은 아니다.

손으로 풀어 볼 질문

의존 패키지 10개 중 4개에 COLCON_IGNORE를 두면 클린 순차 합계는? 340 − 4×30 = 220초다. 다만 그 4개에 작업공간의 다른 패키지가 의존한다면 underlay가 그 패키지를 제공해야 빌드가 이어진다.

A의 setup.py entry_points에 새 실행 파일을 추가했다. symlink 덕분에 0초일까? 아니다. 진입점과 링크 대상은 빌드할 때 만들어지므로 다시 빌드해야 하고, 표의 20초 쪽에 해당한다.

B 대신 A에 -DBUILD_TESTING=0을 붙이면 줄어들까? 줄지 않는다. CMake 인자라서 ament_python 패키지의 setup.py 빌드에는 적용되지 않는다.

Jazzy 컨테이너에서 잰 값은 계산과 어디서 갈렸나

산술은 “패키지마다 고정된 시간이 든다”고 가정한다. 이 가정이 무변경 재빌드에서도 맞는지, 플래그를 바꿀 때 재구성이 정말 다시 도는지를 하네스가 같은 스크립트로 5회 실행해 확인했다.

환경은 결과 파일에 기록된 그대로다. 이미지는 ros:jazzy-ros-base를 다이제스트 ros@sha256:066420e07f60aa18262f2479981def87ebcfcec42eefb0c0c57c4a46098348ca로 고정했고, ROS 2 Jazzy, Python 3.12.3, 네트워크 없음, 1 CPU·1 GiB 제한이다. 같은 이미지의 패키지 버전은 조사 단계에서 colcon-core 0.21.3, colcon-cmake 0.2.30, colcon-ros 0.5.0, colcon-parallel-executor 0.3.0, cmake 3.28.3으로 확인했다. 하네스 기록상 RMW(ROS 미들웨어 계층, 노드 사이 통신을 구현하는 부분)는 기본값 rmw_fastrtps_cpp이지만, 이 실험은 노드를 실행하지 않으므로 측정과 관계가 없다.

작업공간 구성은 이렇다. ws_cmake에는 서로 의존하지 않는 최소 ament_cmake 패키지 cm_1~cm_4가 있고, 각각 한 줄짜리 C++ 실행 파일, 설치되는 YAML 한 개, if(BUILD_TESTING) 안의 테스트용 타깃 한 개를 가진다. Python 쪽은 같은 ament_python 패키지 py_demo를 복사 설치용과 symlink 설치용 작업공간에 따로 둔다. 시간은 time.monotonic()(단조 시계, 시스템 시각을 바꿔도 뒤로 가지 않는 경과 시간용 시계)으로 각 colcon build 명령의 벽시계 시간을 쟀다. 1초 안팎인 무변경 재빌드는 시험 실행에서 연속 두 번의 값 차이가 커서, 같은 명령을 3번 재고 중앙값을 남기도록 바꿨다.

재구성이 돌았는지는 colcon이 패키지마다 남기는 log/latest_build/<패키지>/command.log에서 cmake <소스 경로> 형태의 호출을 찾아 판정했다. 첫 시험 실행은 CMakeCache.txt 수정 시각으로 판정했는데, 눈에 띄게 오래 걸린 실행이 “재구성 0″으로 나왔다. 값이 바뀌지 않으면 캐시 파일의 수정 시각이 그대로일 수 있기 때문이다. 자기 작업공간에서 재구성 여부를 확인할 때도 캐시 파일의 시각보다 command.log를 보는 편이 맞다.

스크립트와 실행별 원본 출력은 실험 디렉터리에 있다. 이 글이 인용한 파일은 공개 커밋 0c285fb에 고정한 experiment.py, results.json, 예측과 시행착오를 적은 plan.md다.

Docker가 있는 Linux에서 빈 디렉터리를 하나 만들고 아래 세 줄을 실행하면 같은 이미지, 네트워크 차단, 1 CPU·1 GiB 조건으로 재현할 수 있다. 첫 줄은 같은 커밋의 스크립트를 내려받고, 둘째 줄은 그 파일의 SHA-256이 results.json에 기록된 script_sha256과 같은지 확인한다. 하네스는 여기에 더해 --pids-limit 256, --tmpfs /tmp, 환경변수 HOME=/tmp·ROS_LOG_DIR=/tmp, 읽기 전용(:ro) /work 마운트를 썼다. 아래 블록은 이 격리 설정을 뺀 형태이며, 스크립트는 작업공간을 컨테이너의 /tmp/colcon_lab에만 만든다.

curl -fsSLO https://raw.githubusercontent.com/sungpyo9053/blog/0c285fb1b57bacb758f786e15b2bf7e12da69faa/experiments/colcon-build-slow-overlay-colcon-ignore-symlink-install-order/experiment.py
echo "2989285c5d908c4828f8f3b64a395c8bdaf0adced5cd6fe4442c5958acb4b29e  experiment.py" | sha256sum -c -
docker run --rm --network none --cpus 1 --memory 1g -v "$PWD":/work ros@sha256:066420e07f60aa18262f2479981def87ebcfcec42eefb0c0c57c4a46098348ca bash -c "source /opt/ros/jazzy/setup.bash && python3 /work/experiment.py"

둘째 줄은 experiment.py: OK를 출력해야 한다. FAILED가 나오면 받은 파일이 글의 근거와 다르므로 셋째 줄을 실행하지 않는다. 셋째 줄이 정상이라면 한 줄에 JSON 하나씩 출력되고, 마지막 summary 줄의 failures가 빈 배열이며 종료 코드가 0이다. colcon 명령 중 하나라도 실패하면 error 줄이 찍히고 종료 코드 1로 끝난다.

아래 시간은 하네스 5회 실행의 값이다. 무변경 재빌드 행은 실행마다 3번 잰 중앙값이며, 표에는 5회 중 가장 작은 값과 큰 값을 적었다. 모든 행은 --executor sequential이고, 기본 실행기 행만 예외다.

무변경 재빌드 (ws_cmake) 처리한 패키지 plan.md 예측 실측 (5회 범위) 재구성된 패키지
전체 4 0보다 크고 셋 중 가장 김 1.019~1.137초 0
cm_3·cm_4에 COLCON_IGNORE 2 그 사이 0.709~0.718초 0
--packages-select cm_1 1 0보다 크고 가장 짧음 0.562~0.573초 0
전체, 기본 실행기(colcon build) 4 예측 없음 1.088~1.104초 0

COLCON_IGNORE 파일 두 개를 둔 동안 colcon list --names-only는 5회 모두 cm_1, cm_2 두 개만 찾았고, 파일을 지우자 다시 4개를 찾았다. 무변경 재빌드에서 cm_1의 command.log에 남은 호출은 매번 build, install 두 단계였다. 참고로 같은 4개 패키지의 첫 클린 빌드는 4.54~4.587초였다.

직전 실행 대비 cmake 관련 인자 (ws_cmake, 무변경) 실측 (5회 범위) 재구성된 패키지 cm_1 캐시의 BUILD_TESTING 테스트 타깃 구성
--cmake-args -DBUILD_TESTING=0 추가 2.329~2.455초 4 0 없음
같은 인자 반복 0.844~0.868초 0 0 없음
인자를 빼고 기본 명령으로 2.326~2.357초 4 0 없음
--symlink-install 추가 3.058~4.3초 4 0 없음
--symlink-install 반복 0.912~0.933초 0 0 없음
colcon_defaults.yaml에 symlink-install: true, 명령줄 인자 없음 0.907~0.934초 0 0 없음

--symlink-install을 추가한 실행부터 cm_1 캐시의 AMENT_CMAKE_SYMLINK_INSTALL은 1이었고, 설치된 params.yaml은 5회 모두 src의 원본을 가리키는 심볼릭 링크였다. --symlink-install 추가 행의 4.3초는 5회 중 한 번(5회차)이고, 나머지 네 번은 3.058~3.108초였다.

py_demo (ament_python) 클린 빌드 core.py를 v2로 고친 뒤, 다시 빌드 없이 읽은 값 다시 빌드 후
복사 설치 1.624~1.833초 v1 (반영 안 됨) 1.615~1.623초 걸려 v2
--symlink-install 2.008~2.176초 v2 (반영됨) 필요 없음

1회차 원본 출력 가운데 판단을 바꾼 줄이다.

{"step": "environment", "ros_distro": "jazzy", "colcon_core": "0.21.3+upstream-1", "os_cpu_count": 2, "sched_affinity": 2, "python": "3.12.3"}
{"step": "nochange_rebuild_4pkgs", "args": "colcon build --executor sequential", "samples_seconds": [1.035, 1.03, 1.025], "median_seconds": 1.03, "packages_finished": 4, "cmake_configure_invoked": 0, "cm_1_commands": ["build", "install"]}
{"step": "nochange_select_1pkg", "args": "colcon build --executor sequential --packages-select cm_1", "samples_seconds": [0.567, 0.602, 0.565], "median_seconds": 0.567, "packages_finished": 1, "cmake_configure_invoked": 0, "cm_1_commands": ["build", "install"]}
{"step": "nochange_args_same_BUILD_TESTING_0", "args": "colcon build --executor sequential --cmake-args -DBUILD_TESTING=0", "seconds": 0.853, "packages_finished": 4, "cmake_configure_invoked": 0, "cmake_cache_rewritten": 0, "cm_1_BUILD_TESTING_in_cache": "0", "cm_1_AMENT_CMAKE_SYMLINK_INSTALL_in_cache": "OFF", "cm_1_selftest_in_generated_makefile": false, "cm_1_commands": ["build", "install"]}
{"step": "nochange_args_dropped_back_to_default", "args": "colcon build --executor sequential", "seconds": 2.347, "packages_finished": 4, "cmake_configure_invoked": 4, "cmake_cache_rewritten": 0, "cm_1_BUILD_TESTING_in_cache": "0", "cm_1_AMENT_CMAKE_SYMLINK_INSTALL_in_cache": "OFF", "cm_1_selftest_in_generated_makefile": false, "cm_1_commands": ["configure", "build", "install"]}
{"step": "py_edit_without_rebuild_copy", "before_edit": "v1", "after_edit_no_rebuild": "v1", "reflected": false}
{"step": "py_edit_without_rebuild_symlink", "before_edit": "v1", "after_edit_no_rebuild": "v2", "reflected": true}

plan.md의 예측과 비교하면, 패키지 수 순서(4개 > 2개 > 1개), COLCON_IGNORE에 따른 패키지 수 감소, 인자를 바꾼 실행의 재구성과 같은 인자·colcon_defaults.yaml 고정 실행의 재구성 없음, symlink 쪽만 Python 수정이 반영되는 결과는 5회 모두 예측대로였다. 예측과 다른 부분은 하나다. plan.md는 무변경 재빌드가 “대략 패키지 수에 비례해” 커진다고 적었지만, 패키지를 1개만 골라도 4개 전체의 절반 안팎이 걸렸다. -DBUILD_TESTING=0을 뺀 뒤 테스트가 다시 켜지는지는 plan.md가 예측하지 않은 항목이다. 시험 실행에서 먼저 보였고, 정식 5회에서도 모두 캐시 값이 0으로 남고 테스트 타깃이 돌아오지 않았다.

여기서부터는 측정에 대한 해석이다.

패키지 수는 무변경 재빌드의 손잡이가 맞았다. 다만 시간이 패키지 수에 비례하지 않았다는 것은, 패키지 수와 무관하게 실행마다 드는 몫이 따로 있다는 뜻으로 읽힌다. colcon 자체의 시작과 패키지 발견 같은 공통 작업으로 보이지만, 이 실험은 그 몫을 단계별로 나눠 재지 않았다. 앞의 340초 산술은 이 공통 몫을 0으로 둔 모델이므로, 패키지가 작을수록 “패키지를 빼면 그만큼 준다”는 어림이 실제보다 후하다. 반대로 컴파일이 무거운 실제 패키지라면 패키지 하나가 차지하는 몫이 커지는데, 그 크기는 이 실험으로 말할 수 없다.

더 큰 차이는 플래그에서 나왔다. 인자를 바꾼 실행은 같은 인자 반복보다 두 배 넘게 걸렸고, --symlink-install을 새로 붙인 실행은 세 배 넘게 걸렸다. COLCON_IGNORE로 패키지 두 개를 빼서 아낀 시간보다, 플래그를 한 번 바꿔 단 비용이 컸다. “오늘은 빨리 돌리게 -DBUILD_TESTING=0만 붙이자” 같은 습관이 이 조건에서는 오히려 손해였다는 뜻이다. colcon_defaults.yaml로 고정한 실행은 명령줄에 인자가 없어도 재구성이 돌지 않았으니, colcon은 설정 파일까지 합쳐 최종적으로 정해진 인자를 직전 실행과 비교하는 것으로 보인다.

-DBUILD_TESTING=0을 뺀 실행은 재구성 비용을 그대로 치르고도 테스트를 되돌리지 못했다. CMake 캐시에 한 번 저장된 0이 남아 있었기 때문으로 보인다. 테스트를 다시 켜려면 값을 명시적으로 주거나 캐시를 지우는 방법을 확인해야 하는데, 이 실험은 그 복구 절차를 재지 않았다. 테스트를 생략한 빌드 뒤에 colcon test를 돌릴 계획이라면 이 점을 먼저 확인하는 것이 안전하다.

기본 실행기가 순차 실행보다 빠르지 않았던 것은 이 컨테이너의 조건 때문으로 보인다. 컨테이너 안에서 os.cpu_count()는 2를 보고했고 colcon의 병렬 작업자 수 기본값이 이 값이므로(executor 문서), 작업자 두 개가 1 CPU 할당량을 나눠 썼을 것이다. 다만 작업자 수를 바꿔 가며 재지는 않았고, 코어가 여러 개인 개발 PC에서 병렬이 얼마나 줄이는지는 이 결과로 판단할 수 없다.

Python 쪽은 예측과 같았다. symlink 설치의 첫 빌드는 복사 설치보다 조금 길었고, 이득은 그 뒤 Python 파일을 고칠 때마다 다시 빌드를 건너뛰는 데서 나온다. ament_cmake가 설치한 YAML도 원본을 가리키는 링크였으므로, 이미 설치 목록에 있는 launch·YAML 파일의 내용 수정은 같은 방식으로 반영될 것으로 본다.

측정이 다루지 않은 범위도 분명히 해 둔다. overlay 작업공간을 실제로 따로 만들지는 않았고, overlay가 줄이는 “이번 실행의 패키지 수”를 --packages-select와 COLCON_IGNORE로 대신 쟀다. C++ 소스를 고친 뒤의 재컴파일, 새 Python 파일 추가, setup.py 변경 시 symlink 동작, catkin과의 비교, Jetson 같은 arm64 기기도 재지 않았다. 모두 한 호스트의 1 CPU 컨테이너에서 나온 값이다.

내 작업공간에서는 플래그 고정부터 시작한다

측정을 반영하면 줄이는 순서가 이렇게 바뀐다.

  1. 지금 무엇이 오래 걸리는지 먼저 기록한다. 같은 작업공간에서 기본 설정과 colcon build --executor sequential로 각각 빌드하고, 패키지별 시간을 비교한다. 각 실행은 log/build_<날짜 시각>/ 아래에 패키지별 command.log, stdout_stderr.log를 남기고 latest_build가 최신 실행을 가리킨다(log files 문서). 작업마다 시작과 종료를 출력하는 console_start_end 이벤트 핸들러도 있다(event handler 문서). 순차 실행은 속도를 높이는 옵션이 아니다. 공식 튜토리얼은 Raspberry Pi처럼 CPU·메모리가 부족한 기기에서 화면이 멈추는 것을 피하는 용도로 소개하며, 여기서는 패키지별 몫을 나눠 보는 진단에 쓴다.
  2. 플래그를 작업공간 루트의 colcon_defaults.yaml로 고정한다. 실험에서 확인한 형태는 아래와 같다. configuration 문서에 따르면 --cmake-args 같은 다른 인자도 같은 파일로 고정할 수 있지만, 실험은 symlink-install 한 항목만 확인했다.
build:
  symlink-install: true
  1. 거의 고치지 않는 패키지를 매번의 대상에서 뺀다. 오래 쓸 구성이면 underlay로 옮기고, 잠시 빼 둘 패키지면 COLCON_IGNORE, 그 실행에만 좁히려면 --packages-select나 --packages-above를 쓴다.
  2. 자주 고치는 패키지의 유형을 본다. ament_python이면 2번에서 고정한 --symlink-install이 Python 파일 수정마다 다시 빌드할 필요를 없앤다. ament_cmake이고 그 작업공간에서 colcon test를 돌리지 않는다면 -DBUILD_TESTING=0을 역시 설정 파일로 고정하는 것을 검토한다.

이 순서가 맞지 않는 경우도 있다. 같은 작업공간에서 테스트를 자주 돌린다면 BUILD_TESTING을 끄는 이득보다 다시 켜는 번거로움이 클 수 있다. Windows에서는 --symlink-install에 별도 권한이 필요하다. 다른 패키지가 의존하는 패키지에 COLCON_IGNORE를 두려면 그 의존성을 underlay가 제공하는지부터 확인해야 한다.

1번에서 얻은 패키지별 시간을 앞의 340초 표처럼 “자주 고침/거의 안 고침, python/cmake”로 나눠 적어 보면, 어느 수단이 내 작업공간에서 가장 큰 몫을 덜어낼지 숫자로 비교할 수 있다. 그 어림이 실제 대기 시간과 얼마나 다른지가 다음에 잴 대상이다.

이 글에는 제휴·협찬이 없다.

비슷한 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다