Also known as: SIMD, vectorisation, vector processing
SIMD vectorization — Single Instruction, Multiple Data — is the technique of applying one CPU instruction to several data elements simultaneously, packed side by side in a wide register.1 Because digital signal processing hammers the same arithmetic across long streams of samples, SIMD is the single most effective way to speed up DSP inner loops on a general-purpose CPU, and it underpins how SDR software sustains multi-megasample-per-second rates in software rather than dedicated hardware.
How it works
A modern CPU register can be 128, 256, or 512 bits wide. A 256-bit AVX register holds eight
32-bit floats; one vaddps instruction adds all eight lanes at once. DSP kernels are ideal
for this because their arithmetic is data-parallel — a FIR filter multiplies each of N
coefficients by a sample and sums, a mixer multiplies every sample by a rotating phasor, a
power detector squares and sums — the same operation, independent across samples. Packing
samples into vector lanes turns these loops into a handful of wide instructions.
The major instruction-set families are:
- SSE — 128-bit x86 vectors (4 floats), near-universal on desktop/server CPUs.
- AVX / AVX2 / AVX-512 — 256- and 512-bit x86 vectors, with fused multiply-add (FMA) that does a multiply and an add in one instruction, exactly the FIR/complex-multiply pattern.
- NEON — 128-bit ARM vectors, the reason a Raspberry Pi or phone can run real-time SDR.
Getting the speedup is the hard part. Auto-vectorizing compilers help but often fail on strided, complex-valued, or reduction-heavy code. Hand-written intrinsics give control but are per-ISA: the same kernel needs an SSE, an AVX, and a NEON version, plus runtime dispatch to pick the widest the host supports. Data must also be laid out contiguously and aligned so whole vectors load in one go — interleaved complex I/Q, for instance, is often deinterleaved so real and imaginary parts vectorise cleanly.
In practice
Because writing and dispatching per-ISA kernels is tedious, most SDR stacks lean on a kernel library. VOLK (the Vector-Optimized Library of Kernels, from the GNU Radio project) is the canonical example: it ships many hand-tuned SIMD implementations of common DSP primitives — complex multiply, dot product, magnitude, conversions — and at load time probes the CPU and binds each call to the fastest available version. Application code calls one portable function and transparently gets AVX-512 on a new Xeon or NEON on an ARM board. SIMD is complementary to the other two acceleration axes: multithreaded DSP spreads blocks across cores, and GPU DSP offloads massively parallel work to a graphics processor; SIMD extracts parallelism within a single core’s inner loop.
Relevance to SDR
SIMD is what makes software radio viable on commodity CPUs. Down-conversion, filtering, resampling, and demodulation all reduce to vectorizable multiply-accumulate loops, and at several megasamples per second a scalar implementation would saturate a core while a vectorized one leaves headroom for more channels. GopherTrunk is written in Go, which does not expose SIMD intrinsics in portable code and leans on the compiler and scalar-clean kernels rather than a VOLK-style hand-tuned library; its performance strategy is careful data layout, avoiding allocation in hot loops, and concurrency across goroutines rather than manual vectorization. That is an honest contrast worth understanding: the wider C/C++ SDR ecosystem (GNU Radio, liquid-dsp, csdr) depends heavily on SIMD via VOLK, whereas a Go SDR application trades some peak per-core throughput for simplicity and portability, recovering it through parallelism and lean allocation instead. Either way, the concept — one instruction over many samples — is the foundation of real-time DSP on a CPU.
Sources
-
Single instruction, multiple data — Wikipedia, on data-parallel vector execution and its use in signal processing. ↩