Skip to content

Fast convolution

Convolve by multiplying FFTs, find the lengths where that is cheaper than flip and slide, and cut a long signal into blocks.

Before this3.3 · 13.4 · 14.1 · 2 more
Chapter 14 · Lesson 3 of 4

First, the picture

Two routes to one convolution, in a race. Watch the solid FFT curve pass the dashed direct one as the length grows.

When the FFT route wins

Two signals, each N samples long. Real multiplies for their convolution.

Two 4-sample signals: 16 multiplies by hand, 176 by FFT. Short signals: go direct.

N
4
cheaper
direct
direct
16
FFT route
176
0.00 / 12.00 s
Describe this picture

Real multiplies for the convolution of two signals, each NN samples long, on log axes: the dashed curve is the direct route, N2N^2, and the solid one the FFT route. The readouts are NN, which route is cheaper, and the multiplies by each route. The clip sweeps NN from 4 to 4096. At the start the caption says “Two 4-sample signals: 16 multiplies by hand, 176 by FFT. Short signals: go direct.” At N=256N=256 it says “From N = 173 on, the FFT route is always cheaper. (It already wins from 116 to 128, then loses again when the FFT size jumps to 512.)”, and the readouts show 65 536 multiplies directly and 29 696 by FFT. At the end it says “N = 4096: 16.8 million multiplies directly, 671 744 by FFT, 25 times fewer.” After the sweep, dragging along the plot or the arrow keys choose NN: the arrows step by 1, Page Up and Page Down double or halve it, and Home and End jump to the ends. At 128 the caption says “N = 128: the FFT route wins, 13 312 against 16 384.” At 129 it says “N = 129: 2N − 1 = 257 needs an FFT of 512, and direct wins again.”

Convolution through the FFT

On the page Circular vs linear convolution (13.4) you saw that multiplying two DFTs convolves the two sequences circularly, and that padding both with zeros turns the circle into a line. This page asks a practical question: when is that route faster than flip and slide, and how do you use it on a signal that never ends?

Here is the recipe. Let xx have NxN_x samples and hh have NhN_h. The linear convolution has Nx+Nh−1N_x+N_h-1 samples (see Discrete convolution, 5.2). Choose the FFT size NfftN_\text{fft}: the smallest power of two that is at least Nx+Nh−1N_x+N_h-1. Then do five things.

  1. Zero-pad xx and hh to NfftN_\text{fft} samples.
  2. Take the FFT of each.
  3. Multiply the two spectra, bin by bin.
  4. Take the inverse FFT of the product.
  5. Keep the first Nx+Nh−1N_x+N_h-1 samples.

I call this FFT convolution. In symbols, with both inputs already padded:

y=IFFT{FFT{x}⋅FFT{h}}.y=\text{IFFT}\{\text{FFT}\{x\}\cdot\text{FFT}\{h\}\}.

Nothing about it is approximate. It gives the same samples as flip and slide, up to rounding in the arithmetic.

When the FFT route wins

To compare the two routes I count real multiplies, because an FFT works on complex numbers and a complex multiply costs 4 real ones, as on the page Complex numbers for signals (3.3). I use no tricks for real inputs. The two-for-one trick of More FFT algorithms (14.2) would roughly halve the FFT part, and I leave it out so the count stays simple.

Take two signals of the same length NN. Flip and slide multiplies every sample of one by every sample of the other: N2N^2 real multiplies. The FFT route needs three FFTs (two forward, one inverse) of size NfftN_\text{fft}, and NfftN_\text{fft} complex products. From The FFT (14.1), one FFT costs Nfft2log⁡2Nfft\tfrac{N_\text{fft}}{2}\log_2N_\text{fft} complex multiplies. So

costFFT=4(3⋅Nfft2log⁡2Nfft+Nfft).\text{cost}_\text{FFT}=4\left(3\cdot\frac{N_\text{fft}}{2}\log_2N_\text{fft}+N_\text{fft}\right).

The FFT route is like a motorway: slower to get onto, faster for a long trip. For short signals the fixed cost of three FFTs is more than the N2N^2 you were trying to avoid. For long signals N2N^2 grows much faster than Nlog⁡2NN\log_2N.

The race at the top of this page runs the formula. At its start, two 4-sample signals, the FFT size is 8, and 4(3⋅4⋅3+8)=1764(3\cdot4\cdot3+8)=176 multiplies against 16 by hand. At N=256N=256 it is 65 536 multiplies directly and 29 696 by FFT. At N=4096N=4096 it is 16.8 million against 671 744, 25 times fewer.

The FFT curve is a staircase, not a smooth line. The size NfftN_\text{fft} has to be a power of two, so it jumps each time 2N−12N-1 passes one. The jump is why the crossover is not a single point. Here are the numbers around it.

NNdirectFFT routeNfftN_\text{fft}cheaper
6440965888128direct
11613 45613 312256FFT route
12816 38413 312256FFT route
12916 64129 696512direct
17229 58429 696512direct
17329 92929 696512FFT route
10241 048 576143 3602048FFT route, 7.3 times fewer

From N=116N=116 to 128128 the FFT route wins, because Nfft=256N_\text{fft}=256 is already enough. At N=129N=129 the length 2N−1=2572N-1=257 needs 512, the cost more than doubles (13 312 to 29 696), and direct wins again until N=173N=173. From 173 onward the FFT route is cheaper at every length up to 4096.

After the sweep you can choose NN yourself in that picture. Try 128, then 129, and watch the cheaper trace swap twice.

Overlap-add: a signal that never stops

A microphone never stops, so you cannot wait for the whole signal before you start. Instead, cut xx into blocks of NblkN_\text{blk} samples each. Convolution is linear, so by the superposition of The impulse response (5.1) the output is the sum of what each block produces alone.

Each block’s output is Nblk+Nh−1N_\text{blk}+N_h-1 samples long. That is Nh−1N_h-1 more than the block, so the end of each output, its tail, spills into the time of the next block. The fix is to place each block’s output at the block’s own start and to add wherever tails overlap. This is overlap-add. Think of tiling a floor with tiles that overlap at the edges: the overlapped strips get painted twice, and you add the two coats.

Watch the two hatched zones, where one block’s tail lands on the start of the next: only there do two outputs add.

Overlap-add, three blocks

x has 24 samples, h = 1, 1, 1. Blocks of 8, each convolved with an FFT of 16.

A long input, cut into blocks of 8 samples.

block
none yet
samples finished
0
0.00 / 16.00 s
Describe this picture

Overlap-add with three blocks: xx has 24 samples, h=1,1,1h = 1, 1, 1, and blocks of 8 are each convolved with an FFT of 16. The input panel shades block 1, block 2 and block 3. The output panel draws each block’s output as open circles, labelled “block 1 output” and so on; the two overlap zones are hatched and labelled “tails add”, and the final sum is drawn as filled dots labelled “sum = x * h”. The readouts are the block and the samples finished; there is no control. Each caption appears once the picture it describes is complete and stays until the next. The first says “A long input, cut into blocks of 8 samples.” Once block 1 is drawn: “Block 1 convolved with h gives 10 samples, n = 0 to 9. The last 2 spill into block 2’s time.” Then: “Block 2’s output starts at n = 8, on top of block 1’s tail: at n = 8 and 9 the two add, 2 + 0 = 2 and 1 + 2 = 3.” At the end: “Added up: 26 samples, exactly the direct convolution x * h. Each block needed only a 16-point FFT, however long x is.”

The input xx has three blocks of 8: 2, 1, 3, 0, −1, 2, 1, 1; then 0, 2, −2, 1, 3, 1, 0, −1; then 1, 1, 2, 0, −1, 0, 2, 1. The filter is h=1,1,1h=1,1,1. Each block of 8 convolved with hh gives 8+3−1=108+3-1=10 samples, so a 16-point FFT is enough.

The block outputs, each computed with a 16-point FFT, are:

  • block 1, at n=0n=0 to 9: 2,3,6,4,2,1,2,4,2,12,3,6,4,2,1,2,4,2,1;
  • block 2, at n=8n=8 to 17: 0,2,0,1,2,5,4,0,−1,−10,2,0,1,2,5,4,0,-1,-1;
  • block 3, at n=16n=16 to 25: 1,2,4,3,1,−1,1,3,3,11,2,4,3,1,-1,1,3,3,1.

Block 2 starts at n=8n=8, on top of block 1’s tail: at n=8n=8 and 9 the two add, 2+0=22+0=2 and 1+2=31+2=3. At n=16n=16 and 1717 the tails of block 2 meet the start of block 3: −1+1=0-1+1=0 and −1+2=1-1+2=1. Only those four samples are sums; every other sample belongs to one block alone.

Added up, the 26 samples of yy are 2, 3, 6, 4, 2, 1, 2, 4, 2, 3, 0, 1, 2, 5, 4, 0, 0, 1, 4, 3, 1, −1, 1, 3, 3, 1, and they equal the output of flip and slide sample for sample. Each block needed only a 16-point FFT, however long xx is.

Overlap-save: keep what did not wrap

There is a second way to cut a signal, and it works the other way round. Let the FFT wrap, then throw away what wrapped. This is overlap-save.

Take overlapping segments of NfftN_\text{fft} input samples. Each segment starts Nfft−Nh+1N_\text{fft}-N_h+1 samples after the last one, so it repeats the end of the one before. Convolve each segment with hh circularly, as on page 13.4. The circular convolution wraps the tail of the segment back onto its first Nh−1N_h-1 outputs, so those are wrong. The remaining Nfft−Nh+1N_\text{fft}-N_h+1 outputs have nothing wrapped onto them and are correct. Keep those and drop the rest.

input segment, N_fft = 8002130−12circular output12236421wrappedkept: y[0] to y[5]
Fig. Overlap-save, first segment: the circular convolution’s first 2 samples are wrapped; the other 6 equal y[0] to y[5] = 2, 3, 6, 4, 2, 1. The next segment starts 6 samples later.

The figure uses the same xx and hh as before. The segment holds two zeros of history, then x[0]x[0] to x[5]x[5]. Of the eight circular outputs, the first two (1,21,2) are wrapped. The other six equal y[0]y[0] to y[5]y[5]. Each segment yields 8−3+1=68-3+1=6 good samples, so the next one starts 6 samples later. The two methods differ in where the repeated work sits: overlap-add overlaps the outputs and adds, overlap-save overlaps the inputs and discards.

Bigger blocks: cheaper, but later

Overlap-add leaves one choice: the block length NblkN_\text{blk}. To see what it costs, take a real job, a reverb. The echo of a room is its impulse response, as on page 5.1. Record it once, and any dry sound can be placed in that room by convolution with it.

A hall with a 1-second echo, sampled at fs=16 000f_s=16\,000 Hz, has Nh=16 000N_h=16\,000 taps. Directly, that is 16 000 multiplies for every output sample. With overlap-add each block costs two FFTs (the FFT of hh is done once and kept) and NfftN_\text{fft} products, where NfftN_\text{fft} is the first power of two at least Nblk+Nh−1N_\text{blk}+N_h-1. Spread over the NblkN_\text{blk} outputs of the block, the cost per output sample is

4(Nfftlog⁡2Nfft+Nfft)Nblk.\frac{4\left(N_\text{fft}\log_2N_\text{fft}+N_\text{fft}\right)}{N_\text{blk}}.

There is a price. The first output of a block can only appear after the whole block has arrived, so every output is late by about Nblk/fsN_\text{blk}/f_s. This waiting time is the latency. It is like a bus that waits to fill: cheaper per passenger, later to leave.

Watch the cost fall as the blocks double, while the delay doubles with them, until past 16 384 the cost climbs again.

Bigger blocks: cheaper, but later

A 1-second room, 16 000 taps at 16 kHz, run by overlap-add.

Blocks of 64: only 4 ms of delay, but 15 360 multiplies per output, about as many as direct.

block N_blk
64
delay
4.0 ms
multiplies per output
15 360
times fewer
1.0×
0.00 / 11.50 s
Describe this picture

A 1-second room, 16 000 taps at 16 kHz, run by overlap-add. The axes are the block length NblkN_\text{blk} in samples and the real multiplies per output sample. A dashed line at 16 000 is labelled “direct”, and dots joined by a thin line are labelled “overlap-add”. The readouts are the block NblkN_\text{blk}, the delay, the multiplies per output and how many times fewer than direct. The clip doubles the block length from 64 to 65 536. At the start the caption says “Blocks of 64: only 4 ms of delay, but 15 360 multiplies per output, about as many as direct.” (readouts 64, 4.0 ms, 15 360, 1.0×). At 512: “Blocks of 512: the FFT size jumps to 32 768, so the cost goes up a little, to 4096.” At 16 384: “Blocks of 16 384: 128 multiplies per output, 125 times fewer than direct. But each block waits 1024 ms to fill.” (readouts 16 384, 1024.0 ms, 128.0, 125.0×). At the end: “Past 16 384 the FFT grows faster than the block, and the cost climbs again (144 at 65 536). The choice is delay against cost.” (readouts 65 536, 4096.0 ms, 144.0, 111.1×). After the sweep, dragging along the plot or the arrow keys choose the block length, one doubling per press, and Home and End jump to the ends. A “Hear the room” button plays the sound described below.

Blocks of 64 give only 4 ms of delay, but 15 360 multiplies per output, about as many as direct. At 512 the cost rises a little, from 3840 to 4096, because the FFT jumps to 32 768.

Blocks of 16 384 cost 128 multiplies per output, 125 times fewer than direct, but each block waits 1024 ms to fill. This is the cheapest block length of the sweep.

Past that point the FFT grows faster than the block, and the cost climbs again: 144 at 65 536. The choice is delay against cost. The whole table, from the same formula:

NblkN_\text{blk}NfftN_\text{fft}delaymultiplies per outputtimes fewer
6416 3844.0 ms15 3601.0
25616 38416.0 ms38404.2
51232 76832.0 ms40963.9
102432 76864.0 ms20487.8
409632 768256.0 ms51231.3
16 38432 7681024.0 ms128125.0
32 76865 5362048.0 ms136117.6
65 536131 0724096.0 ms144111.1

Press “Hear the room” in the picture. It plays a 0.3 s dry pluck, a 440 Hz tone with a 60 ms decay, and then the same pluck convolved with a synthetic 1 s room. The room is seeded white noise times e−6.91t/0.8e^{-6.91t/0.8}, which has decayed to −60-60 dB at 0.8 s. The output is the same for every block length. It sounds the same; only the wait changes.

Real-time reverbs split hh into pieces too. This is partitioned convolution: small blocks for the start of hh give low delay, and big blocks for its tail give low cost.

The maths behind it · circulant matrices

Convolution with hh is multiplication by a Toeplitz matrix. Padded to a circle it becomes circulant, and the DFT diagonalises every circulant matrix, as on The DFT as a matrix (13.5). Fast convolution is “change basis, scale each coordinate, change back”.

The maths behind it · cross-correlation

The same trick computes a correlation, which is convolution with one signal reversed. That is the basis of fast cross-correlation for time-delay estimation (25.x).

Worked example

Use the reverb above with Nblk=1024N_\text{blk}=1024. The first power of two at least 1024+16 000−1=17 0231024+16\,000-1=17\,023 is Nfft=32 768N_\text{fft}=32\,768. The cost per output is 4(32768⋅15+32768)/1024=20484(32768\cdot15+32768)/1024=2048 real multiplies, against 16 000 directly: 7.8 times fewer. The delay is 1024/16 000=0.0641024/16\,000=0.064 s, which is 64 ms.

Is that a good choice? If 64 ms of delay is acceptable, 7.8 times fewer multiplies is a good return. If you need under 10 ms, blocks of 64 give 4.0 ms but save nothing. That tension is why real systems partition hh.

Where you’ll meet this

Long FIR filters are applied by FFT in this way, as on the pages of Chapter 19. The short-time Fourier transform of Chapter 15 also cuts a signal into overlapping frames (15.5 and 15.6). Multirate filter banks use the same blocking (Chapter 22).

Reference card

QuantityFormulaNotes
FFT convolutiony=IFFT{FFT{x}⋅FFT{h}}y=\text{IFFT}\{\text{FFT}\{x\}\cdot\text{FFT}\{h\}\}both zero-padded to NfftN_\text{fft}
FFT sizeNfft≥Nx+Nh−1N_\text{fft}\ge N_x+N_h-1next power of two
Cost, equal lengths NNdirect N2N^2; FFT 4(3⋅Nfft2log⁡2Nfft+Nfft)4\left(3\cdot\tfrac{N_\text{fft}}{2}\log_2N_\text{fft}+N_\text{fft}\right) realFFT wins for 116 to 128, then for good from N=173N=173
Overlap-addblocks of NblkN_\text{blk}; outputs Nblk+Nh−1N_\text{blk}+N_h-1 long; add the Nh−1N_h-1 overlaptails add
Overlap-savesegments of NfftN_\text{fft}, step Nfft−Nh+1N_\text{fft}-N_h+1; drop the first Nh−1N_h-1 outputswrapped part dropped
Cost per output, overlap-add4(Nfftlog⁡2Nfft+Nfft)/Nblk4\left(N_\text{fft}\log_2N_\text{fft}+N_\text{fft}\right)/N_\text{blk}hh‘s FFT done once
DelayNblk/fsN_\text{blk}/f_sbigger blocks, cheaper, later

End of lesson 14.3

Where to go next.

Phasorium
LibraryEvery lesson, in order

Parts

About Phasorium
Look