Also known as: zero-copy, zero-copy I/O, no-copy
Zero-copy is the practice of moving sample data through a system by giving each stage access to the same memory rather than copying bytes from one buffer to the next.1 In high-rate SDR software, where a stream may run at tens of megasamples per second, needless copies waste memory bandwidth, evict caches, and add latency; zero-copy design keeps the data still and passes references to it, which is often the difference between keeping up with the radio and dropping samples.
How it works
The costly thing in a naive pipeline is memcpy: every time samples pass from the driver to
the application, or from one processing stage to the next, the bytes are read from one region
and written to another. At SDR rates that traffic can dominate the memory bus and thrash the
CPU cache. Zero-copy eliminates the copies with a few complementary mechanisms.
- DMA (direct memory access). The device’s controller writes incoming samples straight into host RAM without the CPU touching each byte; the application is handed the filled buffer. This is how USB and PCIe SDR front-ends deliver samples efficiently.
- Memory mapping (
mmap). A file or a device/kernel buffer is mapped into the process’s address space, so reading a capture file or a kernel DMA ring means dereferencing memory, not issuingread()calls that copy through kernel buffers. Great for IQ recording/playback of large files. - In-place processing. A stage overwrites its input buffer with its output (a mixer multiplying samples by a phasor, say) instead of allocating a fresh output buffer.
- Shared queues. A ring buffer between a producer and consumer passes ownership of a slot rather than copying its contents; the consumer reads the same memory the producer wrote.
The cost is discipline. Sharing a buffer means agreeing on lifetime and ownership: a stage must not overwrite data another stage is still reading, alignment and slot boundaries must be respected, and in a multi-reader design you need reference counting or careful hand-off so a sample buffer is not recycled too early. Zero-copy trades away the safety of private copies for bandwidth and latency, so it belongs in the hot path, not everywhere.
In practice
The rule of thumb is to copy at the boundaries where you genuinely need isolation and share everywhere else on the hot path. Format conversion and deinterleaving are often the one unavoidable copy — turning packed device bytes into the float layout the DSP wants — after which the sample block flows through filtering, down-conversion, and demodulation by reference. Overrun handling interacts closely: if a consumer stalls, a shared ring buffer must either apply back-pressure or drop a slot, since there is no private copy to fall back on, which is why zero-copy pipelines pair naturally with explicit overrun/underrun policies.
Relevance to SDR
Zero-copy is a core reason SDR frameworks can run megasample streams on ordinary CPUs.
Vendor libraries expose DMA’d buffers, GNU Radio passes blocks between processing nodes
through shared circular buffers, and file replay uses mmap to stream captures without
per-block reads. GopherTrunk embodies the spirit of this in a garbage-collected language:
it streams sample blocks through its decode chain via shared buffers and ring buffers rather
than reallocating per stage, and it works hard to avoid allocation in hot loops so the Go
garbage collector stays quiet — a Go program cannot achieve C-style pointer-level zero-copy
everywhere, but reusing and sharing backing slices is the same idea applied within the
language’s model. Honestly framed, true kernel-bypass zero-copy (raw DMA rings, mmap‘d
device buffers) lives mostly in the C driver layer beneath any host application; what an SDR
program controls is not re-copying samples needlessly once they arrive, and that alone often
decides whether the chain keeps pace.