Loading content...
If bandwidth is the width of the highway, throughput is how many cars actually reach their destination per hour. Your internet connection might boast 100 Mbps of bandwidth, but the file download showing 8 MB/s tells the real story of throughput.
Throughput is the actual rate of successful data delivery through a network, measured over a given time period. It's the practical measure of network performance that users experience and applications depend upon—not what the network could do in theory, but what it does do in reality.
By the end of this page, you will understand throughput as the practical counterpart to bandwidth. You'll learn why throughput always falls below bandwidth, how to identify bottlenecks, measure real performance, and apply optimization strategies that maximize useful data transfer.
Throughput measures what networks actually deliver—not their theoretical potential, but their measured output. Several related terms are used in the industry, often interchangeably but with subtle distinctions:
Throughput: The rate at which data successfully traverses the network from source to destination, including all overhead.
Goodput: The rate of useful data delivery, excluding protocol overhead like headers and retransmissions. Goodput ≤ Throughput.
Effective Bandwidth: Another term for achieved throughput, emphasizing it's the 'effective' portion of raw bandwidth.
Transfer Rate: User-focused term typically meaning goodput as seen by the application.
| Term | Measures | Includes Overhead? | Typical Context |
|---|---|---|---|
| Bandwidth | Maximum capacity | N/A (theoretical) | Link specifications |
| Throughput | Actual data rate | Yes (all bits on wire) | Network engineering |
| Goodput | Useful data rate | No (application data only) | Application performance |
| Transfer Rate | User-visible speed | No (file bytes) | End-user experience |
In practice: Bandwidth ≥ Throughput ≥ Goodput. Understanding this hierarchy helps you diagnose where performance is lost. If throughput is close to bandwidth but goodput is low, the issue is overhead. If throughput itself is low, look at network conditions.
Time Matters:
Throughput is inherently time-dependent. The same network can show vastly different throughput depending on when you measure:
For meaningful analysis, always specify the measurement interval and understand that throughput fluctuates naturally.
The gap between bandwidth and throughput isn't a bug—it's fundamental to how networks work. Multiple mechanisms consume capacity without contributing to user data transfer:
1. Protocol Overhead: Every layer of the network stack adds headers, trailers, and control information:
| Layer | Headers/Overhead | Bytes | % of Payload |
|---|---|---|---|
| Ethernet | Preamble, MAC addresses, FCS | 38 | 2.5% |
| IP | IP header (v4) | 20 | 1.3% |
| TCP | TCP header (no options) | 20 | 1.3% |
| TCP Options | Timestamps, SACK, etc. | 0-40 | 0-2.7% |
| TLS | Record header + MAC + padding | 25-50 | 1.7-3.3% |
| Total | 103-168 | 6.8-11.2% |
With typical overhead, even a perfectly functioning network delivers only ~90-93% of bandwidth as goodput. Small packets suffer even more—a 64-byte packet carrying 6 bytes of useful data has 90%+ overhead.
2. Control Traffic: Networks require non-data traffic to function:
3. Retransmissions: Packet loss requires re-sending data, wasting bandwidth:
Packet loss has nonlinear effects on TCP throughput. At 0.1% loss, throughput might be 80-90% of capacity. At 1% loss, it can drop to 30-50%. At 5% loss, throughput may be 10% or less of bandwidth. On high-latency links, these effects are even more severe.
4. Congestion and Queuing: When traffic exceeds capacity, queues form:
5. Flow Control Constraints:
6. Physical/Link Layer Issues:
Throughput is influenced by factors spanning every layer of the network and beyond. Understanding these factors is essential for diagnosis and optimization.
Network Factors:
End-System Factors:
Protocol Factors:
Throughput issues are either sender-limited (end-system can't generate packets fast enough) or network-limited (network can't deliver packets fast enough). Diagnosing which is critical: fixing the wrong end wastes effort.
Unlike bandwidth (per-link property), throughput is measured end-to-end. A chain is only as strong as its weakest link, and throughput reflects the entire path including endpoints.
TCP dominates Internet traffic, and understanding its throughput characteristics is essential for network performance analysis.
TCP Throughput Equation:
The simplified Mathis equation models TCP throughput:
Throughput ≈ (MSS × C) / (RTT × √p)
Where:
This equation reveals crucial insights:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
import math def mathis_throughput(mss_bytes: int, rtt_ms: float, loss_rate: float, c: float = 1.22) -> float: """ Calculate theoretical TCP throughput using Mathis equation. Args: mss_bytes: Maximum Segment Size in bytes rtt_ms: Round-Trip Time in milliseconds loss_rate: Packet loss probability (0.0 to 1.0) c: Constant (~1.22 for standard TCP) Returns: Throughput in bits per second """ if loss_rate <= 0: # No loss - limited by bandwidth or window, not this model return float('inf') rtt_seconds = rtt_ms / 1000 mss_bits = mss_bytes * 8 throughput_bps = (mss_bits * c) / (rtt_seconds * math.sqrt(loss_rate)) return throughput_bps def format_throughput(bps: float) -> str: """Format throughput in human-readable form.""" if bps == float('inf'): return "Unlimited (bandwidth-limited)" for unit, divisor in [("Gbps", 1e9), ("Mbps", 1e6), ("Kbps", 1e3)]: if bps >= divisor: return f"{bps/divisor:.2f} {unit}" return f"{bps:.2f} bps" # Demonstrate impact of RTT and lossprint("=== TCP Throughput Analysis (Mathis Model) ===\n")print("MSS = 1460 bytes\n") # Varying RTT with 0.1% lossprint("Effect of RTT (0.1% loss):")for rtt in [10, 50, 100, 200, 500]: tp = mathis_throughput(1460, rtt, 0.001) print(f" RTT {rtt:3d}ms: {format_throughput(tp)}") print() # Varying loss with 50ms RTTprint("Effect of Loss Rate (50ms RTT):")for loss in [0.0001, 0.001, 0.01, 0.05, 0.1]: tp = mathis_throughput(1460, 50, loss) print(f" Loss {loss*100:.2f}%: {format_throughput(tp)}") print() # Real-world scenariosscenarios = [ ("LAN (low latency, no loss)", 1, 0.0001), ("Metro (moderate latency)", 20, 0.001), ("Transoceanic (high latency)", 150, 0.002), ("Congested WiFi", 30, 0.03), ("Cellular (poor signal)", 100, 0.05),] print("Real-World Scenarios:")for name, rtt, loss in scenarios: tp = mathis_throughput(1460, rtt, loss) print(f" {name}") print(f" RTT={rtt}ms, Loss={loss*100:.2f}%") print(f" Max Throughput: {format_throughput(tp)}\n")High-latency, lossy links suffer doubly: retransmissions waste more time (RTT for recovery), and the throughput equation compounds both factors. A 200ms RTT with 1% loss yields theoretical max of ~4 Mbps—regardless of link bandwidth.
Congestion Control Evolution:
TCP congestion control has evolved significantly, each generation improving throughput in specific scenarios:
Choosing the right congestion control can dramatically affect throughput, especially on challenging paths.
Accurate throughput measurement requires careful methodology. Poorly designed tests often blame the network for application or endpoint issues.
Measurement Principles:
| Tool/Method | Measures | Strengths | Limitations |
|---|---|---|---|
| iperf3 (TCP) | End-to-end TCP throughput | Configurable, reliable | Requires server on far end |
| iperf3 (UDP) | Raw network capacity | No TCP limitations | Doesn't reflect real TCP performance |
| Speedtest.net | Throughput to test servers | Easy, no setup | Server selection, marketing-optimized |
| wget/curl timing | Application-layer transfer | Real-world test | Server/storage bottlenecks |
| tcptraceroute + perf | Per-hop analysis | Identifies bottlenecks | Complex, requires expertise |
| SNMP/NetFlow | Interface utilization | Non-intrusive, continuous | Device-side view only |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
#!/usr/bin/env python3"""Throughput measurement and analysis utilities.Demonstrates proper measurement methodology.""" import socketimport timeimport statisticsfrom typing import List, Tuple def measure_tcp_throughput(host: str, port: int, duration_seconds: float = 10, buffer_size: int = 65536) -> dict: """ Measure TCP throughput by receiving data from a server. This implements a simple throughput test receiver. The server should be sending continuous data. """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, buffer_size * 4) try: sock.connect((host, port)) sock.setblocking(True) total_bytes = 0 samples = [] start_time = time.perf_counter() last_sample_time = start_time sample_bytes = 0 while (time.perf_counter() - start_time) < duration_seconds: data = sock.recv(buffer_size) if not data: break bytes_received = len(data) total_bytes += bytes_received sample_bytes += bytes_received # Take samples every second now = time.perf_counter() if now - last_sample_time >= 1.0: sample_throughput = (sample_bytes * 8) / (now - last_sample_time) samples.append(sample_throughput) sample_bytes = 0 last_sample_time = now elapsed = time.perf_counter() - start_time finally: sock.close() avg_throughput = (total_bytes * 8) / elapsed if elapsed > 0 else 0 return { "total_bytes": total_bytes, "duration_seconds": elapsed, "average_throughput_bps": avg_throughput, "average_throughput_mbps": avg_throughput / 1e6, "samples_bps": samples, "min_throughput_mbps": min(samples) / 1e6 if samples else 0, "max_throughput_mbps": max(samples) / 1e6 if samples else 0, "stddev_mbps": statistics.stdev(samples) / 1e6 if len(samples) > 1 else 0, } def analyze_throughput_samples(samples_mbps: List[float]) -> dict: """ Analyze throughput samples for reporting. Reports percentiles which are more meaningful than averages for variable network performance. """ if not samples_mbps: return {} sorted_samples = sorted(samples_mbps) n = len(sorted_samples) def percentile(p: float) -> float: idx = int(p * n / 100) return sorted_samples[min(idx, n-1)] return { "count": n, "mean": statistics.mean(samples_mbps), "median": statistics.median(samples_mbps), "p5": percentile(5), # Low end "p25": percentile(25), # Q1 "p75": percentile(75), # Q3 "p95": percentile(95), # High end "min": min(samples_mbps), "max": max(samples_mbps), "stddev": statistics.stdev(samples_mbps) if n > 1 else 0, "coefficient_of_variation": (statistics.stdev(samples_mbps) / statistics.mean(samples_mbps) * 100 if n > 1 and statistics.mean(samples_mbps) > 0 else 0), } # Example analysis outputexample_samples = [95.2, 97.1, 94.8, 92.3, 96.4, 98.1, 93.5, 95.8, 97.3, 94.1, 45.2, 89.3, 96.7, 98.2, 95.5] # Note the outlier at 45.2 analysis = analyze_throughput_samples(example_samples)print("=== Throughput Analysis Example ===")print(f"Samples: {len(analysis['count'])} measurements")print(f"Mean: {analysis['mean']:.1f} Mbps")print(f"Median: {analysis['median']:.1f} Mbps")print(f"P5-P95: {analysis['p5']:.1f} - {analysis['p95']:.1f} Mbps")print(f"Std Dev: {analysis['stddev']:.1f} Mbps")print(f"CV: {analysis['coefficient_of_variation']:.1f}%")print("\nNote: Mean (affected by outlier) differs from Median (robust).")Throughput varies over time. An average of 100 Mbps could mean consistent 100 Mbps or alternating 50/150 Mbps—very different experiences. Report P5/P50/P95 to capture the distribution. Users experience percentiles; SLAs should reflect them.
Improving throughput requires identifying and addressing bottlenecks systematically. Different bottlenecks require different solutions.
TCP/Socket Tuning:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
#!/bin/bash# Linux TCP/network tuning for higher throughput # === Socket Buffer Sizes ===# Increase default and maximum socket buffer sizes# Allows larger TCP windows for high-BDP paths # Receive buffers (min, default, max in bytes)sudo sysctl -w net.core.rmem_max=134217728sudo sysctl -w net.core.rmem_default=16777216sudo sysctl -w net.ipv4.tcp_rmem="4096 87380 134217728" # Send buffers (min, default, max in bytes)sudo sysctl -w net.core.wmem_max=134217728sudo sysctl -w net.core.wmem_default=16777216sudo sysctl -w net.ipv4.tcp_wmem="4096 65536 134217728" # === TCP Window Scaling ===# Required for windows > 65KBsudo sysctl -w net.ipv4.tcp_window_scaling=1 # === Congestion Control ===# BBR often provides better throughput than CUBIC on lossy pathssudo sysctl -w net.ipv4.tcp_congestion_control=bbr # === Other Throughput-Related Settings ===# Enable TCP timestamps (needed for high-speed and long RTT)sudo sysctl -w net.ipv4.tcp_timestamps=1 # Increase max connections in accept queuesudo sysctl -w net.core.somaxconn=4096 # Increase network device backlogsudo sysctl -w net.core.netdev_max_backlog=5000 # Enable TCP selective acknowledgmentssudo sysctl -w net.ipv4.tcp_sack=1 # Allow reuse of TIME_WAIT sockets for new connectionssudo sysctl -w net.ipv4.tcp_tw_reuse=1 # === NIC Offloading ===# Enable hardware offloading features for your interfaceINTERFACE="eth0"ethtool -K $INTERFACE gso on gro on tso onethtool -G $INTERFACE rx 4096 tx 4096 echo "Network tuning applied. Verify with 'sysctl -a | grep tcp'"Application-Level Optimizations:
Network Infrastructure Optimizations:
Optimize in order of impact: 1) Fix packet loss first, 2) Reduce latency, 3) Increase bandwidth. Throwing bandwidth at a lossy or high-latency path often doesn't help—the Mathis equation shows why RTT and loss dominate.
Production networks require continuous monitoring to detect throughput problems before users complain. A comprehensive monitoring strategy includes:
Interface-Level Monitoring: Monitor network interface utilization on routers, switches, and servers:
Flow-Level Analysis: Understand which flows consume bandwidth:
Synthetic Monitoring: Continuously test achievable throughput:
| Metric | Description | Alert Threshold | Investigation Trigger |
|---|---|---|---|
| Interface Utilization | % of bandwidth in use | 80% for 5 minutes | 60% average |
| Throughput Baseline | Normal vs. current throughput | <50% of baseline | <80% of baseline |
| Top Flow Ratio | % of traffic from top flow | 50% | 30% |
| Synthetic Test | Achieved vs. expected throughput | <70% expected | <85% expected |
| Application Response | Application-specific throughput | Varies by app | Degradation trend |
Before setting alerts, establish what 'normal' looks like. A week of baseline data reveals patterns (peak hours, day-of-week variation). Alerting without baselines leads to false positives or missed problems.
Throughput characteristics and optimization strategies vary across network environments:
Data Center Networks:
Enterprise WAN:
Internet/Consumer:
| Environment | Typical Throughput | Main Bottleneck | Key Optimization |
|---|---|---|---|
| Data Center | 10-100 Gbps | Server CPU/storage | RDMA, kernel bypass |
| Enterprise LAN | 1-10 Gbps | Access layer | Upgrade, proper design |
| Enterprise WAN | 100 Mbps-1 Gbps | Link bandwidth | SD-WAN, compression |
| Cloud IaaS | 10-100 Gbps (burst) | Instance type limits | Right-size instances |
| Home Internet | 50-500 Mbps | Last-mile connection | Upgrade ISP tier |
| Mobile/Cellular | 10-100 Mbps | Radio conditions | Edge caching, adaptive bitrate |
Cloud environments add complexity: throughput may be throttled by instance type, availability zone placement matters, and 'network optimized' instances cost significantly more. Always verify actual throughput matches expectations with cloud workloads.
Throughput is the practical measure of network performance—what actually gets delivered versus what the network could theoretically provide. Understanding throughput enables you to diagnose performance problems, optimize systems, and set realistic expectations.
What's Next:
With bandwidth (capacity) and throughput (reality) understood, we'll explore latency—the time dimension of network performance. Latency affects everything from user experience to TCP throughput, and it's often the difference between 'fast enough' and 'feels slow.'
You now understand throughput as the practical counterpart to bandwidth. You can identify why throughput falls short, measure it accurately, and apply optimizations appropriate to your environment. This knowledge is essential for any performance-focused network engineering.