사전 성능 분석
개념

성능 분석

gabury1고친 사람 github-actions[bot]

성능 분석은 시스템이 느린 까닭을 재서 찾아내는 일입니다. 어디가 느린지 짐작으로 고르는 대신 시간과 자원이 어디에 쓰이는지 숫자로 보고 고칠 곳을 정합니다. 알고리즘 분석에서는 같은 이름으로 코드의 복잡도를 따지는 일을 부르기도 합니다. 이 항목은 돌아가는 시스템을 재는 쪽을 다룹니다.

쉽고 빠른 이해

성능 분석은 느려진 시스템에서 시간이 어디로 갔는지 재서 원인을 짚는 일입니다. 주문 조회가 느려졌다면 데이터베이스 조회 탓인지, 다른 서버를 부르느라 기다린 탓인지를 가려 줍니다.

짐작은 자주 빗나갑니다. 코드에서 복잡해 보이는 곳과 시간을 많이 쓰는 곳이 다른 일이 흔합니다. 엉뚱한 곳을 고치면 품만 들고 응답은 그대로 느립니다.

  1. 무엇이 얼마나 느린지 숫자로 적습니다
  2. 서비스 전체에서 출발해 느린 요청, 느린 구간, 느린 함수 순으로 좁혀 들어갑니다
  3. 하나를 고치고 같은 조건에서 다시 재서 나아졌는지 봅니다

느리다는 증상과 닿아야 할 목표가 있을 때 시작합니다. 증상 없이 미리 코드를 다듬는 일은 성능 분석이 아닙니다.

대가도 있습니다. 재는 도구가 시스템을 조금 느리게 만듭니다. 개발 기계에서 잰 결과가 운영 서버와 다를 때도 있습니다.

상세

열이 난다고 찾아온 환자에게 의사는 바로 약을 주지 않습니다. 체온을 재고 피를 뽑아 검사한 뒤 결과를 보고 원인을 좁혀서 처방합니다. 약이 들었는지는 며칠 뒤 다시 재서 확인합니다.

성능 분석은 시스템이 느리거나 자원을 지나치게 쓸 때 그 까닭을 재서 찾아내는 일입니다. 주문 목록을 돌려주는 요청이 지난주부터 눈에 띄게 느려졌다고 해 봅시다. 데이터베이스 조회가 느려졌을 수 있습니다. 다른 서버를 부르는 호출이 늦어졌을 수도 있습니다. 성능 분석은 이 후보들 가운데 어느 것이 원인인지 숫자로 가립니다.

짐작으로 고르면 자주 틀립니다. 복잡해 보이는 반복문보다, 단순해 보여도 요청마다 수백 번 불리는 함수가 시간을 더 쓰는 일이 흔합니다. 틀린 곳을 고치면 품은 들고 응답은 그대로입니다.

성능 분석은 고치기 전에 한 번 재고 고친 뒤에 한 번 더 잽니다. 앞의 것은 어디를 고칠지 정합니다. 뒤의 것은 고친 것이 효과가 있었는지 확인합니다.

재는 잣대

분석은 무엇을 잴지 정하는 데서 시작합니다. 「느리다」는 말은 사람마다 뜻하는 것이 다릅니다. 잣대를 먼저 정해야 원인을 좁힐 수 있습니다. 자주 쓰는 잣대는 아래 다섯입니다.

다섯은 재는 곳으로 갈립니다. 응답 시간·처리량·오류율은 요청 쪽에서 잽니다. 사용률·포화는 서버의 부품 쪽에서 잽니다. 부품은 프로세서나 메모리, 디스크처럼 서버에서 일을 맡는 하드웨어입니다.

잣대 무엇을 세나 답하는 물음
응답 시간 요청 하나가 들어와 답을 받기까지 걸린 시간 사용자가 얼마나 기다리나
처리량 같은 시간 안에 끝낸 요청의 건수 한꺼번에 얼마나 받아 내나
사용률 부품이 일하느라 바빴던 시간의 비율 서버가 얼마나 차 있나
포화 부품이 더 못 받아서 줄을 선 일의 양 넘친 일이 쌓이고 있나
오류율 실패로 끝난 요청의 비율 빨라진 대신 실패가 늘지 않았나

응답 시간은 지연이라고도 부릅니다. 이 항목에서는 응답 시간으로 씁니다.

응답 시간은 평균 하나로 보면 속기 쉽습니다. 요청 백 건 가운데 아흔아홉 건은 금방 끝났습니다. 한 건만 아주 오래 걸렸다면 평균은 거의 안 움직입니다. 그런데 그 한 건을 받은 사용자는 한참 기다립니다.

그래서 요청을 걸린 시간 순으로 줄 세워 놓고 특정 순번의 값을 봅니다. 백 건을 빠른 순으로 세웠을 때 아흔아홉 번째 요청이 걸린 시간을 99번째 백분위수라고 부릅니다. 흔히 p99 라고 적습니다. 평균은 멀쩡하고 p99 만 늘었다면 무언가가 일부 요청만 붙잡고 있다는 신호입니다.

넓게 보고 좁혀 들어가기

원인을 찾을 때는 넓은 데서 시작해 좁은 데로 내려갑니다. 처음부터 함수 하나를 들여다보면 그 함수가 원인이 아닐 때 시간만 버립니다. 층마다 쓰는 도구가 다릅니다.

flowchart TD
    A["서비스 전체<br/>메트릭 · 언제부터 무엇이 나빠졌나"] --> B["느린 요청<br/>로그 · 어느 요청이 느린가"]
    B --> C["느린 구간<br/>트레이스 · 요청 안의 어느 구간인가"]
    C --> D["느린 함수<br/>프로파일러 · 어느 코드가 시간을 쓰나"]

메트릭은 응답 시간이나 처리량 같은 값을 일정한 간격으로 모아 둔 숫자 기록입니다. 그래프로 그려 두면 언제부터 어느 잣대가 나빠졌는지 보입니다. 나빠지기 시작한 시각을 배포 시각과 겹쳐 보면 후보가 크게 줄어듭니다.

로그는 요청 하나하나가 남긴 글 기록입니다. 요청마다 걸린 시간을 적어 두면 느린 요청이 특정 주소나 특정 사용자에게 몰려 있는지를 가를 수 있습니다.

요청 하나는 안에서 여러 구간을 지납니다. 구간마다 걸린 시간을 이어 붙인 기록이 트레이스입니다. 주문 조회 요청 한 건의 트레이스가 이렇게 나왔다고 해 봅시다.

구간 걸린 시간
로그인 확인 0.02초
주문 목록 조회 0.85초
배송 서버 호출 0.20초
응답 만들기 0.05초
합 1.12초

이 요청에서는 주문 목록 조회가 시간의 대부분을 먹었습니다. 응답 만들기를 아무리 잘 고쳐도 줄일 수 있는 것은 0.05초가 전부입니다. 전체에서 몫이 작은 부분은 빨라져도 전체가 조금밖에 안 바뀝니다. 이 관계를 암달의 법칙이라고 부릅니다.

느린 구간이 우리 코드 안이라면 프로파일러를 붙입니다. 프로파일러는 프로그램이 도는 동안 함수마다 시간을 얼마나 썼는지 세어 주는 도구입니다. 이렇게 재는 일을 프로파일링이라고 부릅니다.

느린 구간이 데이터베이스 조회라면 들여다볼 것이 다릅니다. 데이터베이스가 그 쿼리를 어떤 순서로 처리하는지 적은 실행 계획을 봅니다. 찾는 행을 곧장 짚지 못하고 테이블을 처음부터 끝까지 읽고 있다면 그것이 원인 후보입니다.

부품에서 출발하는 방법

앞의 순서는 느린 요청에서 출발합니다. 반대로 서버의 부품에서 출발하는 방법도 있습니다. 어느 요청이 느린지 아직 모르거나 서버 전체가 굼뜰 때 씁니다.

부품을 하나씩 꼽으면 CPU(Central Processing Unit, 중앙처리장치)와 메모리, 디스크, 네트워크입니다. 하나가 가득 차면 그 부품을 쓰는 요청이 모두 줄을 서게 됩니다.

USE 방법(Utilization Saturation Errors, 사용률·포화·오류)은 부품마다 세 가지를 차례로 확인합니다. 얼마나 바빴나, 못 받은 일이 줄을 섰나, 오류가 났나입니다. 부품 목록을 빠짐없이 훑으므로 짐작이 끼어들 틈이 줄어듭니다.

여기서 오류는 부품 자체가 낸 오류입니다. 디스크 읽기가 실패하거나 네트워크에서 패킷이 사라지는 일이 그렇습니다. 앞의 표에서 요청 쪽에서 잰 오류율, 곧 실패로 끝난 요청의 비율과는 다른 것입니다.

두 출발점은 중간에서 만납니다. 트레이스로 찾은 느린 구간이 데이터베이스 서버라면, 그 서버의 부품을 USE 방법으로 훑어 이어 갈 수 있습니다.

고치고 다시 재기

원인을 찾았다고 끝나지 않습니다. 응답이 나아졌는지는 다시 재야 압니다. 처음에 숫자로 적어 둔 목표에 닿을 때까지 아래 순서를 되풀이합니다.

flowchart TD
    A["무엇이 얼마나 느린지 숫자로 적기"] --> B["좁혀 들어가 원인 찾기"]
    B --> C["하나만 고치기"]
    C --> D["같은 조건에서 다시 재기"]
    D --> E{"목표에 닿았나"}
    E -->|예| F["끝"]
    E -->|아니오| B

한 번에 하나만 바꿉니다. 둘을 같이 바꾸면 어느 쪽이 들었는지 모릅니다. 하나가 나빠지고 다른 하나가 좋아져서 서로 가려지는 일도 생깁니다.

같은 조건에서 잽니다. 들어오는 요청의 양이나 데이터 크기가 다르면 두 숫자를 견줄 수 없습니다. 견주는 기준으로 삼는 고치기 전의 값을 기준선이라고 부릅니다.

운영 서버에서 같은 조건을 다시 만들기는 어렵습니다. 그래서 시험 환경에 요청을 일부러 쏟아부어 느린 상황을 다시 만드는 부하 테스트를 씁니다.

하나를 고치면 원인이 다른 곳으로 옮겨 가기도 합니다. 전체 속도를 혼자 정하는 구간이나 부품을 병목이라고 부릅니다. 병목을 넓히면 그다음으로 느린 구간이나 부품이 새 병목이 됩니다. 목표에 못 닿았다면 좁혀 들어가기로 돌아갑니다.

성능 분석을 시작하는 때

성능 분석은 증상이 있을 때 시작합니다. 느리다는 증상과 닿아야 할 목표가 있어야 어디서 멈출지를 압니다. 흔히 아래 셋이 계기가 됩니다.

계기 부르는 이름
응답 시간이 팀이 약속한 목표를 넘겼다 서비스 수준 목표
배포 뒤에 전보다 느려졌다 성능 회귀
요청이 늘 것에 대비해 서버를 얼마나 준비할지 정한다 용량 계획

증상 없이 코드를 빠르게 다듬는 일은 성능 분석이 아닙니다. 재 보기 전에 다듬으면 몫이 작은 곳에 품을 쓰기 쉽습니다. 이렇게 재기도 전에 다듬는 것을 성급한 최적화라고 부릅니다.

재는 일의 대가

재는 도구도 시간을 씁니다. 함수가 불릴 때마다 빠짐없이 기록하는 도구는 프로그램을 눈에 띄게 느리게 만들기도 합니다. 운영 서버에서는 일정한 간격으로 지금 무엇을 하는지만 엿보는 샘플링 방식을 흔히 씁니다.

환경이 다르면 결과도 다릅니다. 데이터가 적은 개발 기계에서는 안 보이던 느린 쿼리가, 데이터가 쌓인 운영 서버에서만 드러나기도 합니다. 개발 기계에서 잰 숫자는 운영 서버의 답을 대신하지 못합니다.

알고리즘 분석에서 말하는 성능 분석

같은 이름이 알고리즘 분석에서는 다른 일을 가리킵니다. 거기서 성능 분석은 코드를 돌리지 않고 종이 위에서 따지는 일입니다.

입력이 커질수록 연산 횟수가 얼마나 빨리 늘어나는지를 따지는 것이 시간 복잡도입니다. 입력이 두 배가 될 때 연산도 두 배로 느는 코드와 네 배로 느는 코드를 견줘 봅시다. 입력이 작을 때는 둘이 비슷해 보입니다. 입력이 커지면 크게 벌어집니다.

두 뜻은 이어집니다. 시스템을 재서 찾아낸 느린 함수를 열어 보면, 목록 안에서 목록을 다시 훑는 반복문처럼 시간 복잡도가 큰 코드일 때가 있습니다. 어디가 느린지는 재서 찾습니다. 왜 느린지는 따져서 압니다.

관련 항목

성능 분석이 재는 지표

응답 시간 · 지연 · 처리량 · 사용률 · 포화 · 오류율 · 평균 · 중앙값 · 백분위수 · 꼬리 지연

성능 분석에 쓰는 도구

모니터링 · 메트릭 · 로그 · 트레이스 · 분산 추적 · 프로파일러 · 플레임 그래프 · 실행 계획 · 슬로 쿼리 로그 · 힙 덤프 · 스레드 덤프 · APM

성능 분석을 이끄는 방법론

USE 방법 · RED 방법 · 프로파일링 · 드릴다운 분석 · 워크로드 특성화 · 샘플링 · 기준선 · 동적 분석

분석할 부하를 만드는 시험

부하 테스트 · 스트레스 테스트 · 성능 테스트 · 벤치마크 · 마이크로벤치마크

성능 분석이 찾아내는 원인

병목 · 핫스팟 · 락 경합 · 가비지 컬렉션 · N+1 문제 · 메모리 누수 · 큐 대기

분석 결과를 해석하는 법칙

암달의 법칙 · 리틀의 법칙 · 큐잉 이론

성능 분석을 시작하게 하는 계기

서비스 수준 목표 · 서비스 수준 지표 · 성능 회귀 · 용량 계획

성능 분석 결과로 하는 개선 작업

성능 튜닝 · 최적화 · 성급한 최적화 · 캐싱 · 수평 확장

성능 분석이 재는 하드웨어 자원

CPU · 메모리 · 디스크 · 네트워크

성능 분석이 속하는 상위 분류

성능 · 성능 공학 · 관측성

같은 이름으로 불리는 알고리즘 분석 개념

시간 복잡도 · 공간 복잡도 · 빅오 표기법 · 점근 표기법 · 알고리즘 분석

다른 이름: performance analysis · 퍼포먼스 분석 · 성능 진단