📕 전자책 전문 읽기 재는 기술 · 31쪽
브라우저가 PDF를 바로 표시하지 못합니다. 새 탭에서 열기
이 글은 요약해 다시 쓴 것이고, PDF가 전문입니다.

같은 부하 테스트를 두 번 돌렸는데 숫자가 다르게 나온 적이 있습니까. 이 글은 성능 숫자를 매일 보고 그 숫자로 배포 여부와 서버 증설을 결정하는 엔지니어를 위해 씁니다. 여기서 얻어 갈 것은 하나입니다. 지연 시간이나 처리량 같은 성능 지표는 자연 현상을 관찰한 값이 아니라 우리가 무엇을 재기로 선택한 결과라는 사실이고, 그 선택을 문장으로 적어두지 않으면 숫자는 반년 뒤에 아무 뜻도 없는 문자열이 됩니다.

결론부터 말하면 이렇습니다. 벤치마크가 거짓말을 하는 대부분의 경우, 원인은 도구의 결함이나 누군가의 조작이 아니라 측정을 설계하는 사람이 자신이 무엇을 재고 있는지를 명시적으로 정하지 않은 데 있습니다. 지표를 고르는 절차, 환경을 통제하는 절차, 결과를 보고하는 절차가 모두 산문으로만 존재하고 코드로 강제되지 않으면 그 절차는 바쁜 날 가장 먼저 무너집니다. 그리고 하필 바쁜 날 나온 숫자가 가장 중요한 의사결정에 쓰입니다.

P95 180밀리초는 아무것도 증명하지 않는다: 성능 숫자를 믿을 수 있게 만드는 법 개념을 형상화한 이미지 글의 핵심 개념을 형상화했습니다.

지표는 관찰이 아니라 선택이다

시스템은 초당 수백만 개의 사건을 만들어냅니다. 우리가 보고서에 적는 숫자는 그중 아주 작은 일부를 골라 요약한 결과입니다. 이 선택은 중립적이지 않습니다. 무엇을 고르느냐가 결론의 절반을 이미 결정해 버립니다. 부하 테스트 도구가 평균 응답 시간을 기본으로 찍어주니 평균을 보고하고, 대시보드에 초당 요청 수 그래프가 이미 그려져 있으니 그것을 성능이라 부르는 팀이 많습니다. 도구가 기본으로 뿌려주는 값이 조직의 성능 정의가 되는 순간, 그 조직은 자신이 실제로 무엇을 최적화하고 있는지 모르게 됩니다.

이 문제는 회의실에서 가장 자주 터집니다. 지연 시간이 줄었다는 사람과 메모리가 늘었다는 사람이 서로 다른 결론으로 평행선을 달리는 이유는, 둘이 서로 다른 역할의 지표를 같은 무게로 취급하고 있기 때문입니다. 지연 시간과 메모리 사용량은 같은 자리에서 다뤄질 수 있는 지표가 아닙니다. 하나는 이번 작업이 개선하려는 목표이고, 다른 하나는 개선하지 않아도 되지만 나빠지면 안 되는 한계선입니다. 두 역할을 같은 칸에 넣고 논쟁하니 끝이 나지 않는 겁니다.

이 혼선을 구조적으로 없애는 방법은 지표를 세 칸에 나눠 담는 것입니다. 목표 지표는 이번 작업이 개선하려는 대상이며 반드시 하나여야 합니다. 둘이 되는 순간 트레이드오프를 판단할 기준 자체가 사라집니다. 가드레일 지표는 개선할 필요는 없지만 정해둔 한도 밖으로 나가면 안 되는 값입니다. 진단 지표는 목표가 왜 움직였는지 설명하기 위한 값이며, 이 값 자체를 최적화 대상으로 삼지 않습니다.

역할 개수 판정 방식
목표 지표 하나 개선 폭으로 판정
가드레일 지표 두 개에서 네 개 악화 한도로 판정
진단 지표 제한 없음 설명 용도, 최적화 대상 아님

이 표가 별것 아닌 것처럼 보일 수 있습니다. 하지만 실제로 적용해 보면 회의 시간이 눈에 띄게 줄어듭니다. 예를 들어 동시 사용자 300명 조건에서 응답 지연을 목표로 잡고, 메모리 사용량과 오류율을 가드레일로 놓고, 큐 길이와 GPU 사용률을 진단 지표로 걸어두면 다음 배포 검토에서 나오는 질문이 달라집니다. “지연은 줄었는데 메모리가 늘었으니 안 된다”는 문장 대신 “가드레일을 넘었으니 안 된다” 혹은 “가드레일 안에서 목표가 개선됐으니 된다”는 판정이 즉시 나옵니다. 사람이 매번 트레이드오프를 새로 논쟁할 필요가 없어집니다.

같은 명령, 다른 숫자: 재현되지 않는 측정은 측정이 아니다

같은 명령을 두 번 실행했는데 결과가 다르게 나오는 상황을 마주하면 대개 둘 중 하나를 합니다. 마음에 드는 숫자를 고르거나, 그냥 평균을 내고 넘어갑니다. 둘 다 위험합니다. 왜 달라졌는지를 먼저 밝히지 않으면 다음 측정에서도 똑같은 흔들림이 반복되고, 그때마다 다른 숫자를 고르는 일이 관행이 됩니다.

숫자를 흔드는 원인은 무한하지 않습니다. 실무에서 마주치는 대부분은 네 갈래로 정리됩니다. 시스템이 아직 정상 상태에 도달하지 못했거나, 기준선을 잘못된 시점에 쟀거나, 표본이 하나뿐이거나, 우리 몫이 아닌 자원의 소비를 우리 것으로 잘못 세고 있는 경우입니다. 이 네 가지를 하나씩 짚어보면 왜 측정이 규율의 문제인지가 분명해집니다.

첫 실행이 느린 이유는 다양합니다. 런타임이 아직 코드를 기계어로 컴파일하지 않았고, 페이지 캐시가 비어 있고, 커넥션 풀이 비어 있습니다. GPU를 쓰는 워크로드라면 커널 자동 튜닝과 메모리 할당자 예열이 더해지고, 프로세서 클럭은 부하가 어느 정도 들어온 뒤에야 올라갑니다. 여기서 흔한 오해는 워밍업을 정확도를 높이기 위한 배려로 이해하는 것입니다. 그렇지 않습니다. 워밍업은 우리가 측정하려는 상태 자체를 정의하는 행위입니다. 콜드 스타트 지연을 알고 싶은 사람에게 워밍업을 마친 숫자는 틀린 답이고, 정상 운영 상태의 지연을 알고 싶은 사람에게 콜드 상태 숫자는 틀린 답입니다. 그러니 측정 스펙에는 워밍업 횟수보다 먼저 우리가 재려는 상태가 무엇인지가 문장으로 적혀야 합니다.

정상 상태에 도달했는지는 눈으로 판단할 일이 아니라 수치로 판정할 일입니다. 실용적인 방법은 측정 구간을 여러 개의 창으로 쪼갠 뒤, 최근 세 창의 중앙값이 서로 정해둔 퍼센트 안에 들어오는지 확인하는 것입니다. 이 조건을 만족하기 전의 데이터는 버립니다. 이렇게 하면 워밍업 횟수를 사람이 손으로 정할 필요가 없어지고, 환경이 바뀌어도 절차가 그대로 작동합니다. 표본이 하나뿐인 측정도 같은 계열의 함정입니다. 다섯 번 반복해 사분위 범위를 함께 보고하는 관행이 없으면, 우연히 한 번 잘 나온 숫자가 그대로 보고서에 실립니다.

마지막으로 공유 자원 문제는 클라우드 환경에서 특히 자주 벌어집니다. 같은 노드에서 다른 워크로드가 돌고 있는데 그 영향을 우리 시스템의 성능 저하로 오해하는 경우입니다. 이 문제는 측정 시점에 노드 점유율이나 이웃 워크로드 존재 여부를 함께 기록해 두지 않으면 사후에 절대 밝혀낼 수 없습니다. 원인을 나중에 추정하려 하지 말고, 측정하는 순간에 함께 남겨야 합니다.

벤치마크가 거짓말하는 방식: 의도가 아니라 설계의 문제

벤치마크가 왜곡될 때 대부분은 누가 속이려 해서가 아닙니다. 측정을 설계한 사람은 성실했고, 결과를 발표한 사람도 자기 숫자를 믿었습니다. 문제는 측정이라는 작업에 사람의 기대가 스며들 통로가 아주 많다는 데 있습니다. 그리고 이 왜곡은 특정 도구나 분야에 국한되지 않습니다. 웹 서비스든 데이터베이스든 모델 추론이든 성능 숫자가 오가는 곳이면 같은 방식으로 반복됩니다.

가장 흔하고 가장 크게 결과를 흔드는 왜곡은 합성 부하가 현실의 분포를 닮지 않는 경우입니다. 합성 부하는 만들기 쉽고 재현하기도 쉬워서 매력적이지만, 그 편리함이 곧 위험 신호이기도 합니다. 전형적인 형태는 이렇습니다. 같은 요청을 반복해서 보내면 캐시 적중률이 백 퍼센트에 수렴하고, 시스템은 실제로는 존재하지 않는 성능을 보여줍니다. 요청 크기를 전부 동일하게 맞추면 큰 요청이 만드는 메모리 압박과 꼬리 지연이 통째로 사라집니다. 사용자 도착 시간을 완전히 균등하게 만들면 실제 트래픽의 몰림 현상이 지워지고, 큐가 쌓이는 구간을 벤치마크에서는 영원히 보지 못합니다.

이 왜곡을 막는 방법은 프로덕션 로그에서 뽑은 분포를 부하 생성기의 입력으로 쓰는 것입니다. 요청 종류의 비율, 크기 분포, 도착 간격의 분포를 실제 데이터에서 추정합니다. 완벽하게 재현할 필요는 없고, 최소한 분포의 모양이 닮으면 됩니다. 그리고 벤치마크 스펙에는 이 워크로드가 어느 시점의 어떤 로그에서 유도됐는지를 한 줄로 남깁니다. 이 한 줄이 없으면 반년 뒤에 그 벤치마크는 근거 없는 숫자로 취급되거나, 반대로 무비판적으로 재인용됩니다.

여기서 중요한 지점이 있습니다. 이런 왜곡을 막는 규칙을 문서에 적어두는 것만으로는 충분하지 않습니다. 사람이 지켜야 하는 규칙은 마감이 다가오거나 바쁜 날 가장 먼저 무너지고, 하필 그날 나온 숫자가 승진 발표나 릴리스 승인 같은 중요한 자리에 올라갑니다. 그래서 이 규율은 문서가 아니라 코드로 옮겨야 합니다. 부하 생성기가 스펙에 없는 균일 분포를 기본값으로 쓰려 하면 파이프라인이 실패하도록 만들고, 워크로드 출처 한 줄이 비어 있으면 벤치마크 실행 자체를 막는 식입니다. 규칙이 게이트가 되는 순간에야 바쁜 날에도 살아남습니다.

숫자에 조건이 없으면 숫자가 아니라 소문이다

지표를 골랐고, 환경을 통제했고, 흔한 왜곡을 게이트로 막았다고 해도 아직 끝난 게 아닙니다. 이제 남은 일은 그 숫자를 사람에게 전달하고 결정으로 바꾸는 것입니다. 많은 팀이 바로 이 마지막 단계에서 앞의 모든 노력을 잃습니다. 정교하게 잰 값이 슬라이드 한 장으로 옮겨지면서 조건이 떨어져 나가고, 조건 없는 숫자는 몇 주 뒤에 전혀 다른 맥락에서 인용됩니다.

측정의 마지막 단계는 사실 통계가 아니라 서술입니다. 숫자를 어떻게 적느냐가 그 숫자의 수명을 결정합니다. 보고의 최소 단위는 숫자 하나가 아니라 세 가지의 묶음이어야 합니다. 얼마인지, 얼마나 흔들리는지, 어떤 조건에서였는지입니다. 이 셋 중 하나라도 빠지면 그 문장은 재사용될 수 없는 문장이 됩니다.

“응답 지연 95백분위 180밀리초”라는 문장을 예로 들어보겠습니다. 이것은 절반짜리 문장입니다. 완성된 문장은 이렇습니다. “동시 사용자 300명, 요청 크기 중앙값 4킬로바이트 조건에서 응답 지연 95백분위 180밀리초, 다섯 회 반복의 사분위 범위 174에서 191밀리초.” 길어 보이지만 이 문장은 반년 뒤에도 그대로 쓸 수 있고, 앞의 짧은 문장은 다음 주만 지나도 아무도 무슨 뜻인지 모르게 됩니다. 조건이 빠진 숫자는 데이터가 아니라 소문에 가깝습니다. 누가 인용했는지에 따라 뜻이 계속 바뀌기 때문입니다.

효율 지표라면 여기에 한 가지가 더 필요합니다. 절대값과 순증가분을 함께 내고 각각 이름을 붙이는 일입니다. 서비스 원가를 이야기하는 자리에서는 유휴 상태의 소비까지 포함한 절대값이 맞는 답이고, 요청 하나를 더 처리할 때 드는 한계 비용을 이야기하는 자리에서는 순증가분이 맞는 답입니다. 어느 쪽이 맞는지는 질문이 정합니다. 그러니 둘 다 내고 라벨을 붙여두는 편이 언제나 더 안전합니다.

숫자에 조건을 붙였더라도 그 조건이 산문으로만 존재하면 시간이 지나면서 부정확해집니다. 그래서 측정할 때마다 기계가 읽을 수 있는 원장을 함께 남겨야 합니다. 실행 시각과 실행자, 코드 커밋 해시와 설정 파일의 해시, 하드웨어와 드라이버 버전을 최소한으로 기록합니다. 이 원장이 있으면 “지난달 벤치마크와 지금 벤치마크가 왜 다르냐”는 질문에 기억이 아니라 기록으로 답할 수 있습니다.

왜 이것이 지금 더 중요해졌는가

이 규율이 예전에는 성능 팀만의 관심사였다면, 지금은 사정이 다릅니다. GPU 한 대의 시간당 비용이 크고, 추론 서빙 인프라의 규모는 자동 확장 임계값 하나로 몇 배씩 늘거나 줄어듭니다. 조건 없는 숫자 하나가 서버 증설 결정이나 오토스케일링 임계값의 근거가 되는 순간, 그 숫자의 오차는 곧바로 비용으로 환산됩니다.

더 나쁜 것은 이런 결정이 대개 한 번의 회의로 끝나지 않는다는 사실입니다. 초기에 정한 임계값은 이후 여러 팀이 참조하는 기준선이 됩니다. 콜드 상태에서 잰 지연 시간을 정상 운영 지표로 착각해 오토스케일링 임계값을 낮게 잡으면, 그 시스템은 실제로는 필요하지 않은 순간에도 계속 확장하며 비용을 태웁니다. 반대로 워밍업을 과하게 해서 얻은 낙관적인 숫자를 기준으로 삼으면, 트래픽이 몰리는 순간 시스템이 준비되지 않은 채로 요청을 받게 됩니다. 두 실패 모두 처음에는 조용합니다. 대시보드는 초록색이고, 문제는 몇 달 뒤 특정 시간대에만 터집니다. 그리고 그때는 아무도 그 임계값이 어떤 조건에서 나왔는지 기억하지 못합니다.

모델 추론처럼 지연과 처리량과 비용이 한 몸으로 얽혀 있는 워크로드에서는 이 문제가 더 날카롭게 드러납니다. 목표 지표 하나만 보고 배치 크기를 키우면 처리량은 늘지만 개별 요청의 꼬리 지연이 가드레일을 넘을 수 있습니다. 반대로 지연만 보고 배치 크기를 줄이면 같은 하드웨어로 처리할 수 있는 요청 수가 줄어 원가가 올라갑니다. 목표와 가드레일을 미리 나눠두지 않은 팀은 이 트레이드오프를 매번 새로 논쟁하고, 그 논쟁의 결과는 회의실에 남은 사람의 목소리 크기에 좌우되기 쉽습니다.

규율을 코드로 만드는 법: 오늘 할 수 있는 것

여기까지 읽었다면 남은 질문은 하나입니다. 이 규율을 어떻게 팀의 일상 절차로 옮길 것인가입니다. 핵심은 산문으로 적힌 규칙을 코드가 강제하는 게이트로 바꾸는 데 있습니다. 사람이 기억해서 지키는 규칙은 결국 잊히지만, 파이프라인이 거부하는 규칙은 잊을 수가 없습니다.

가장 먼저 할 일은 측정을 시작하기 전에 목표와 가드레일과 진단 지표를 문서 한 장으로 확정하는 것입니다. 이 문서가 없는 벤치마크 실행은 애초에 시작하지 않습니다. 두 번째로 정상 상태 판정을 자동화합니다. 눈으로 보고 워밍업이 끝났다고 판단하는 대신, 최근 몇 개 창의 중앙값이 서로 얼마나 가까운지를 코드가 계산하고 그 조건을 만족하기 전의 데이터는 자동으로 버립니다. 세 번째로 부하 생성기의 입력을 프로덕션 로그에서 자동으로 뽑아내는 파이프라인을 만듭니다. 사람이 매번 손으로 분포를 흉내 내려 하면 결국 균일 분포로 돌아가게 됩니다.

마지막으로 보고 자동화입니다. 값과 흔들림과 조건을 하나의 문장으로 묶어 자동으로 생성하고, 조건이 비어 있으면 보고서 생성 자체를 실패시킵니다. 여기에 기계가 읽는 원장을 함께 남기면, 다음 팀이 같은 벤치마크를 재현하거나 지난 결과와 비교할 때 산문 기억이 아니라 기록에 의존할 수 있습니다.

이 모든 절차는 처음에는 번거롭게 느껴집니다. 목표 지표 하나를 정하는 데도 팀 안에서 합의가 필요하고, 정상 상태 판정 로직을 짜는 데도 시간이 듭니다. 하지만 이 초기 비용은 한 번만 지불하면 됩니다. 반면 조건 없는 숫자를 근거로 잘못된 결정을 내리는 비용은 매번 다시 지불해야 하고, 대개는 훨씬 늦게, 훨씬 크게 청구됩니다. 다음에 벤치마크 결과를 슬라이드에 올리기 전에, 그 숫자가 어떤 조건에서 나왔는지 한 문장으로 완성할 수 있는지 스스로 물어보시기 바랍니다. 완성할 수 없다면 아직 보고할 준비가 되지 않은 숫자입니다.

태그: benchmarking, engineering-discipline, inference-serving, 관찰성, performance-measurement, sre-practices, statistical-rigor

카테고리:

업데이트: