부하 테스트 · 회귀 판정 · 이론
8% 느려졌다는 말에는 잡음의 크기가 빠져 있다
한 줄 요약
두 실행의 차이가 회귀인지 아닌지는 차이의 크기만으로 정할 수 없다. 아무것도 바꾸지 않았을 때 나오는 차이, 곧 잡음의 크기를 먼저 재야 한다.
왜 이게 필요했나
배포 파이프라인에 성능 시험을 붙인 팀이 있었다. 규칙은 간단했다 — 직전 배포보다 p50 이 5% 넘게 느려지면 배포를 막는다. 그럴듯해 보였고 실제로 첫 주에 세 번 막았다.
문제는 그 세 번 중 두 번이 거짓 경보였다는 것이다. 되돌린 변경 하나는 로그 한 줄을 지운 것이었고, 다른 하나는 주석 수정이었다. 나중에 아무것도 바꾸지 않은 커밋으로 같은 시험을 다섯 번 돌려 보니 p50 이 7%까지 벌어졌다. 문턱 5% 는 잡음보다 작았던 것이다.
거짓 경보는 그 자체로도 비용이지만 더 큰 피해는 신뢰를 갉아먹는 것이다. 게이트가 자주 헛돌면 사람들은 게이트를 끄거나 "재시도" 를 누르는 법을 배운다. 그리고 진짜 회귀가 지나갈 때 아무도 멈추지 않는다.
반대로 문턱을 지나치게 느슨하게 잡으면 게이트는 아무것도 막지 않으면서 막고 있다는 착각만 준다. 이 두 실패는 같은 뿌리에서 나온다 — 문턱을 정할 때 잡음을 재지 않았다는 것. 잡음을 재는 데 드는 비용은 같은 시험을 몇 번 더 돌리는 시간뿐이고, 그 시간은 잘못 되돌린 변경 하나를 조사하는 시간보다 언제나 짧다.
어떻게 동작하나
판정에 필요한 것은 세 가지다. 무엇을 잴 것인가, 여러 번 잰 값을 어떻게 하나로 합칠 것인가, 그리고 그 하나를 무엇과 견줄 것인가.
무엇을 잴 것인가. 평균은 꼬리에 끌려다니고 최댓값은 한 번의 사고에 좌우된다. p50 은 안정적이지만 꼬리를 못 보고, p99 는 꼬리를 보지만 표본이 적으면 심하게 흔들린다. 게이트에는 흔들림이 작은 값을 쓰고, 꼬리는 따로 감시하는 편이 낫다.
여러 번 잰 값을 어떻게 합칠 것인가. 한 번 재고 끝내면 그 한 번이 운 나쁜 실행이었는지 알 길이 없다. 그래서 같은 조건으로 여러 번 돌리고 그 값들의 중앙값을 쓴다. 중앙값은 한 번의 이상한 실행에 끌려가지 않는다.
무엇과 견줄 것인가. 여기가 핵심이다. 문턱을 "5%" 처럼 고정하면 그 숫자가 어디서 왔는지 아무도 모른다. 대신 기준선을 반복해 잰 값들의 흩어진 정도를 그대로 문턱으로 쓴다. 가장 단순한 형태는 최대에서 최소를 뺀 값이고, 더 튼튼하게 하려면 중앙값 절대편차의 몇 배를 쓴다. 어느 쪽이든 문턱이 그때 그 기계에서 실제로 잰 값이라는 점이 중요하다.
이 규칙에는 자연스러운 성질이 하나 있다. 반복 횟수를 늘리면 잡음의 추정이 좋아져 더 작은 회귀를 잡아낼 수 있고, 반복을 한 번으로 줄이면 문턱이 0 이 되어 아무 차이나 회귀로 보고한다. 그래서 "몇 번 돌릴 것인가" 는 시간과 민감도를 맞바꾸는 결정이지 취향이 아니다.
현장에서 만나는 모습
가장 흔한 실패는 기준선을 한 번만 재는 것이다. 어제 배포의 측정값 하나를 기준선으로 삼고 오늘 값 하나와 견주면, 문턱을 정할 재료가 아예 없다. 이때 사람들은 고정 백분율을 꺼내 들고, 그 숫자는 대개 회의실에서 정해진다.
두 번째는 조건이 달라진 것을 회귀로 읽는 경우다. 기준선은 한가한 새벽에 재고 새 측정은 다른 작업이 함께 도는 낮에 쟀다면, 두 숫자는 애초에 견줄 수 있는 짝이 아니다. 같은 기계, 같은 시간대, 같은 부하 형태에서 번갈아 재야 잡음의 정의가 성립한다.
세 번째는 기준선이 낡아 가는 것이다. 기준선을 한 번 재 두고 몇 달 동안 그 값과만 견주면, 그 사이에 커널이 올라가고 기반 라이브러리가 바뀌고 기계가 교체된다. 그때의 잡음과 지금의 잡음은 다른 값이고, 낡은 기준선과의 차이는 회귀가 아니라 세월이다. 기준선은 새 측정과 같은 날 같은 기계에서 함께 다시 재는 것이 안전하다.
네 번째는 규칙 없이 눈으로 판정하는 경우다. 두 막대 그래프를 나란히 놓고 "확실히 느려졌네" 라고 말하는 순간, 그 판정은 재현할 수 없는 것이 된다. 다음 사람이 같은 자료를 보고 다른 결론을 내도 누구도 틀렸다고 말할 수 없다. 규칙을 스크립트로 적어 두면 판정이 기록으로 남고, 문턱이 틀렸다는 것을 나중에 알았을 때 무엇을 고쳐야 하는지도 분명해진다. 그리고 그 스크립트는 사람이 읽는 보고서가 아니라 종료 코드를 내야 한다 — 파이프라인이 읽을 수 있어야 실제로 무언가를 막을 수 있기 때문이다.
다섯 번째는 회귀가 아니라 개선을 놓치는 경우다. 게이트를 한쪽 방향으로만 걸어 두면 성능이 좋아진 것도 알 수 없다. 좋아진 쪽의 변화가 잡음을 크게 넘는다면 그것도 기록해야 한다 — 대개는 좋은 소식이지만, 가끔은 시험이 일을 건너뛰기 시작했다는 신호이기 때문이다.
다음 실습에서 할 것
바탕 지연에 재현 가능한 흔들림을 얹은 대상을 띄우고, 같은 조건으로 다섯 번 재서 잡음의 크기를 먼저 구한다. 그다음 지연을 5% 늘린 판과 50% 늘린 판을 각각 다섯 번씩 재고, 다섯 실행의 중앙값끼리 견주는 규칙을 세워 어느 쪽이 잡음을 넘는지 판정한다. 기준선 다섯 실행 중 가장 빠른 것과 가장 느린 것을 골라 한 번짜리 비교를 흉내 내면, 같은 코드끼리도 게이트가 회귀를 보고한다는 것을 숫자로 보게 된다. 마지막으로 기준선 디렉터리와 새 측정 디렉터리를 받아 종료 코드를 내는 게이트 스크립트를 만들고, 채점기가 스스로 만든 입력 세 벌로 그 게이트를 돌려 통과와 실패를 모두 확인한다.