"Mạng nhà tôi nhanh, 1 Gbps đấy" — nhưng tải một file từ server bên kia địa cầu vẫn ì ạch. Nghịch lý này đến từ việc gộp hai đại lượng hoàn toàn khác nhau vào một chữ "nhanh": băng thông (bao nhiêu dữ liệu mỗi giây) và độ trễ (một gói đi-về mất bao lâu). Chúng độc lập, và một khái niệm nối chúng lại — Bandwidth-Delay Product — giải thích vì sao đường băng thông cao mà độ trễ lớn vẫn có thể truyền chậm. Bài này (phần 10 loạt Mạng) chạy thật để tách hai thứ này và cho thấy cửa sổ TCP quyết định throughput ra sao.

Cơ chế: hai đại lượng độc lập và BDP

  • Băng thông (throughput): lượng dữ liệu chuyển được mỗi giây — MB/s, Gbps. Như độ rộng của ống nước.
  • Độ trễ (RTT): thời gian một gói đi tới và ACK quay về — ms. Như độ dài của ống.

Hai thứ này độc lập: đường vệ tinh có băng thông cao nhưng RTT ~600ms; đường sợi quang gần vừa băng thông cao vừa RTT thấp. Nói "nhanh" mà không rõ nhanh về cái gì là mơ hồ.

Bandwidth-Delay Product (BDP = băng thông × RTT) là lượng dữ liệu tối đa "đang bay" trên đường cùng lúc — dung tích của ống. Điểm mấu chốt: cửa sổ TCP phải ≥ BDP mới lấp đầy được ống. Nếu cửa sổ nhỏ hơn BDP, bên gửi gửi hết một cửa sổ rồi phải chờ ACK trước khi gửi tiếp — ống rỗng một phần thời gian, throughput sụt dù băng thông vật lý còn nguyên. Đây là hiện tượng "đường dài béo" (Long Fat Network).

// throughput: gửi khối lớn, đo MB/s
sent += c.Write(buf); bw := total / seconds
// RTT: ping-pong 1 byte, đo thời gian đi-về
c.Write(b); c.Read(b)  // lặp N lần, chia trung bình
// cửa sổ nhỏ giới hạn throughput: SetReadBuffer/SetWriteBuffer nhỏ

Ảnh chụp đoạn mã Go nền tối minh hoạ băng thông vs độ trễ, hai đại lượng khác nhau và độc lập băng thông bandwidth throughput bao nhiêu dữ liệu mỗi giây MB/s Gbps độ trễ latency RTT một gói đi-về mất bao lâu ms độc lập đường vệ tinh băng thông cao nhưng RTT cao 600ms đường sợi quang gần băng thông cao và RTT thấp nhanh là mơ hồ nhanh về throughput hay độ trễ, Bandwidth-Delay Product BDP dung tích ống BDP bằng băng thông nhân RTT dữ liệu tối đa đang bay cùng lúc vd 1 Gbps nhân RTT 100ms bằng 12.5 MB đang bay cửa sổ TCP phải lớn hơn hoặc bằng BDP mới lấp đầy ống cửa sổ nhỏ hơn BDP gửi xong 1 cửa sổ phải chờ ACK ống rỗng throughput thấp dù băng thông lớn đường dài béo LFN, đo bằng Go throughput gửi khối lớn đo MB/s sent cộng c Write buf bw bằng total chia seconds RTT ping-pong 1 byte đo thời gian đi-về c Write b c Read b lặp N lần chia trung bình cửa sổ nhỏ giới hạn throughput SetReadBuffer SetWriteBuffer nhỏ

Hình 1: Băng thông (dữ liệu/giây) và độ trễ (RTT) là hai đại lượng độc lập; BDP = băng thông × RTT là lượng dữ liệu đang bay — cửa sổ TCP phải ≥ BDP mới lấp đầy ống, nếu không throughput sụt.

Đo thật: throughput, RTT, và ảnh hưởng cửa sổ

Mình đo throughput (truyền 500MB), RTT (ping-pong), rồi giảm cửa sổ để thấy throughput sụt:

Ảnh chụp bảng kết quả chạy thật băng thông độ trễ output thật, một băng thông và độ trễ đo riêng băng thông 500MB 10.562 MB/s khoảng 84.5 Gbps localhost khủng độ trễ RTT ping-pong 1 byte 3.147 µs localhost RTT 0 hai con số độc lập đo bằng hai phép khác nhau, hai Bandwidth-Delay Product BDP localhost bằng 10.562 MB/s nhân 3µs bằng khoảng 32 KB nhỏ vì RTT 0 ví dụ đường thực 1 Gbps nhân RTT 100ms bằng 12.5 MB đang bay cùng lúc localhost BDP tí hon khó thấy hiệu ứng đường dài béo trên đường xuyên lục địa cửa sổ nhỏ hơn 12.5MB bằng không lấp đầy ống, ba cửa sổ nhỏ giới hạn throughput đo thật 10MB buffer 8 KB throughput khoảng 0 MB/s gần như đứng buffer 32 KB 7.716 MB/s buffer 128 KB 9.706 MB/s buffer mặc định auto-tune 12.210 MB/s cửa sổ càng nhỏ throughput càng thấp dù băng thông vật lý y nguyên, kết luận throughput và RTT là hai thứ khác nhau đo riêng BDP bằng bw nhân RTT quyết định cửa sổ cần để lấp đầy ống cửa sổ nhỏ trên đường RTT cao throughput thấp dù băng thông lớn muốn nhanh trên đường dài béo tăng cửa sổ auto-tune window scaling

Hình 2: Chạy thật — băng thông 10.562 MB/s (~84,5 Gbps) và RTT 3,147µs là hai số độc lập; BDP localhost 32KB (RTT0), ví dụ đường thực 1Gbps×100ms = 12,5MB; cửa sổ nhỏ giới hạn throughput: 8KB→~0, 32KB→7716, 128KB→9706, mặc định→12210 MB/s.

Đọc kết quả đo được:

  • Throughput và RTT là hai con số khác nhau, đo riêng: băng thông trên localhost đạt 10.562 MB/s (~84,5 Gbps — vì không qua card mạng vật lý), còn RTT ping-pong chỉ 3,147µs. Chúng đo bằng hai phép hoàn toàn khác nhau (truyền khối lớn vs đi-về gói nhỏ), và không suy ra được cái này từ cái kia.
  • BDP làm rõ "đường dài béo": BDP localhost chỉ 32KB (vì RTT0), nên trên localhost ta không thấy hiệu ứng cửa sổ giới hạn trong điều kiện thường. Nhưng phép tính cho đường thực rất sáng tỏ: 1 Gbps × RTT 100ms = 12,5MB dữ liệu đang bay cùng lúc — nếu cửa sổ TCP nhỏ hơn 12,5MB, bạn không thể dùng hết băng thông đó.
  • Cửa sổ nhỏ bóp nghẹt throughput: khi ép buffer nhỏ, throughput sụt rõ: 8KB → ~0 (gần như đứng), 32KB → 7716, 128KB → 9706, mặc định (auto-tune) → 12210 MB/s. Băng thông vật lý không đổi, nhưng cửa sổ nhỏ khiến ta không lấp đầy được ống — đúng cơ chế BDP. Đây là bằng chứng đo được cho câu "đường nhanh vẫn có thể truyền chậm".

Đánh đổi cần cân nhắc

Vấn đề của bạn là băng thông hay độ trễ? Lời giải khác nhau hoàn toàn. Nếu tải file lớn chậm → thiếu băng thông (hoặc cửa sổ nhỏ so với BDP) → tăng cửa sổ, nén, dùng CDN gần hơn. Nếu ứng dụng tương tác (game, gọi API qua lại) giật → thiếu độ trễ → đưa server gần người dùng, giảm số vòng đi-về (gộp request, HTTP/2), không phải tăng băng thông. Chẩn đoán nhầm trục này dẫn tới tối ưu vô ích: mua thêm băng thông không làm ứng dụng độ-trễ-nhạy nhanh hơn.

Độ trễ có sàn vật lý không thể vượt. Băng thông có thể tăng bằng tiền (đường lớn hơn, nhiều đường song song), nhưng độ trễ bị chặn bởi tốc độ ánh sáng: một tín hiệu đi vòng quanh Trái Đất tốn tối thiểu ~130ms dù đường có "rộng" đến đâu. Đây là lý do CDN và edge tồn tại — không thể làm ánh sáng nhanh hơn, chỉ có thể đặt dữ liệu gần người dùng để giảm quãng đường.

Auto-tune cửa sổ là mặc định tốt, nhưng biết khi nào cần chỉnh. Linux hiện đại tự động điều chỉnh cửa sổ (TCP window scaling + auto-tuning) khá tốt cho đa số trường hợp — như đo thấy, buffer mặc định cho throughput cao nhất. Chỉ chỉnh tay tcp_rmem/tcp_wmem khi bạn có đường dài béo cụ thể (truyền dữ liệu lớn xuyên lục địa) và đo được auto-tune không đạt BDP. Đừng chỉnh mò.

Ba ý mang về

  1. Băng thông và độ trễ là hai đại lượng độc lập: đo thật throughput 10.562 MB/s và RTT 3,147µs bằng hai phép khác nhau — "nhanh" phải nói rõ nhanh về throughput hay độ trễ, vì lời giải cho mỗi vấn đề khác hẳn nhau.
  2. BDP = băng thông × RTT quyết định cửa sổ cần thiết: đo thật cửa sổ nhỏ bóp throughput từ 12.210 xuống ~0 MB/s dù băng thông vật lý không đổi; trên đường dài béo (1Gbps×100ms=12,5MB), cửa sổ nhỏ hơn BDP thì không lấp đầy được ống.
  3. Chẩn đoán đúng trục và nhớ sàn vật lý của độ trễ: file chậm → băng thông/cửa sổ; app tương tác giật → độ trễ (đưa gần, giảm vòng đi-về); độ trễ bị chặn bởi tốc độ ánh sáng nên CDN/edge là lời giải, không phải mua thêm băng thông.

Nguồn

Phần sau ta lên tầng ứng dụng: HTTP/2 multiplexing — nhiều request chạy song song trên một kết nối TCP, giải quyết vấn đề head-of-line blocking của HTTP/1.1; đo thật nhiều request song song trên một kết nối so với HTTP/1.1.