합동회사 코무라소프트
5장

혼잡 제어 — TCP 는 네트워크의 혼잡에 양보한다

경로의 혼잡 정도는 아무도 알려 주지 않는다. TCP 는 cwnd 라는 자체 창을 배증으로 넓히고, 패킷 로스를 신호로 양보한다 — 탐색하면서 보내는 구조를 손계산으로 따라간다.

상대는 멀쩡해도, 길이 막혀 있다

제 4 장의 흐름 제어로, 상대를 익사시키는 일은 없어졌습니다. 그렇다면 rwnd 가 허락하는 한 전력으로 보내도 될까요. 답은 아니오입니다. 송신 측과 수신 측 사이에는 남의 통신도 흘러드는 공유 네트워크가 있습니다. 도중 라우터의 처리 능력을 넘은 분량은 큐에 쌓이고, 큐가 넘치면 버려집니다. 전원이 전력으로 보내면, 재전송이 재전송을 부르고, 네트워크 전체가 막혀 누구의 데이터도 흐르지 않게 됩니다(혼잡 붕괴 — 1980 년대에 실제로 일어난 사고입니다).

골치 아픈 것은, 경로의 빈 용량은 아무도 알려 주지 않는다는 점입니다. rwnd 같은 신고는 없습니다. 그래서 TCP 는 약속 그 3 의 후반으로서, 송신 측이 자력으로 경로의 용량을 추정하는 혼잡 제어를 갖습니다. 추정값은 또 하나의 창 — 혼잡 윈도우(cwnd) — 로서 갖고, 실제로 보내도 좋은 양은 min(rwnd, cwnd) 로 정해집니다. 상대의 사정과 길의 사정, 엄격한 쪽에 맞추는 것입니다.

cwnd 가 시간과 함께 슬로 스타트로 지수적으로 늘고, 임계값부터는 1 씩의 직선적인 증가로 바뀌며, 로스의 감지로 절반 또는 1 로 떨어져 다시 올라가는 그래프풍의 그림

cwnd 의 일생. 배증으로 달려 올라가고(슬로 스타트), 임계값부터는 한 걸음씩(혼잡 회피), 로스로 양보하고 다시 올라간다. 삐죽삐죽한 톱니는 TCP 가 건전하게 한계를 계속 탐색하고 있는 모습입니다.

탐색 방식 — 배증으로 달리고, 한 걸음씩 좁힌다

용량을 알 수 없는 길에서는, 적게 시작해서 상황을 보며 늘려 갈 수밖에 없습니다. TCP 의 탐색 방식은 2 단 변속입니다.

슬로 스타트 — 매 RTT 마다 배증
cwnd = 1 MSS(세그먼트 1 개 분량)부터 시작해, ACK 가 무사히 돌아올 때마다 늘린다. 결과적으로 1 → 2 → 4 → 8 → 16… 으로 1 RTT 마다 배가 된다. 이름과 달리 지수적인 급가속
혼잡 회피 — 매 RTT 마다 +1
임계값(ssthresh: 지난번 로스가 일어난 수준의 기억)을 넘으면 덧셈으로 전환하여, 1 RTT 에 1 MSS 씩. 한계의 근처를 살며시 탐색한다

그리고 경로의 한계를 넘으면 패킷 로스가 일어나, 제 3 장의 감지가 작동합니다. 여기서 증거의 무게에 따라 벌을 바꾸는 것이 TCP 의 지혜입니다.

중복 ACK 3 회(경증)
후속은 도착하고 있다 = 길은 살아 있다. cwnd 를 대략 절반으로 낮추고, 그대로 혼잡 회피로 계속
타임아웃(중증)
아무것도 돌아오지 않는다 = 심각한 막힘. cwnd = 1 MSS 로 되돌리고, 슬로 스타트부터 다시

늘리고, 부딪히고, 양보하고, 다시 늘린다. cwnd 의 그래프가 그리는 톱니 모양의 파도는, TCP 가 네트워크의 용량을 모두가 나누어 갖기 위해 계속 지불하고 있는 인사 같은 것입니다. 누군가가 양보하지 않는 설계였다면, 인터넷은 성립하지 않았을 것입니다.

소문제 5-1 — 2 개의 브레이크를 구별한다

「누구의 사정에 맞추고 있는가」로 2 개의 창을 구별해 말할 수 있게 합니다.

Q1. 흐름 제어와 혼잡 제어의 차이에 대한 설명으로 올바른 것은 어느 것입니까.

Q2. TCP 가 패킷 로스를 「혼잡의 신호」로 사용하는 것은 왜입니까.

Q3. 송신 측이 어느 순간에 보내도 좋은 데이터 양(in-flight 의 상한)을 정하는 것은 어느 것입니까.

실무에서 어떻게 보이는가

혼잡 제어는 평소에는 투명하지만, 성능 문제에서는 반드시 얼굴을 내밉니다.

  • 접속 직후에는 누구나 느리다 — 아무리 굵은 회선이라도 cwnd = 1 MSS 근처부터 탐색을 시작하므로, 짧은 통신은 한 번도 최고 속도에 도달하지 않습니다. 작은 요청을 대량으로 다시 맺기보다, 1 개의 커넥션을 재사용하는(keep-alive) 편이 빠른 이유가 이것입니다.
  • 로스 1 회의 비용은 「재전송 1 개」로 끝나지 않는다 — cwnd 가 절반(또는 1)으로 떨어지므로, 스루풋의 회복에 몇 RTT 나 걸립니다. 로스율 1% 는 「1% 느려진다」가 아니라, 자릿수가 다르게 느려질 수 있습니다.
  • 속도 상한 정체에는 3 명의 용의자 — rwnd(상대의 버퍼), cwnd(경로의 혼잡), 그리고 대역폭 그 자체. 제 4 장의 식에 대입하여, 어느 창이 min 을 차지하고 있는지를 생각하는 것이 구분의 정석입니다.

소문제 5-2 — cwnd 의 성장을 손으로 계산한다

cwnd 는 세그먼트 수(MSS 단위)로 셉니다. 배증과 덧셈, 2 가지 성장을 계산해 주세요.

Q4. 슬로 스타트로 cwnd = 1 MSS 부터 송신을 시작했습니다. 1 RTT 마다 cwnd 가 2 배가 될 때, 3 RTT 경과 후의 cwnd 는 몇 MSS 입니까.

MSS

Q5. 임계값(ssthresh)을 넘어 혼잡 회피 모드에 들어가, cwnd = 10 MSS 부터 1 RTT 마다 1 MSS 씩 늘리고 있습니다. 5 RTT 경과 후의 cwnd 는 몇 MSS 입니까.

MSS

Q6. 로스를 감지했을 때의 TCP 의 반응은 감지 방식에 따라 다릅니다. 올바른 조합은 어느 것입니까. (제 3 장의 2 가지 감지 방법을 떠올려 주세요)

이 장에서 가져갈 것

  • 흐름 제어는 상대의 사정, 혼잡 제어는 네트워크의 사정. 보내도 좋은 양은 min(rwnd, cwnd)
  • 경로의 용량은 신고되지 않으므로, 패킷 로스를 혼잡의 신호로 삼아 자력으로 탐색한다
  • 성장은 2 단 변속 — 슬로 스타트는 매 RTT 마다 배증, 혼잡 회피는 매 RTT 마다 +1 MSS
  • 벌도 2 단계 — 중복 ACK 3 회로 절반, 타임아웃으로 1 로 되돌려 다시 시작. 톱니의 파도는 건전한 양보의 모습

이것으로 3 가지 약속이 모두 갖춰졌습니다. 다음 장은 시점을 앱 측으로 옮깁니다. TCP 가 완벽하게 약속을 지키고 있는데도, 앱의 데이터가 「빠지고·붙는」 것처럼 보인다 — 실무에서 가장 많은 TCP 의 오해, 바이트 스트림과 프레이밍의 이야기입니다.