iperf3 Tutorial: Network Bandwidth Is Not LLM Throughput
iperf3 measures data transferred between two test endpoints. It does not measure LLM tokens per second. Start with one forward TCP stream, preserve the receiver result and its units, and record the path and test conditions. A fast socket test can help diagnose a network path; it cannot establish RDMA, GPU collective or model-serving performance.
If the number looks eight times too large, check Mb/s versus MB/s first. If the client cannot connect, confirm the agreed server address, listener and narrow network access with its owner; do not disable the firewall. Run a short, rate-limited test only between endpoints you control and are authorized to load.
Documentation checked October 25, 2026, Korea time. The network commands below were not executed: this Windows PC did not have iperf3 on PATH, and no remote test endpoints were approved. The Python parser was actually executed on a hand-written synthetic fixture using CPython 3.14.3. No network, GPU, NCCL or LLM benchmark was run.
Contents and the terms you need
- Scope: bandwidth is a capacity or rate with a defined scope; throughput is work actually completed per unit time.
- Endpoints: the client starts a test; the server accepts it. Their names do not fix the data direction.
- Direction: forward sends client to server; reverse sends server to client; bidirectional loads both simultaneously.
- Units: Mb/s means decimal megabits per second; MB/s means decimal megabytes per second. One byte is eight bits.
- Receiver result: bytes received divided by the receiver's elapsed interval, converted to the chosen unit.
- Next layer: RDMA is a different data-transfer path; NCCL implements GPU collectives; LLM throughput counts completed model work under a stated workload.
1. Choose the measurement that answers your question
A port's advertised line rate, a TCP application's payload rate and generated tokens per second describe different things. iperf3 sends generated traffic between hosts. Its TCP receiver result excludes the Ethernet framing overhead from the payload byte count and reflects the tested hosts, route, operating system and socket behavior.
iperf3 TCP payload
Mb/s or Gb/s, one stated direction
RDMA or NCCL tests on the actual setup
Operation time and explicitly defined bandwidth
Model, prompts, concurrency and latency targets
Tokens/s, TTFT, E2EL and SLO attainment
For a model-serving measurement, record model and engine versions, input/output lengths, concurrency, cache policy, errors and the measurement window. For definitions, use the existing LLM inference metrics guide. The question here is narrower: how do you obtain and read a controlled network result?
2. Run a short forward TCP test on approved Linux endpoints
The commands use the ESnet iperf3 manual, which identified itself as version 3.22 when checked. Use an organization-approved Linux installation on both machines, and record the output of iperf3 --version on each. Check local help for these options before a run. This tutorial does not install a package or change network settings.
192.0.2.20 is a documentation address, not a public test server. Replace it with the agreed IP address actually assigned to your server. Confirm the intended NIC, route, existing TCP 5201 access and an acceptable load window. A rate cap still consumes real network resources.
Server B — one test, then exit (not executed here):
iperf3 --version
iperf3 -s -1 -B 192.0.2.20 -p 5201 \
--idle-timeout 30 --server-max-duration 10
-B binds the listener to that local address. -1 accepts at most one client test and exits. The idle timeout avoids leaving the one-off listener waiting indefinitely. The duration limit rejects tests exceeding the server's allowed duration; it is not an access-control mechanism. Keep existing host and network access controls in place.
Client A — five seconds, one stream, 100 Mb/s target (not executed here):
iperf3 --version
iperf3 -c 192.0.2.20 -p 5201 -P 1 -t 5 -b 100M \
--connect-timeout 3000 -J --logfile forward.json
TCP is the default protocol. -P 1 selects one stream; -t 5 requests a five-second test; -b 100M requests a 100,000,000-bit/s target. The actual receiver result can be lower. --connect-timeout 3000 limits the initial control connection wait to 3,000 milliseconds. -J writes JSON, saved here with --logfile.
This is a restrained smoke test, not a maximum-capacity benchmark. A 100 Mb/s cap cannot establish whether a 100 Gb/s path reaches its capacity. Extend duration or raise load only within the endpoint owner's approved test plan. Save errors and failed attempts rather than presenting a partial run as a valid result.
3. Keep forward, reverse and simultaneous tests separate
| Mode | Data flow |
|---|---|
| Default | Client → server |
-R | Server → client |
--bidir | Both at once |
For a later approved reverse test, restart the one-off server, add -R to the client command and use a new output filename. A bidirectional run uses --bidir instead and creates simultaneous contention. Do not silently add its two rates and compare that sum with a one-direction line rate. The Python reader below intentionally rejects reverse and bidirectional inputs.
Parallel streams also change the experiment. ESnet notes that -b is a per-stream limit when -P exceeds one. Four streams capped at 100M each are not a 100M aggregate test. Version matters too: iperf3 added one thread per stream starting with 3.16. A higher multi-stream result does not prove that the network alone changed.
4. Convert bytes and seconds without mixing bits and bytes
Use the receiver's summary interval and byte count from the same completed run. Do not divide its bytes by the requested duration if the reported receiver interval differs. Decimal conversions are:
bits per second = received bytes × 8 ÷ receiver seconds
Mb/s = bits per second ÷ 1,000,000
MB/s = received bytes ÷ receiver seconds ÷ 1,000,000
In the synthetic exercise below, 50,000,000 bytes in 5.0 seconds gives 80 Mb/s = 10 MB/s. The conversion changes the unit, not the speed. For binary units, divide bytes/s by 1,048,576 to obtain MiB/s; label that separately.
5. Read a forward JSON result in Python
Save the following as read_forward.py. It reads the receiver's end.sum_received summary and accepts only forward, single-stream TCP JSON with the expected fields. The official iperf3 output source uses start.test_start.bidir for the bidirectional flag. Missing fields or another mode require inspection of that version's output, not silently substituting a different summary.
import json
import math
import sys
from pathlib import Path
def receiver_rate(data):
if "error" in data:
raise ValueError("iperf3 reported an error; do not report a rate")
test = data["start"]["test_start"]
if (test["protocol"] != "TCP" or test["num_streams"] != 1
or test["reverse"] != 0 or test["bidir"] != 0):
raise ValueError("Expected forward TCP, one stream, no bidirectional test")
result = data["end"]["sum_received"]
size = result["bytes"]
seconds = result["seconds"]
if type(size) is not int or size < 0:
raise ValueError("bytes must be a nonnegative integer")
if (type(seconds) not in (int, float) or not math.isfinite(seconds)
or seconds <= 0):
raise ValueError("seconds must be finite and positive")
return size * 8 / seconds, size / seconds
if __name__ == "__main__":
try:
data = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8-sig"))
bits, bytes_per_sec = receiver_rate(data)
except (IndexError, OSError, ValueError, KeyError, TypeError) as error:
raise SystemExit(f"No result: {error}")
print(f"Receiver: {bits / 1e6:.3f} Mb/s = {bytes_per_sec / 1e6:.3f} MB/s")
print("Forward TCP payload only; not wire rate, RDMA, GPU or LLM throughput.")
The reader uses Python's standard library only. It is a deliberately narrow example, not a validator for every iperf3 version or JSON mode. Keep the original JSON and version alongside the number. --json-stream produces a different output shape and is outside this example.
Runnable synthetic input: save this as synthetic-forward.json. It is a hand-written subset for teaching the parser, not captured iperf3 output.
{
"_notice": "HAND-WRITTEN SYNTHETIC SUBSET, not captured iperf3 output",
"start": {
"test_start": {"protocol": "TCP", "num_streams": 1, "reverse": 0, "bidir": 0}
},
"end": {"sum_received": {"bytes": 50000000, "seconds": 5.0}}
}
python read_forward.py synthetic-forward.json
Actually observed local output:
Receiver: 80.000 Mb/s = 10.000 MB/s
Forward TCP payload only; not wire rate, RDMA, GPU or LLM throughput.
Executed on Windows with CPython 3.14.3. Seven local test methods passed, covering the conversion, zero bytes, an explicit error, other modes, invalid time/byte fields and a missing summary. This checks parsing and arithmetic; it supplies no evidence of network speed. After an authorized real forward test completes, run python read_forward.py forward.json against its saved file.
6. Interpret a result before changing the system
- Connection failure: confirm the correct server IP, that its one-off listener is still waiting, the matching port and the intended route. Ask the owner to check narrowly scoped access. Do not disable security controls or repeatedly hit an unapproved endpoint.
- A low number: first inspect the target rate, direction, stream count, test version, CPU load, route, competing traffic and test duration. A cap or host limitation is not evidence of a defective link.
- A high number but slow AI work: use the actual GPU/RDMA path and application test next. iperf3's TCP socket traffic does not establish RDMA health, collective communication time, token latency or inference capacity.
- Comparing two runs: hold endpoints, direction, protocol, stream count, load cap, duration and background conditions constant. Preserve both sender and receiver summaries and failure records; state which rate you report.
NCCL tests distinguish algorithm bandwidth from a formula-derived bus bandwidth, with the formula depending on the collective. Those columns are not interchangeable with iperf3's socket payload rate. Likewise, LLM SLO Goodput needs actual request-level latency and success criteria. The related Python guide to p50, p95 and SLO Goodput from request logs shows that next step without pretending a network test generated tokens.
7. Keep a reproducible test record
Record date and time zone; both iperf3 versions and operating systems; approved endpoint names; NIC and route; TCP/UDP; direction; stream count; target bitrate; requested and observed durations; receiver bytes/rate; sender summary; errors; CPU load and competing traffic. Keep internal addresses and topology out of a public report unless cleared for disclosure.
The useful conclusion is precise: “This forward TCP test achieved X Mb/s of receiver payload under these conditions.” Claims about RDMA, GPU collectives or LLM serving need their own measurements.
Appendix · environment and sources
Executed: local Python JSON parsing and synthetic arithmetic, CPython 3.14.3 on Windows. Not executed: all iperf3 commands, Linux server setup, RDMA/NCCL/GPU/LLM tests. No packages, firewall rules, NIC settings or cloud resources were changed. ESnet's FAQ states that native Windows iperf3 is not officially supported; this guide therefore does not present an unverified Windows binary as an equivalent tested environment.
- ESnet: Invoking iperf3 — command options, rate limits, directions and JSON output; page labeled 3.22 when checked.
- ESnet: iperf3 FAQ — Windows support and the 3.16 threading change.
- ESnet: iperf3 output implementation — JSON field names; moving master source checked October 25, 2026, not a promise about every installed build.
- NVIDIA nccl-tests: Performance — algorithm and bus bandwidth definitions.
- Python 3.14: JSON — standard-library decoding and invalid JSON errors.
댓글
댓글 쓰기
안녕하세요 :)