러너가 느린 범인을 Wi-Fi로 지목했다가 틀렸습니다 — 워밍이 콜드보다 느렸던 이유
러너 네 대가 준비됐으니 이제 실제로 얼마나 빠른지 재볼 차례였습니다. 같은 작업을 네 대에 각각 강제로 배정해서 시간을 쟀습니다. 결과는 예상과 여러모로 달랐고, 저는 그중 하나를 자신 있게 오진했습니다.
같은 작업, 네 대의 러너
| 러너 | 소요 시간 |
|---|---|
| GitHub hosted | 65초 |
| 씽크패드 (2015년, 가상 머신) | 3분 29초 |
| 맥북 (M2 Pro, 개발 본체) | 6분 33초 |
| 윈도우 노트북 (16GB, 4코어 8스레드) | 16분 이상 |
첫 번째로 눈에 들어온 것은 2015년 노트북 위의 가상 머신이 M2 Pro 맥북보다 두 배 가까이 빨랐다는 사실입니다. 하드웨어만 보면 말이 안 됩니다. 이유는 단순했습니다. 맥북은 제가 그 위에서 일하고 있는 기계입니다. 편집기가 돌고 브라우저가 떠 있고 다른 프로세스들이 자원을 나눠 쓰는 동안, 씽크패드의 가상 머신은 오직 CI만 하고 있었습니다. 성능은 사양이 아니라 한가함에서 나온다는 생각이 들었습니다.
전환 전후를 좀 더 넓게 비교해보면 격차는 더 분명했습니다.
| 지표 | hosted | 맥북 self-hosted |
|---|---|---|
| 전체 소요 시간 중앙값 | 3.1분 | 12.9분 |
| 백엔드 작업 실행 시간 | 181초 | 307초 |
| 백엔드 작업 큐 대기 중앙값 | — | 430초 |
여기서 알게 된 게 하나 있습니다. 저는 self-hosted가 느린 이유를 줄 서는 시간 때문이라고만 생각했습니다. 러너가 적으니 기다린다는 것이죠. 그런데 대기를 빼고 순수 실행 시간만 봐도 hosted가 1.7배 빨랐습니다. 기다리는 것도 문제였지만, 막상 돌기 시작해도 느렸던 것입니다.
윈도우가 15배 느렸습니다
진짜 이상한 것은 윈도우였습니다. 하드웨어는 씽크패드보다 훨씬 좋은데 다섯 배 가까이 느렸고, hosted와 비교하면 15배였습니다. 내역을 뜯어봤습니다.
의존성 설치 10.5분
테스트 실행 3초
캐시 업로드 5분 이상
테스트는 3초 만에 끝났습니다. 나머지 16분은 전부 파일을 내려받고 올리는 데 쓰이고 있었습니다.
저는 범인을 알았다고 생각했습니다
내려받고 올리는 게 느리다면 답은 뻔해 보였습니다. 네트워크입니다. 게다가 이 노트북은 무선으로 연결돼 있었습니다.
그래서 집 네트워크를 실제로 측정해봤습니다. 그리고 증거가 정확히 제 가설대로 나왔습니다. 통신사에서 준 공유기는 유선과 무선 모두 100Mbps급이었습니다. 유선으로 바꿔도 상한이 100Mbps 언저리라는 뜻입니다. 게다가 5GHz 대역을 아예 지원하지 않는 모델이라 2.4GHz만 쓸 수 있었고, 실측 속도는 무선에서 40Mbps 정도가 나왔습니다.
완벽했습니다. 느린 러너가 있고, 그 러너는 무선을 쓰고, 무선은 느리고, 느린 구간은 전부 파일 전송입니다. 저는 유선 랜 어댑터를 사야겠다는 결론을 내렸습니다.
돌이켜 생각해보면 이 순간이 가장 위험했습니다. 전에도 비슷한 함정에 빠진 적이 있는데, 그럴듯한 가설에 딱 맞는 증거가 실제로 발견되면 조사가 거기서 멈춥니다. 증거가 없어서 못 찾는 것보다, 증거가 있어서 만족하는 쪽이 더 위험합니다. 저는 이미 답을 정해두고 그 답을 확인해줄 데이터를 찾으러 갔고, 성공적으로 찾아냈습니다.
그런데 두 번째 측정이 진단을 뒤집었습니다
어댑터를 사기 전에 한 번 더 재보기로 했습니다. 첫 측정은 아무것도 없는 상태에서 시작한 것이었으니, 이미 파일들이 받아진 상태에서 다시 돌리면 얼마나 빨라지는지 보고 싶었습니다. 네트워크가 범인이라면 두 번째는 훨씬 빨라야 합니다.
| 측정 | 결과 |
|---|---|
| 1차 (아무것도 없는 상태) | 21분 |
| 2차 (파일이 이미 받아진 상태) | 18분 경과 시점에도 미완료 |
더 빨라지기는커녕 비슷하거나 더 느렸습니다. 이 한 줄이 제 진단을 무너뜨렸습니다. 받을 것이 이미 있는데도 느리다면, 느린 이유는 받는 것이 아닙니다.
진짜 범인은 파일시스템이었습니다
자바스크립트 프로젝트의 의존성 폴더는 파일이 십만 개 단위로 만들어집니다. 용량은 크지 않지만 개수가 압도적입니다. 그리고 윈도우는 파일을 하나하나 만들고 지우는 작업이 리눅스나 맥에 비해 눈에 띄게 느립니다. 파일 하나당 차이는 미세하지만 십만 번 반복되면 분 단위가 됩니다.
그러니까 내려받는 게 느렸던 게 아니라 내려받은 것을 디스크에 펼치는 게 느렸던 것입니다. 두 작업이 같은 단계에 묶여 있어서 겉으로는 구분되지 않았고, 저는 그 뭉쳐진 시간을 보고 익숙한 쪽을 범인으로 지목했습니다.
따라오는 결론이 셋이었습니다.
첫째, 유선 랜 어댑터는 살 필요가 없었습니다. 무료로 운영하겠다는 원칙에서 보면 다행이었는데, 실은 원칙을 지킨 게 아니라 오진 직전에 멈춘 것에 가깝습니다.
둘째, 이 노트북은 메모리가 16GB나 남아도는데도 무거운 작업을 맡을 수 없습니다. 발목을 잡는 게 메모리가 아니라 디스크 입출력이라, 사양을 올려도 해결되지 않습니다.
셋째가 가장 흥미로웠습니다. 캐시 업로드에만 5분이 걸린다는 것은, self-hosted에서는 캐시가 손해일 수 있다는 뜻입니다. 캐시라는 개념 자체가 "매번 새로 만들어지는 일회용 머신"을 전제로 설계된 것입니다. 그런데 제 러너는 계속 살아 있고 받아둔 파일이 디스크에 그대로 남습니다. 이미 갖고 있는 것을 굳이 압축해서 올리고 다시 내려받는 셈이니, 최적화가 아니라 순손실이 됩니다. hosted의 상식이 self-hosted에서 뒤집히는 지점이었습니다.
100Mbps는 사실이었지만 범인은 아니었습니다
오해를 남기고 싶지 않아 덧붙입니다. 집 공유기가 100Mbps 한계인 것도, 5GHz를 지원하지 않는 것도 전부 사실입니다. 기가 요금제를 쓰고 있다면 실제로 손해를 보고 있는 구성이고, 교체를 요청할 만합니다.
다만 그것은 이 문제의 원인이 아니었습니다. 참인 사실과 원인인 사실은 다릅니다. 진짜 원인을 찾는 과정에서 가장 방해가 되는 것은 거짓 정보가 아니라, 마침 그 자리에 있던 무관하지만 참인 사실이라는 생각이 들었습니다.
이제 마지막 편입니다. 여기까지 저는 러너 네 대를 만들고, 절전에 실패하고, 성능을 재고, 오진을 하나 바로잡았습니다. 그런데 이 모든 일의 출발점이었던 질문, 애초에 왜 무료 사용량이 부족했는가는 여전히 답하지 않은 상태였습니다. 다음 편에서 그 답을 찾고, 지금까지 만든 것을 전부 끕니다.
관련 글
캐시를 껐더니 CI가 빨라졌습니다 — 최적화가 손해가 되는 조건
의존성 캐시를 끄자 작업당 4분이 사라졌습니다. 캐시는 매번 새로 만들어지는 기계를 전제로 설계된 장치인데, 제 러너는 사라지지 않는 기계였습니다. 전제가 다르면 최적화는 순손실이 됩니다.
다 만들어놓고 전부 껐습니다 — 쿼터를 태운 범인은 macOS 10배 배수였습니다
사용량을 총량으로만 보면 원인이 안 보입니다. 저장소별 SKU로 분해하자 raw 99.4분짜리 macOS 작업이 994분으로 청구돼 쿼터의 절반을 태우고 있었습니다. 2주간 만든 러너 함대는 그래서 전부 껐습니다.
파일시스템이 범인이라던 제 진단도 절반만 맞았습니다 — 링크가 만든 곱셈
윈도우 러너가 느린 범인을 파일시스템으로 지목했는데, 그것도 절반만 맞았습니다. 파일 16만 개짜리 폴더를 93만 개 경로로 걷게 만든 것은 패키지 매니저의 링크 구조였고, 설정 한 줄로 260초가 14초가 됐습니다.