"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ỏ

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:

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ề
- 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.
- 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.
- 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
- Wikipedia — Bandwidth-delay product: https://en.wikipedia.org/wiki/Bandwidth-delay_product
- Kernel docs — TCP window scaling và auto-tuning (tcp_rmem/tcp_wmem): https://docs.kernel.org/networking/ip-sysctl.html
- Ilya Grigorik — High Performance Browser Networking (Latency vs Bandwidth): https://hpbn.co/primer-on-latency-and-bandwidth/
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.