Devin.KR

CS · 기본

개발자를 위한 컴퓨터 사이언스 - 내 코드가 실제로 도는 곳

요청 하나를 끝까지 따라가기 - 지연 예산, 구간 분해와 병목 확인 (CS 기초 13장)

이름 해석부터 응답 전송까지 한 요청의 지연을 구간별로 나누고 앞 장의 CPU·메모리·I/O·네트워크 개념을 연결해 측정 근거로 조사 순서를 정하는 절차를 익힌다.

개발자 · 원고 갱신

이 장에서 배우는 것

12장에서 이름 해석과 TLS 검증을 구분했다. 마지막 장에서는 1~12장의 개념을 하나의 요청 시간선에 겹쳐 놓는다. 선수지식은 앞 장에서 정리한 계산·대기·전송의 차이다.

  • 누적 시각과 개별 구간의 소요 시간을 구분한다.
  • 가장 큰 지연 구간에 필요한 앞 장의 관측을 연결한다.
  • 계획한 지연 예산과 실제 측정 결과를 분리한다.
  • 느리다는 신고를 구간별 숫자와 검증 가능한 다음 조사로 바꾼다.

개념

사이트가 느리다는 신고를 받고 서버 CPU부터 확인했는데 사용률이 낮다고 가정한다. 누군가는 연결 수를 늘리고 다른 사람은 캐시를 넣자고 제안한다. 아직 어느 구간이 길어졌는지 모르므로 두 처방 모두 근거가 부족하다. 마지막 장의 목표는 앞 장의 용어를 한꺼번에 외우는 것이 아니라 요청 한 건에 필요한 관측 순서를 고르는 것이다.

하나의 시간선에서 경계를 먼저 정한다

이름 해석, 연결 수립, TLS 준비, 첫 응답 대기, 나머지 전송을 구분하면 시간이 쌓이는 위치가 보인다. 도구가 보여 주는 값은 각 구간의 소요 시간이 아니라 시작점부터 누적된 시각일 수 있다. 누적값을 전부 더하면 같은 시간을 여러 번 세게 된다. 수치를 더하기 전에 측정 시작점과 종료점을 적는다.

클라이언트가 첫 바이트를 기다리는 구간에는 서버의 계산만 들어 있지 않다. 요청이 도달하는 시간, 서버 앞의 대기, 파일 읽기나 다른 호출 대기, 첫 응답이 돌아오는 시간까지 섞일 수 있다. 이 값을 그대로 서버 CPU 실행 시간이라고 이름 붙이면 첫 분석부터 잘못된다. 서버 내부 기록이 없으면 클라이언트에서 확인한 경계까지만 결론을 내리는 편이 정확하다.

한 요청의 관측 경계
-------------------
시작 → 이름 완료 → 연결 완료 → TLS 완료
     → 첫 바이트 → 전체 수신 완료
누적값의 차이가 구간의 시간이다.

앞 장의 개념을 의심할 위치에만 겹친다

서버 처리 구간이 길다는 증거가 생기면 2·3장의 CPU와 캐시를 살펴볼 이유가 생긴다. 같은 결과를 만드는 과정에서 4장의 자료구조 선택이 접근 횟수를 늘렸는지도 확인할 수 있다. CPU 사용률이 낮아도 6장의 실행 대기와 제한, 8장의 락 대기, 9장의 I/O 대기는 남아 있다. 지표 하나로 모든 후보를 지우지 않고 해당 요청이 머문 구간에 맞는 관측을 선택한다.

이름과 연결 구간이 길면 서버 내부 계산을 고치는 작업으로는 그 시간을 직접 줄이지 못한다. 10장의 경로와 크기, 11장의 연결 재사용과 상태, 12장의 캐시와 인증 절차를 차례로 연결한다. 응답 크기가 커졌다면 1장의 바이트 수와 9장의 버퍼, 전송 대기도 다시 등장한다. 앞 장의 모든 개념을 무조건 조사하는 대신 증거가 있는 갈래만 깊게 내려간다.

길어진 구간연결할 앞 장다음에 필요한 관측
이름 해석12장 DNS와 캐시해석 위치, 캐시 조건, 결과 주소
연결·TLS 준비10~12장 경로·상태·신뢰새 연결 여부, 실패 단계, 같은 목적지 비교
서버 내부2~9장 계산·메모리·대기처리 시간과 대기 시간의 분리
응답 전송1·9~11장 바이트·버퍼·전송응답 크기, 수신 속도, 재전송 단서

지연 예산은 각 구간에서 허용할 시간을 정한 계획이며 실제 측정값과 섞지 않는다. 목표가 전체 500밀리초라고 정해졌다면 어느 구간이 얼마를 쓰는지 합계부터 맞춘다. 여기서 숫자는 학습용 목표일 뿐 특정 서비스의 성능 기준이 아니다. 모든 구간을 동시에 빠르게 만들려는 시도보다 큰 비중을 차지한 구간에서 설명 가능한 한 가지 변경을 하는 편이 결과 해석에 도움이 된다.

수정 전 기록에는 가설과 기대 변화도 한 줄로 남긴다. 연결 재사용을 바꾸는 실험이라면 준비 구간이 줄어들 것이라는 예상을 먼저 세울 수 있다. 예상한 구간의 변화를 확인한 뒤 개선을 설명한다. 전체 평균만 줄었는데 예상한 구간은 그대로라면 다른 조건이 달라졌는지 다시 비교한다.

요청의 시간은 이름 해석과 연결 준비, 서버 계산 및 자원 대기, 첫 바이트 이후 전송으로 나누어 관찰한다.

요청의 시간은 이름 해석과 연결 준비, 서버 계산 및 자원 대기, 첫 바이트 이후 전송으로 나누어 관찰한다.

직접 확인하기

누적 시각을 겹치지 않는 구간으로 바꾼다

marks = [('name', 20), ('connect', 65), ('tls', 110),
         ('first_byte', 390), ('total', 470)]
previous = 0
durations = {}
for label, value in marks:
    durations[label] = value - previous
    previous = value
print(durations)
print('sum:', sum(durations.values()))

Python 3로 실행하며 입력은 직접 만든 학습용 누적 밀리초 값이다. 실제 사이트나 네트워크에서 측정한 숫자가 아니다. 첫 바이트 대기 280밀리초에는 클라이언트에서 분리하지 못한 서버와 전송의 시간이 함께 들어 있다. 딕셔너리의 이름은 경계 이름이며 서버 내부 작업의 이름이 아니다.

출력
----
{'name': 20, 'connect': 45, 'tls': 45, 'first_byte': 280, 'total': 80}
sum: 470

가장 큰 구간을 고쳐도 남는 시간을 계산한다

parts = {'name': 20, 'connect': 45, 'tls': 45,
         'wait': 280, 'transfer': 80}
before = sum(parts.values())
parts['wait'] //= 2
after = sum(parts.values())
print('before:', before)
print('after:', after)
print('saved:', before - after)

대기 구간만 절반으로 줄인 가정 계산이다. 전체는 470에서 330으로 바뀌므로 해당 구간을 절반으로 고쳐도 전체가 절반이 되지는 않는다. 다른 구간은 변하지 않는다는 가정을 코드에 명시했다. 실제 변경 결과는 동일한 조건에서 다시 관찰해야 하며 이 값은 최적화 성공의 실측 증거가 아니다.

출력
----
before: 470
after: 330
saved: 140

평균 뒤에 숨는 요청과 비교 조건을 남긴다

from statistics import mean, median
samples = [90, 95, 100, 105, 610]
print('mean:', mean(samples))
print('median:', median(samples))
print('maximum:', max(samples))
print('over 300:', sum(x > 300 for x in samples))

다섯 개의 입력은 서로 다른 요청의 가상 밀리초 값이다. 평균 200만 기록하면 대부분의 요청과 가장 느린 요청이 얼마나 떨어져 있는지 사라진다. 작은 표본으로 전체 서비스의 안정성을 선언하지 않는다. 실패한 요청을 목록에서 제외했다면 평균이 좋아 보여도 사용자 경험은 나빠졌을 수 있다.

출력
----
mean: 200
median: 100
maximum: 610
over 300: 1
학습용 비교 기록 양식
---------------------
대상: 같은 경로와 입력 크기
연결: 새 연결 또는 재사용 여부
캐시: 이름·파일·앱 캐시 조건
결과: 성공·실패를 함께 기록
변경: 한 번에 설명 가능한 항목 하나

이 양식은 새로운 성능 도구를 도입하는 절차가 아니다. 앞 장에서 다뤘던 캐시와 연결 조건을 실험 기록에 옮긴 것이다. 이름 해석이 생략된 요청과 새 연결의 요청을 같은 범주로 합치면 평균의 의미가 달라진다. 같은 조건으로 나눌 표본이 부족하면 결론을 좁히고 추가 관측이 필요하다고 적는다.

환경별 차이

환경비교할 때 맞출 것해석의 한계
로컬 리눅스요청 입력과 캐시·프로세스 조건운영망 지연을 재현하지 않음
로컬 macOS같은 입력과 실행 중인 작업커널·하드웨어 차이를 그대로 비교하지 않음
컨테이너CPU·메모리 제한과 볼륨·네트워크 조건호스트 여유가 컨테이너 여유를 뜻하지 않음

실제 구간을 재려면 한 호스트 안에서의 경과 시간에는 시간 조정의 영향을 피하도록 적절한 단조 시계를 사용한다. 다른 호스트가 기록한 시각끼리 빼면 시계 차이가 섞일 수 있다. 클라이언트의 총시간과 서버의 자체 처리 시간을 나란히 둘 수는 있지만 둘이 동일한 경계를 재는지 먼저 확인해야 한다. 이 장의 코드는 가정한 숫자를 계산하므로 시계 차이도 측정하지 않는다.

curl과 같은 도구의 시간 값은 항목별 정의를 읽고 사용한다. 연결 재사용과 리디렉션이 포함되면 단순한 새 연결 한 번의 그림과 다르게 보일 수 있다. 첫 응답이 온 시점과 본문 전체를 받은 시점을 구분하면 전송 크기의 영향을 확인할 수 있다. 최종 원인을 모르는 단계에서는 느린 구간이 확인됐다는 사실과 원인까지 증명됐다는 주장을 분리해 기록한다.

실무에서 자주 틀리는 것

1. 첫 바이트 대기를 서버 계산 시간이라고 부른다

증상은 클라이언트 대기 시간이 긴데 서버 코드의 측정 구간은 짧은 것이다. 흔한 오진은 서버 측 시간 측정이 틀렸다고 단정하는 것이다. 실제로 두 값은 시작과 끝이 다른 경계를 측정하며 중간 대기와 왕복 전달이 포함될 수 있다. 확인은 두 기록의 경계를 그려서 겹치는 부분과 빠진 부분을 표시하는 것이다. 측정 이름만 같아 보인다는 이유로 숫자를 직접 빼지 않는다.

2. CPU가 낮으면 서버 쪽 원인을 모두 지운다

증상은 사용률이 낮은데 요청 대기 시간이 계속 늘어나는 것이다. 흔한 오진은 네트워크만 조사하면 된다는 것이다. 실제로 락, I/O, 자원 제한이나 실행 기회 대기가 서버 내부에 남을 수 있다. 확인은 해당 요청의 실행 시간과 대기를 나눌 수 있는 기록을 찾는 것이다. 반대로 CPU가 높다는 사실도 그 요청이 CPU에서 대부분의 시간을 썼다는 직접 증거는 아니다.

3. 여러 설정을 동시에 바꾸고 평균만 비교한다

증상은 새 설정에서 평균은 좋아졌지만 일부 사용자의 실패가 늘어난 것이다. 흔한 오진은 표본의 흔들림으로 치부하는 것이다. 실제로 연결 조건, 캐시, 부하가 함께 바뀌었거나 실패 요청이 집계에서 빠졌을 수 있다. 확인은 변경 항목과 요청 조건을 맞추고 성공·실패·느린 요청을 함께 보는 것이다. 되돌릴 수 있는 한 가지 변경으로 가설을 확인해야 효과를 다른 구간의 변화와 구분할 수 있다.

스스로 확인하기

  1. 누적 시각이 이름 20, 연결 65, TLS 110, 첫 바이트 390, 전체 470밀리초다. TLS 준비 시간과 첫 바이트 이후 전송 시간을 계산하라.
  2. 대기 280밀리초를 절반으로 줄이고 나머지 190밀리초가 같다면 전체 시간이 얼마나 되는가. 전체도 절반이 되는지 판단하라.
  3. 같은 요청의 평균만 개선됐고 실패율을 기록하지 않았다. 성능 개선을 확정하기 전에 보완할 기록을 설명하라.

해설 1. TLS 준비는 110에서 65를 뺀 45밀리초다. 첫 바이트 이후는 470에서 390을 뺀 80밀리초다. 누적값을 모두 더하지 않는다.

해설 2. 140과 190을 더한 330밀리초다. 전체 절반인 235밀리초와 다르므로 어느 구간만 개선했는지 함께 설명해야 한다.

해설 3. 성공·실패 수와 느린 요청의 분포, 연결 재사용·캐시·입력 크기 조건을 남긴다. 실패를 제외한 평균은 모든 요청의 경험을 대표하지 못한다.

출처·작성 안내: 본문 설명, 예제, 문제와 도식은 이 장을 위해 직접 작성했다. 아래 문서는 기술적 사실 확인에 참고했으며 원문 문장·표·그림·코드를 발췌하거나 번역하지 않았다.

참고: curl 시간 측정 항목 문서, Python time 문서, Python statistics 문서

이 책의 끝에서 남겨야 할 것은 원인을 즉시 맞히는 요령이 아니다. 값이 어떤 바이트로 표현됐고 어느 자원에서 계산되며 어디에서 기다렸는지, 확인한 경계까지 설명하는 습관이다. 다음 조사 하나를 증거로 선택할 수 있으면 한 요청의 경로를 실제 코드와 연결할 수 있다.

READER FEEDBACK

질문·오탈자·의견

내용에 관한 질문이나 오탈자, 더 나은 설명을 위한 의견을 남겨 주세요. 이 댓글은 원래 게시글과 같은 자리에 쌓입니다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.