All numbers below were measured on this machine (virtio_net guest, eth0 = 217.60.242.21/24, 8 vCPU) against the host's own /12-/16 address space. They are guest-network numbers: the virtio TX virtqueue (256 frames) is the bottleneck for every tool here, so absolute rates say less than the ranking.
- Same interface, same neighbour (gateway MAC via
-G/--router-mac). - Same probe count and same port.
- zmap/masscan were run with the cooldown disabled (
-c 0/--wait 1), because their default 8-second post-scan wait dominates small scans. - zscan used
--wait 0with the same intent, and--tx-workers 4. - Each figure is the send loop wall time;
zscanprints received frames and findings on stderr, which is how the probe counts were verified.
| Workload | zscan | zmap | masscan |
|---|---|---|---|
| 65 536 probes (/16, 1 port) | 0.61 s | 0.61 s | n/a |
| 1 024 000 probes (/12, 1 port), quiet host, 3 runs | 5.02 / 5.23 / 5.62 s | 5.32 / 5.73 / 5.83 s | 27.3 s |
| 1 024 000 probes, busy host, 3 runs | 4.82 / 5.73 / 6.27 s | 5.92 / 6.53 / 6.94 s | n/a |
| 256 hosts × 1000 probes (bench_tx, 4 workers) | ~170 000 pps (3.0 s for 512k) | n/a | ~33–36 kpps (9–10 s) |
| Rate-cap accuracy (cap 40 000 pps) | 40 030–40 060 pps (0.15 % over) | n/a | n/a |
The third row is the same workload measured while another job was running heavy tests on the host; it is kept because it shows how much the shared VM moves the numbers (zscan 4.82–6.27 s across runs). On a quiet host the ordering is stable: zscan 5.02–5.62 s, zmap 5.32–5.83 s, masscan 27.3 s.
Notes:
- At /16 scale zscan and zmap are within noise of each other on this VM.
- At 1M probes zscan is consistently ahead on a quiet host (best 5.02 s vs zmap's best 5.32 s; zmap wins no trial). The margin is small — a few percent — so treat the two as comparable in rate, with zscan adding the features zmap does not have (UDP modules, banners, per-worker sharding, live progress).
- masscan on the identical workload takes 27.3 s (~37 kpps), roughly 5x slower.
- masscan's default rate is ~100 pps; the 9–10 s figure above is with
--rate 10000000, which is the only honest comparison. - zscan's TX path is where the difference comes from: one AF_PACKET
TPACKET_V3ring per worker, frames rendered straight into ring slots (no intermediateVec), and a spin-kick pacer that treats eachsendtoas real work instead of sleeping between kicks. - Everything is bounded by the virtio TX queue here; on bare-metal NICs with larger TX rings the per-worker ring design should widen the gap, but that claim is not measured here and is marked unverified on purpose.
# zscan (4 workers, no post-scan wait)
target/release/zscan 217.48.0.0/12 -p 443 -i eth0 \
--router-mac <gw-mac> --adapter-ip <our-ip> \
--max-rate 5000000 --num-probes 1 --wait 0 --tx-workers 4 --output-file /dev/null
# zmap, same workload
zmap -i eth0 -p 443 -r 5000000 -c 0 -q -G <gw-mac> -S <our-ip> -o /dev/null 217.48.0.0/12
# masscan, same workload
masscan 217.48.0.0/12 -p443 --rate 10000000 --wait 1 -i eth0 \
--router-mac <gw-mac> --source-ip <our-ip> -oL /dev/null