CS267 · 第 20 课

快速傅里叶变换

快速傅里叶变换(FFT)是科学计算中最重要的算法之一。它将 DFT 的复杂度从 O(m²) 降至 O(m log m),广泛应用于信号处理、偏微分方程求解、大整数乘法等领域。本课深入探讨 FFT 的数学原理、串行与并行实现、通信复杂度理论以及现代自动调优框架。

授课:Jim Demmel 日期:2025-04-03 预计用时:75 分钟 难度:困难 视频:Lecture 20 Video

1. 离散傅里叶变换(DFT)的定义

离散傅里叶变换是连续傅里叶变换的离散版本,是分析离散信号频域特性的核心工具。一维 DFT 将一个 m 元素向量 v 变换为另一个 m 元素向量 F·v,其中变换矩阵 F 定义为:

F[j,k] = ω^(jk),    0 ≤ j, k ≤ m-1

这里 ω = e^(2πi/m) = cos(2π/m) + i·sin(2π/m) 是 m 次单位根(m-th root of unity),满足 ω^m = 1。矩阵 F 是 Vandermonde 矩阵的特殊形式,具有对称性和正交性。

二维与多维 DFT

二维 DFT 作用于 m×m 矩阵 V,定义为 F·V·F——先对每列做一维 DFT,再对每行做一维 DFT。高维 DFT 以此类推,每个维度独立变换。这种可分离性使得高维 FFT 可以通过多次一维 FFT 实现。

DFT 的矩阵形式示例(m=4)

当 m=4 时,ω = i,ω² = -1,ω³ = -i,ω⁴ = 1。此时 F[j,k] = i^(jk),构成 4×4 的对称变换矩阵。DFT 可以理解为将信号从时域(或空域)转换到频域。

DFT 与逆 DFT 的关系

逆 DFT(IDFT)与 DFT 几乎相同,仅差一个共轭和归一化:

F^(-1) = complex_conjugate(F) / m

这意味着实现一个 FFT 核就能同时支持正逆变换。此外,正弦变换(sin transform)是 F 的虚部,余弦变换(cos transform)是 F 的实部,它们用于求解泊松方程等应用。


2. FFT 的应用领域

快速傅里叶变换之所以成为"最重要的算法",源于它在众多科学和工程领域的广泛应用:

信号处理与滤波

在信号处理中,FFT 将时域信号转换到频域。通过将频域中幅度较小的分量置零(低通/高通滤波),再逆变换回时域,即可实现噪声去除、信号增强。例如,含噪信号 sin(7t) + 0.5·sin(5t) 在 128 个采样点上,通过将 FFT 系数小于 0.25 的分量置零,可以有效滤除高频噪声。

图像压缩

二维 FFT 将图像从像素空间转换到频域。JPEG 压缩的核心思想类似:保留 FFT 系数中幅度最大的 2.5%(低频分量),舍弃高频分量,从而实现 40:1 的压缩比而视觉质量损失很小。对于 200×320 的图像,仅保留最大的 2.5% 分量即可重建出视觉可接受的图像。

大整数乘法

Schönhage-Strassen 算法利用 FFT 实现大整数乘法,复杂度为 O(b·log b·log log b),其中 b 是位数。这是目前已知最快的渐近大数乘法算法之一,在密码学(如 RSA)中有重要应用。


3. 用 FFT 求解泊松方程

泊松方程 ∇²u = f 是描述静电势、热传导、流体运动等现象的基本偏微分方程。FFT 提供了近乎最优的求解方法。

一维泊松方程的特征分解

一维泊松方程离散化后得到三对角矩阵 L₁,其特征分解为:

L₁ = F · D · F^T

其中 F 是正弦变换(FFT 的虚部),F(j,k) = √(2/(n+1))·sin(jkπ/(n+1)),D 是对角特征值矩阵,D(j,j) = 2(1 - cos(jπ/(n+1)))。

二维泊松方程的 FFT 求解算法

二维泊松方程 L₂X = B 可以写成 L₁·X + X·L₁ = B 的形式。代入特征分解后,算法分为三步:

  1. 正向 2D FFT:计算 B' = F^T · B · F
  2. 逐点求解:X'(j,k) = B'(j,k) / (D(j,j) + D(k,k))
  3. 逆向 2D FFT:计算 X = F · X' · F^T

总复杂度为 2 次二维 FFT 加上 n² 次加法和除法,即 O(n² log n),其中 n×n 是网格大小。

泊松方程求解器的复杂度对比

FFT 方法的 O(N log N) 复杂度(N = n² 未知量)与 Multigrid 相当,远优于稠密 LU(O(N³))和带状 LU(O(N²))。在 PRAM 模型下,FFT 仅需 O(log N) 时间。

算法 串行复杂度 PRAM 复杂度 内存 处理器数
Dense LUN³NN²N²
Band LUN²NN^(3/2)N
JacobiN²NNN
Conjugate GradientN^(3/2)N^(1/2)·log NNN
FFTN·log Nlog NNN
MultigridN(log N)²NN
Lower boundNlog N——

4. 串行 FFT 算法:分治策略

直接计算 DFT 需要 O(m²) 次运算。FFT 的核心思想是将求值问题转化为多项式的分治求值。

多项式视角

DFT 的第 j 个输出可以写成:

(F·v)[j] = Σ(k=0..m-1) ω^(jk)·v(k) = V(ω^j)

其中 V(x) = Σ(k=0..m-1) x^k · v(k) 是一个 m-1 次多项式。DFT 等价于在 m 个单位根处求多项式的值。

分治分解

将多项式 V(x) 按奇偶次项拆分:

V(x) = V_even(x²) + x · V_odd(x²)

其中 V_even 包含偶次项,V_odd 包含奇次项。两者都是 m/2 次多项式。关键观察是:我们只需要在 m 个点 (ω^j)² 处求值,但由于 (ω^(j+m/2))² = (ω^j)²,这 m 个点只有 m/2 个不同的值!

递归算法

FFT(v, ω, m):       // m 是 2 的幂
  if m = 1 return v[0]
  else
    v_even = FFT(v[0:2:m-2], ω², m/2)
    v_odd  = FFT(v[1:2:m-1], ω², m/2)
    ω_vec  = [ω^0, ω^1, ..., ω^(m/2-1)]    // 预计算
    return [v_even + (ω_vec .* v_odd),
            v_even - (ω_vec .* v_odd)]

复杂度分析

递推关系 T(m) = 2T(m/2) + O(m) 的解为 T(m) = O(m log m)。相比直接 DFT 的 O(m²),FFT 将复杂度降低了一个数量级以上。当 m = 10⁶ 时,FFT 约需 2000 万次运算,而直接 DFT 需 10¹² 次。


5. 迭代实现与 Bit Reversal

递归 FFT 的调用树是一棵 log m 层的完全二叉树。迭代实现自底向上遍历这棵树,避免了函数调用开销。

自底向上遍历

迭代算法从最小的子问题(大小为 2 的 DFT)开始,逐层合并。每一层将两个较小的 DFT 合并为一个更大的 DFT。这种实现方式天然适合循环结构,是现代 FFT 库的主流选择。

Bit-Reversed 输出顺序

迭代 FFT 的输出按"位反转"(bit-reversed)顺序排列。例如,当 m=8 时,输出索引的二进制表示为:

自然顺序二进制位反转对应输出
00000000
10011004
20100102
30111106
41000011
51011015
61100113
71111117

Bit Reversal 的影响

许多 FFT 应用(如泊松方程求解)可以按位反转顺序使用结果,无需额外排序。但需要自然顺序输出的应用必须执行一次位反转排列,这类似于小规模的转置操作。

递归与迭代的结合

实用 FFT 算法通常结合递归和迭代:顶层使用递归以利用内存层次结构(cache 友好),底层使用迭代以减少函数调用开销。FFTW 等库正是采用这种混合策略。


6. 并行一维 FFT

FFT 的并行化面临数据依赖的挑战——蝶形运算(butterfly)模式决定了哪些计算可以并行执行。

蝶形运算与 PRAM 算法

FFT 的数据依赖图是一个 log m 层的蝶形网络。在 PRAM(并行随机存取机)模型下,每层的蝶形运算完全并行,因此仅需 O(log m) 时间。但这忽略了通信成本。

Block Layout vs. Cyclic Layout

在分布式内存系统中,数据分布策略直接影响通信量:

布局局部性通信阶段
Block Layout前 log(m/p) 步局部后 log p 步需通信
Cyclic Layout前 log(m/p) 步局部后 log p 步需通信

无论哪种布局,通信都集中在 log p 个阶段中。每个阶段需要传输 m/p 个字。

并行复杂度模型(LogP)

在 LogP 模型下(参数:延迟 α,带宽 β,每处理器计算速率 1):

Time(FFT) = 2m·log(m)/p          ... 完全并行计算
           + log(p) · α            ... log p 个通信阶段
           + m·log(p)/p · β        ... 每阶段传输 m/p 字

7. Transpose 算法

Transpose 算法通过重新组织通信,将 FFT 的通信集中到一次全局转置中,从而优化整体性能。

算法思想

  1. 使用 Cyclic Layout 执行前 log(m/p) 步局部 FFT(无需通信)
  2. 执行全局转置(Global Transpose)
  3. 使用 Block Layout 执行后 log p 步(无需通信)

所有通信集中在转置步骤中。将向量视为 m/p × p 的二维数组,转置就是交换行列索引。

转置的并行复杂度

Time(transposeFFT) = 2m·log(m)/p           ... 计算
                     + (p-1) · α             ... 通信延迟
                     + m·(p-1)/p² · β        ... 通信带宽

为什么 Transpose 更优?

相比普通 FFT 的 log(p)·α 延迟,Transpose 仅需 (p-1)·α。当通信可以流水线化时,延迟项可进一步降至 α。带宽项从 m·log(p)/p 降至 m·(p-1)/p² ≈ m/p,也显著改善。


8. 通信复杂度理论

FFT 的并行通信复杂度已有严格的理论下界,Transpose 算法达到了这些下界。

串行通信复杂度(Hong-Kung, 1981)

在 cache 大小为 M 的串行计算机上,对规模为 m(m > m)的 FFT,必须在主存和 cache 之间传输的数据量为:

W = Θ(m·log(m) / log(M))

证明基于每个数据字在 cache 中最多被重用 log M 次的观察。Transpose 算法达到此下界,这也启发了 cache-oblivious 递归算法的设计。

并行通信复杂度(Aggarwal, Chandra, Snir, 1990)

在 p 个处理器之间执行规模为 m 的 FFT,必须传输的数据量为:

W = Θ(m·log(m) / (p·log(m/p)))

证明假设无重计算(no recomputation),且与局部内存大小无关(要求局部内存 ≥ m/p)。

Transpose 达到最优

当 log(m/p) ≥ log(p)(即 m ≥ p²)时,2 ≥ log(m)/log(m/p) ≥ 1,因此:

W_transpose = Θ(m/p) = Θ(m·log(m)/(p·log(m/p)))

Transpose 算法达到了通信复杂度的理论下界!

下界提出者年份适用场景
Θ(m·log m / log M)Hong-Kung1981串行 cache 模型
Θ(m·log m / (p·log(m/p)))Aggarwal-Chandra-Snir1990并行分布式内存

9. 高维 FFT 的并行化

二维和三维 FFT 是科学计算(如流体力学、分子动力学)的核心,其并行化策略比一维更复杂。

二维 FFT 的三种策略

策略描述通信需求
(1) 2D Block Layout矩阵二维分块,行/列 FFT 均需通信高(行列均需通信)
(2) Block Row + Transpose行 FFT 局部执行,转置后列 FFT 局部执行一次全局转置
(3) Block Row + Parallel Columns行 FFT 并行,列 FFT 也并行两次通信

策略 (2) 最优,因为通过一次转置将通信集中,且可以与计算重叠。

Bisection Bandwidth

FFT 需要至少一次全局转置操作,每个处理器需向其他所有处理器发送 1/p 的数据。Bisection Bandwidth(对分带宽)是网络最窄截面的带宽,限制了转置的性能:

LogGP 模型与重叠

LogGP 模型扩展了 LogP,增加了重叠能力。现代网络(带 DMA)允许通信与计算并行执行。GASNet 等单边通信库利用 RDMA 实现高效 one-sided 通信,相比 MPI 的双边通信,在中型消息上可获得 2.2 倍带宽提升。


10. 三维 FFT 的并行实现

三维 FFT 是实际应用中最常见的场景,其并行化涉及全局转置和通信-计算重叠。

1D 分区策略

将 NX × NY × NZ 的网格在 Z 维度上分为 P 份,每处理器持有 NZ/P 个 NX×NY 平面。前两个维度的 FFT 完全局部,第三维度的 FFT 需要通信。

Step 1: 列方向 FFT(全部局部)
Step 2: 行方向 FFT(全部局部)
Step 3: Z 方向 FFT(需全局转置)

三种转置算法

全局转置有三种不同的消息粒度:

算法消息数单消息大小重叠能力
Packed Slabs (3D)p-1大(如 512KB)无
Slabs (2D)NZ/P × (p-1)中(如 64KB)中等
Pencils (1D)(NX·NZ/P) × (p-1)小(如 8KB)最大

Packed Slabs 算法

每处理器将所有目标数据打包成 p-1 个大消息,一次性发送。对于 512³ 网格、64 处理器,每处理器发送 63 个 512KB 的消息。这种方式无法与计算重叠,但实现简单。

Slabs 算法

将数据分成 NZ/P 个平面,每个平面内按行分成 p 个 slab。FFT 完一个 slab 后立即发送。对于 512³ 网格,每处理器发送 512 个 64KB 的消息。可以在不同平面的计算和通信之间重叠。

Pencils 算法

最细粒度:每完成一行(pencil)就立即发送。对于 512³ 网格,每处理器发送 4096 个 8KB 的消息。通信事件充分分散在计算中,实现最大重叠。

性能对比

在 NAS FT 基准测试中,UPC(GASNet)实现的 Pencils 算法比原始 Fortran/MPI 实现快约 1.9 倍。MPI 在处理 Pencils 的大量小消息时效率显著下降(甚至崩溃),而 GASNet 的 one-sided RDMA 能充分利用网络带宽。

GASNet vs MPI

GASNet 提供 one-sided put/get 通信,无需远端 CPU 参与。在多个平台上对比:


11. FFTW:西方最快的傅里叶变换

FFTW(Fastest Fourier Transform in the West)是一个自动调优的 FFT 库,由 Matteo Frigo 和 Steven Johnson 开发,获得了 1999 年 Wilkinson 数值软件奖。

核心特性

FFTW 高效的三个关键特性

1. 多算法组合 + Planner 自动选择

FFTW 实现了多种 FFT 算法(Cooley-Tukey、Prime Factor、Rader、Bluestein 等)。Planner 在实际运行前测量不同算法组合的速度,选择最优方案。这类似于 PHIPAC/ATLAS 在稠密线性代数中的自动调优策略。

2. 显式递归增强局部性

传统 FFT 采用广度优先遍历(breadth-first),对整个数组做 log n 遍扫描。FFTW 使用自然递归(深度优先),当子问题小到足以放入 cache 时自动停止划分。这种cache-oblivious 策略对任何 cache 大小都有效,且对所有 cache 层级同时最优。

3. Codelets:自动生成的优化内核

递归的基例是"codelet"——针对小规模 DFT(通常 ≤ 64)的高度优化代码。Codelet 由专用的离线"编译器"生成,充分利用 SIMD 指令(如 SSE2)和寄存器级优化。

使用示例

fftw_complex *in, *out;
fftw_plan p;

in = fftw_alloc_complex(n);
out = fftw_alloc_complex(n);

// 创建"plan"——FFTW 自动选择最优算法
p = fftw_plan_dft_1d(n, in, out, FFTW_FORWARD, FFTW_MEASURE);

// 多次执行同一大小的变换
fftw_execute(p);
fftw_execute(p);
...

fftw_destroy_plan(p);
fftw_free(in);
fftw_free(out);

Cache-Oblivious 算法

Cache-oblivious 算法不需要知道 cache 大小就能达到 cache 最优。Singleton(1967)和 Frigo 等人(1999)证明,递归分治天然具有 cache-oblivious 特性——当递归子问题小到能放入 cache 时,自动获得高命中率,无论 cache 大小如何。

性能表现

FFTW 在各种平台上表现优异:


12. Spiral:硬件/软件自动生成

Spiral 项目将 FFT 的自动调优思想推广到更广泛的 DSP(数字信号处理)算法领域。

核心理念

Spiral 不仅为软件实现自动调优,还支持硬件(FPGA/ASIC)的自动生成。它使用形式化的数学变换规则空间和搜索/学习算法来发现最优实现。

覆盖范围

更多信息请访问 www.spiral.net,提供在线代码生成器和相关论文。


13. 课程测验

Q1:当 m=4 时,单位根 ω 的值是多少?

Q2:分治 FFT 的递推关系 T(m) = 2T(m/2) + O(m) 的解是什么?

Q3:在并行 FFT 的 Transpose 算法中,假设 m ≥ p²,通信数据量达到什么渐近复杂度?

Q4:FFTW 的 cache-oblivious 递归策略为什么对任何 cache 大小都有效?

Q5:在三维 FFT 的三种转置算法中,Pencils 算法相比 Packed Slabs 算法的主要优势是什么?


要点总结

FFT 是科学计算的基石

FFT 将 DFT 从 O(m²) 降至 O(m log m),是 20 世纪最重要的算法之一。掌握 FFT 的原理和优化是并行计算研究者的基本功。

通信是并行 FFT 的核心挑战

Transpose 算法将通信集中并达到理论下界。理解 Hong-Kung 和 ACS 下界有助于设计最优并行算法。

自动调优释放硬件潜力

FFTW 和 Spiral 通过多算法组合、cache-oblivious 递归和自动代码生成,实现了接近峰值的性能,无需手动调优。

One-sided 通信的优势

GASNet 的 RDMA put/get 在 FFT 的全局转置中显著优于 MPI,尤其在大量小消息场景下。理解通信模型对高性能 FFT 实现至关重要。


延伸阅读