Quant Memo
Advanced

Kernel Bypass Networking

Kernel bypass lets a trading application talk to the network card directly, skipping the operating system's networking stack, to shave microseconds off every packet.

Prerequisites: Latency vs Throughput, The CPU Cache Hierarchy

A market-data packet lands on your network card, and somewhere between four and forty microseconds later your strategy sees it. For most software that gap is invisible. For a strategy racing other firms to react to the same tick, it is the entire game — the firm whose packet reaches the exchange first gets filled at the old price, everyone else gets adverse selection. Kernel bypass exists because most of that four-to-forty-microsecond gap turns out to be bureaucracy, not physics.

The post office analogy

Think of the normal way a computer receives network data as a busy post office. A letter (a packet) arrives at the loading dock (the network card). It doesn't go straight to you. A clerk logs it, an alarm bell rings to say mail has arrived (an interrupt), a sorting worker copies it into a numbered pigeonhole (a kernel buffer), and only when you specifically walk up and ask "any mail for me?" (a system call, the request an application makes to the operating system) does a second clerk copy it again into your hands (your application's memory). Every one of those steps is safe, fair to other tenants of the building, and slow.

Kernel bypass is renting your own loading dock. The network card is configured to write incoming packets directly into a block of memory your application already has open, and your application checks that memory in a tight loop instead of waiting for a bell. No sorting clerk, no numbered pigeonhole, no second clerk. You gave up the shared post office's fairness and safety guarantees in exchange for speed.

What the kernel path actually costs

The normal path a packet takes through Linux looks like this: the NIC (network interface card) writes the packet into a kernel-owned ring buffer via DMA (direct memory access, the card writing straight to RAM without the CPU babysitting each byte). The NIC then raises a hardware interrupt, which pauses whatever the CPU was doing to run the kernel's network driver. The driver walks the packet up through several protocol layers (Ethernet, IP, TCP), and when your application calls recv(), that's a system call — a deliberate handoff from your application's low-privilege mode into the kernel's high-privilege mode and back, which requires a context switch (saving and restoring the CPU's full register state). Finally the kernel copies the bytes from its buffer into your application's buffer.

kernel path NIC interrupt + driver kernel buffer copy syscall recv() copy ~4 hops, ~10-50 microseconds, 2 copies

kernel-bypass path NIC shared memory ring (DMA, no interrupt) app polls 0 copies ~2 hops, ~0.3-3 microseconds, 0 copies

The kernel path is safe and shared but pays for a hardware interrupt, a context switch, and two memory copies on every packet. Kernel bypass hands the application card memory directly and replaces the interrupt with a loop that never sleeps.

Worked example 1: the latency budget, added up

Take rough, defensible numbers for a modern x86 server. A context switch costs about 1-2 microseconds. A hardware interrupt, including the driver's handling, costs roughly 1-5 microseconds. A memory copy of a small packet (under 200 bytes) costs a few hundred nanoseconds; the kernel path does this twice (NIC-to-kernel-buffer is DMA, cheap, but kernel-buffer-to-application-buffer is a real CPU copy), and the syscall entry/exit itself costs 100-300 nanoseconds for the privilege-mode switch.

Adding the kernel path: interrupt (call it 2 μs) + driver protocol processing (1 μs) + syscall overhead (0.2 μs) + copy (0.3 μs) ≈ 3.5 microseconds, and that's an optimistic case — under load, with the interrupt handler competing with other work, 10-50 microseconds is common.

The bypass path: the NIC's DMA engine writes the packet into a ring buffer your application already has mapped. Your application is sitting in a while(true) loop reading a memory location. No interrupt fires, no syscall happens, no second copy happens. The only real cost left is checking whether new data arrived — tens of nanoseconds — plus whatever it takes to parse the packet, which is identical either way. That's roughly 0.3-1 microsecond, an order of magnitude smaller, and it stays flat under load because there's no interrupt handler to get backed up.

Worked example 2: throughput at saturation

Now trace what happens as packet rate rises, not just single-packet latency. Each interrupt has a fixed handling cost — say 2 microseconds of CPU time per interrupt, regardless of packet size. A single core spending all its time servicing interrupts can handle at most 1 / (2 × 10⁻⁶ s) = 500,000 interrupts per second before it has no cycles left for anything else, and real workloads hit a wall well below that once you add driver and protocol-stack work — 1-2 million packets per second (pps) is a realistic ceiling for one core doing kernel networking.

A polling loop has no per-packet fixed interrupt tax. The core spins continuously, checking the ring buffer; when several packets have queued up, it drains them in a batch, amortizing the loop-check cost across many packets at once. Production DPDK (Data Plane Development Kit, Intel's kernel-bypass framework) applications routinely sustain 10-30 million pps on a single core on the same hardware. The mechanism generating that 10-20x gap is exactly the one from example 1: you deleted a fixed per-packet cost and replaced it with a cost that is nearly free when amortized over a batch.

// Sketch of a kernel-bypass receive loop (DPDK-style pseudocode)
while (running) {
    uint16_t n = rte_eth_rx_burst(port_id, queue_id, pkts, BURST_SIZE);
    for (uint16_t i = 0; i < n; i++) {
        process_market_data(pkts[i]);   // your strategy's hot path
        rte_pktmbuf_free(pkts[i]);
    }
    // no sleep, no blocking recv() -- the core never yields
}

The classic confusion: kernel bypass is not "the same networking but faster for free." The core running that polling loop is pegged at 100% CPU permanently, whether or not packets are arriving — you have traded a CPU core (and its power draw) for latency and jitter reduction. You also lose everything the kernel used to do for you: TCP retransmission, firewalling, netstat visibility, safe multiplexing between processes. Bypass stacks like DPDK or Solarflare's OpenOnload reimplement a minimal networking stack in user space, and a bug there can crash your process or wedge the NIC in ways a misbehaving normal socket application never could.

Where this shows up

In a quant-dev interview, kernel bypass usually surfaces as a "why is our tick-to-trade latency 20 microseconds and the market leader's is 2" systems question — the expected answer chain is interrupt coalescing, context switches, and copies, in that order. In production, it is standard on the market-data and order-entry legs of any HFT or market-making system sitting in an exchange colocation facility, almost always paired with kernel-bypass techniques like CPU pinning and huge pages to keep the polling core free of scheduler interference, and it is deliberately not used on slower paths — risk checks, logging, position reconciliation — where the operational safety of the normal kernel stack is worth more than the microseconds.

Kernel bypass removes interrupts, context switches, and a memory copy from the packet-receive path by mapping NIC memory directly into the application and replacing "wait for an interrupt" with "poll a memory location in a tight loop." It trades a dedicated, always-busy CPU core and the kernel's safety net for a roughly 10x cut in latency and a 10-20x rise in achievable packets per second.

Related concepts

Practice in interviews

Further reading

  • Intel, Data Plane Development Kit (DPDK) Programmer's Guide
  • Solarflare/Xilinx, OpenOnload Architecture Overview
ShareTwitterLinkedIn