고친 뒤의 증명: 같은 조건, diff, 일의 양으로 나누기
한 줄 요약
고쳤다는 증거는 고치기 전과 같은 조건에서 다시 받은 프로파일입니다. 두 프로파일을 빼서(diff) 줄어든 곳을 확인하고, CPU 합계가 아니라 요청 하나당 CPU 처럼 일의 양으로 나눈 값으로 효과를 말합니다.
왜 이게 필요했나
성능 개선의 보고서에서 가장 흔한 결함은 비교 조건이 다르다는 것입니다. 고치기 전에는 동시성 8로, 고친 뒤에는 동시성 4로 쟀다면 지연이 줄어든 이유가 코드인지 줄어든 동시성인지 알 수 없습니다. 쿠폰 주문 비율이 다르면 하는 일 자체가 다르고, 프로파일을 10초와 30초로 받았다면 표본 수가 세 배 차이 납니다. 이런 비교는 다음 사람이 되풀이할 수 없어서 결국 아무것도 증명하지 못합니다.
반대 방향의 함정도 있습니다. 고친 뒤 처리량이 다섯 배가 되었는데 CPU 사용률은 그대로 두 코어를 다 씁니다. CPU 합계만 보면 「안 나아졌다」로 읽힙니다. 같은 CPU 로 다섯 배의 일을 한 것이 개선입니다.
어떻게 동작하나
조건은 부하 발생기가 결과와 함께 적어 두는 것이 가장 확실합니다. 이 코스의 loadgen 은 동시성·시간·쿠폰 비율·큰 주문 비율·시드를 결과 파일에 함께 씁니다. 명령에 적지 않은 옵션은 기본값으로 돌아간다는 점이 실수의 단골입니다. 시드를 빼먹으면 주문의 모양이 바뀝니다.
go tool pprof -diff_base before.pb.gz after.pb.gz 는 뒤의 프로파일에서 앞의 것을 뺍니다. 줄어든 곳이 음수, 늘어난 곳이 양수입니다. 고친 함수는 자기 시간(flat)이 원래 거의 없었다면 -top 의 위쪽에 안 보이니 -cum 으로 정렬합니다. 그리고 늘어난 곳도 봅니다. 처리량이 늘면 다른 함수가 그만큼 바빠지는 것이 정상이고, 거기가 다음 병목 후보입니다.
일의 양으로 나누는 식은 이렇습니다.
요청 하나당 CPU(ms) = 프로파일의 CPU 합(ms) ÷ (처리량(req/s) × 프로파일 길이(s))
마지막으로 사용자가 느끼는 값(p99)도 함께 적습니다. 서버가 CPU 를 덜 쓰는데 p99 가 그대로라면 시간은 CPU 가 아닌 곳(잠금·네트워크·큐)에서 쓰이고 있다는 뜻입니다.
현장에서 만나는 모습
성능 회귀를 막는 팀은 이 절차를 CI 에 넣습니다. 같은 벤치마크를 같은 기계에서 두 판으로 돌리고, Go 라면 go test -bench 와 benchstat 으로 차이가 잡음보다 큰지 통계로 판정합니다. 수작업이든 자동이든 원칙은 같습니다. 조건을 고정하고, 차이를 빼고, 일의 양으로 나눕니다.
이 코스의 pricing 을 고치면 숫자가 극적으로 바뀝니다. 같은 동시성에서 처리량이 몇 배로 늘고, 쿠폰 정규식이 차지하던 CPU 의 대부분이 diff 에서 음수로 빠집니다. 그런데 CPU 합계는 여전히 두 코어를 다 씁니다. 빨라진 서버가 같은 시간에 더 많은 요청을 처리하기 때문입니다. 이 장면을 한 번 숫자로 보고 나면 「CPU 사용률이 그대로라 효과가 없다」는 말에 속지 않게 됩니다.
반대로 처리량은 그대로인데 요청당 CPU 만 조금 줄었다면, 부하 발생기가 병목일 수도 있습니다. 측정 도구가 먼저 한계에 닿으면 서버의 개선이 숫자에 나타나지 않습니다. 부하 발생기의 CPU 도 함께 확인하는 습관이 그래서 필요합니다.
비교 결과를 남길 때는 숫자와 함께 두 측정의 명령 줄을 그대로 적습니다. 다음 사람이 같은 명령으로 다시 재서 같은 결론에 닿을 수 있어야 그 숫자가 증거가 됩니다. 이 실습의 결과 파일이 조건을 담고 있는 것도 그 때문입니다.
흔히 틀리는 읽기
새 바이너리를 빌드하고 떠 있던 프로세스를 다시 띄우지 않는 실수가 흔합니다. 그러면 after 프로파일은 고치기 전 코드의 프로파일이고, diff 는 「아무것도 안 바뀌었다」를 보여 줍니다. 고친 뒤에는 서비스를 다시 띄웠는지, after 프로파일에 원인 함수가 정말 사라졌는지를 먼저 확인합니다.
「p99 가 줄었다」만으로 끝내는 것도 위험합니다. 부하 발생기가 같은 동시성으로 요청을 보내면, 서버가 빨라질수록 같은 시간에 더 많은 요청을 보내 서버를 더 바쁘게 만듭니다. 그 결과 다른 병목이 드러나 p99 가 생각보다 덜 줄기도 합니다. 처리량, 요청당 CPU, p99 를 함께 적어야 그 상황을 읽을 수 있습니다.
다음 실습에서 할 것
처음 소스로 기준선을 재고, 정규식을 한 번만 컴파일하도록 고친 뒤 같은 조건으로 다시 잽니다. diff 로 줄어든 곳을 확인하고, 처리량과 요청 하나당 CPU, p99 를 견줍니다. 마지막에 일부러 조건을 바꾼 측정을 하나 만들어 무엇이 다른지 찾아냅니다.
English corner
- baseline: 기준선. "Compare against a baseline profile." 의 그 기준입니다.
- regression: 성능이 나빠지는 일. "performance regression" 을 막는 것이 이 절차의 목적입니다.
- apples to apples: 같은 조건끼리의 비교. "That is not an apples-to-apples comparison." 은 리뷰에서 자주 듣는 말입니다.
- normalize: 정규화하다. 「일의 양으로 나눈다」를 영어로 이렇게 씁니다.