2026-08-17
A 10 GbE NIC receiving 64-byte packets at line rate generates 14.88 million packets per second. If each packet raised an interrupt, your CPU would spend all its time in the interrupt handler and never touch userspace. Interrupt coalescing is the hardware mechanism that batches multiple events into a single interrupt, trading latency for throughput.
The NIC maintains two coalescing knobs per receive queue, implemented as hardware counters:
The interrupt fires when either counter trips. The time counter prevents a slow trickle of packets from sitting indefinitely; the packet counter prevents a burst from generating thousands of interrupts. Modern NICs (Intel ixgbe, Mellanox ConnectX) also implement adaptive coalescing: a small on-die state machine watches recent packet rate and dynamically adjusts both thresholds. Under low load it shortens the timer for latency; under high load it lengthens both for throughput.
Concrete example — Intel X710 defaults: rx-usecs=50, rx-frames=64. At 100k packets/sec, packets arrive every 10 µs, so the frame counter trips first (64 packets = 640 µs between interrupts) → ~1,560 interrupts/sec. Without coalescing you'd get 100,000/sec — a 64× reduction in interrupt overhead, at the cost of adding up to 640 µs of latency to the last packet in each batch.
Rule of thumb for tuning: target ~10,000-20,000 interrupts/sec per core under load. Below that, you're wasting latency; above that, interrupt handling starts stealing measurable cycles. Compute rx-usecs as 1e6 / target_ints_per_sec, then verify with ethtool -S.
The hardware pairs coalescing with MSI-X vector steering: each RX queue has its own MSI-X vector routed via the APIC's Interrupt Remapping Table to a specific core. This means coalescing counters are per-queue, per-core — a 16-queue NIC has 16 independent coalescing state machines running in parallel, each servicing one core's cache-hot RX ring.
Trading pieces: coalescing helps throughput-bound workloads (web servers, storage) but hurts latency-sensitive ones (HFT, RDMA). That's why kernel-bypass frameworks like DPDK disable coalescing entirely and use polling instead — the CPU never sleeps, so interrupts become pure overhead. The NIC still writes to the RX ring; software just checks the ring pointer in a tight loop.
