cuFFT API参考文档
cuFFT(CUDA快速傅里叶变换库)的API参考指南。
1. 简介
cuFFT 版本说明: CUDA Toolkit 版本说明
cuFFT GitHub示例: CUDA库示例
Nvidia开发者论坛: GPU加速库
提供反馈: Math-Libs-Feedback@nvidia.com
相关FFT库:
相关cuFFT博客文章和GTC演讲:
本文档介绍NVIDIA® CUDA®快速傅里叶变换(FFT)产品cuFFT。它包含两个独立库:cuFFT和cuFFTW。cuFFT库专为在NVIDIA GPU上实现高性能而设计。cuFFTW库作为移植工具提供,使FFTW用户能够以最小的工作量开始使用NVIDIA GPU。
FFT(快速傅里叶变换)是一种采用分治算法高效计算复数或实数数据集离散傅里叶变换的方法。作为计算物理学和通用信号处理领域最重要且应用最广泛的数值算法之一,cuFFT库为在NVIDIA GPU上计算FFT提供了简洁接口,使用户能够通过这个高度优化且经过验证的FFT库,快速利用GPU的浮点运算能力和并行处理优势。
cuFFT产品在NVIDIA GPU上高效支持广泛的FFT输入和选项。此版本的cuFFT库支持以下功能:
针对输入尺寸高度优化的算法,这些尺寸可以表示为\(2^{a} \times 3^{b} \times 5^{c} \times 7^{d}\)的形式。通常质因数越小性能越好,例如2的幂次是最快的。
针对任意输入数据规模的\(O\left( n\log n \right)\)算法
半精度(16位浮点数)、单精度(32位浮点数)和双精度(64位浮点数)。精度较低的变换具有更高的性能。
-
复数与实数输入输出。实数输入或输出相比复数需要更少的计算量和数据,通常能更快获得解。支持的类型包括:
C2C - 复数输入到复数输出
R2C - 实数输入转复数输出
C2R - 对称复数输入到实数输出
一维、二维和三维变换
同时执行多个一维、二维和三维变换。这些批量变换的性能高于单个变换。
原地转换与非原地转换
任意维度内和维度间的元素步长(跨步布局)
FFTW兼容的数据布局
跨多个GPU执行转换
流式执行,支持异步计算和数据移动
cuFFTW库提供了FFTW3 API,以便于移植现有的FFTW应用程序。
请注意,从CUDA 11.0开始,支持的最低GPU架构为SM35。详见Deprecated Functionality。
2. 使用cuFFT API
本章概述了cuFFT库API的基本内容。如需了解具体函数的完整信息,请参阅cuFFT API参考。建议用户在继续阅读更详细说明之前先阅读本章。
离散傅里叶变换(DFT)将一个复数值向量\(x_{k}\)(时域)映射到其频域表示,表示为:
\(X_{k} = \sum\limits_{n = 0}^{N - 1}x_{n}e^{-2\pi i\frac{kn}{N}}\) |
其中\(X_{k}\)是一个相同大小的复数值向量。这被称为正向DFT。如果将e的指数符号改为正号,则该变换为逆向变换。根据\(N\)的不同,会采用不同的算法以获得最佳性能。
cuFFT API的设计灵感来源于FFTW——这是最流行且高效的基于CPU的FFT库之一。cuFFT提供了一种称为计划的简单配置机制,该机制利用内部构建模块来针对给定配置和所选特定GPU硬件优化变换。随后,当调用执行函数时,实际变换将按照执行计划进行。这种方法的优势在于,一旦用户创建了计划,库就会保留执行计划所需的所有状态,无需重新计算配置即可多次执行。这种模型非常适合cuFFT,因为不同类型的FFT需要不同的线程配置和GPU资源,而计划接口提供了一种复用配置的简单方式。
使用cuFFT计算大小为NX的一维DFT批次BATCH通常如下所示:
#define NX 256
#define BATCH 10
#define RANK 1
...
{
cufftHandle plan;
cufftComplex *data;
...
cudaMalloc((void**)&data, sizeof(cufftComplex)*NX*BATCH);
cufftPlanMany(&plan, RANK, NX, &iembed, istride, idist,
&oembed, ostride, odist, CUFFT_C2C, BATCH);
...
cufftExecC2C(plan, data, data, CUFFT_FORWARD);
cudaDeviceSynchronize();
...
cufftDestroy(plan);
cudaFree(data);
}
2.1. 访问cuFFT
cuFFT和cuFFTW库以共享库形式提供。它们由已编译的程序组成,用户可通过编译器和链接器将其集成到应用程序中。cuFFT可从https://developer.nvidia.com/cufft下载。通过选择下载CUDA正式发布版,所有用户均可安装包含CUDA工具包、SDK代码示例和开发驱动程序的软件包。CUDA工具包中包含cuFFT,示例代码中则包含simplecuFFT。
Linux版本的simplecuFFT默认根安装目录为/usr/local/cuda,且产品文件按以下路径存放。请根据您的系统情况相应修改Makefile。
产品 |
位置和名称 |
包含文件 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
最常见的情况是开发者修改现有的CUDA例程(例如filename.cu)以调用cuFFT例程。这种情况下,应将头文件cufft.h或cufftXt.h插入到filename.cu文件中,并将该库包含在链接行中。单条编译链接行可能如下所示:
/usr/local/cuda/bin/nvcc [options] filename.cu … -I/usr/local/cuda/inc -L/usr/local/cuda/lib -lcufft
当然通常会有许多编译行,只要库路径设置正确,编译器g++也可用于链接。
使用FFTW接口的用户(参见FFTW Interface to cuFFT)需要包含cufftw.h头文件,并链接cuFFT和cuFFTW库。
cuFFT和cuFFTW库中的函数假定数据位于GPU可见内存中。这意味着任何通过cudaMalloc、cudaMallocHost和cudaMallocManaged分配的内存,或通过cudaHostRegister注册的内存,都可以作为cuFFT和cuFFTW函数的输入、输出或计划工作区使用。为了获得最佳性能,输入数据、输出数据和计划工作区应驻留在设备内存中。
cuFFTW库还支持不可见于GPU的输入数据和输出数据。
2.2. 傅里叶变换设置
使用cuFFT库的第一步是通过以下方式之一创建计划:
cufftPlan1D() / cufftPlan2D() / cufftPlan3D()- 分别为1D/2D/3D变换创建简单计划。cufftPlanMany()- 创建一个支持批处理输入和跨步数据布局的计划。cufftXtMakePlanMany()- 创建一个支持批处理输入和跨步数据布局的计划,适用于所有支持的精度。
在计划创建函数中,cufftPlanMany()允许使用更复杂的数据布局和批量执行。执行特定大小和类型的转换可能需要多个处理阶段。当生成转换计划时,cuFFT会推导出需要执行的内部步骤。这些步骤可能包括多次内核启动、内存拷贝等操作。此外,所有中间缓冲区分配(在CPU/GPU内存上)都发生在计划阶段。这些缓冲区在计划销毁时会被释放。在最坏情况下,cuFFT库会为8*batch*n[0]*..*n[rank-1] cufftComplex或cufftDoubleComplex元素分配空间(其中batch表示将并行执行的转换数量,rank是输入数据的维度数(参见多维转换),而n[]是转换维度数组),分别用于单精度和双精度转换。根据计划配置的不同,可能会使用更少的内存。在某些特定情况下,临时空间分配可以低至1*batch*n[0]*..*n[rank-1] cufftComplex或cufftDoubleComplex元素。这个临时空间在创建每个单独计划时独立分配(即计划之间不共享临时空间)。
使用该库的下一步是调用执行函数,例如cufftExecC2C()(参见Parameter cufftType),该函数将按照规划阶段定义的规格执行变换。
用户可以创建一个cuFFT计划,并通过提供不同的输入和输出指针对不同数据集执行多次变换。当不再需要该计划时,应调用cufftDestroy()函数来释放为该计划分配的资源。
2.2.1. 空闲内存要求
首次调用任何cuFFT函数时,会初始化cuFFT内核。如果GPU上没有足够的可用内存,此操作可能会失败。建议先初始化cufft(例如通过创建计划),然后再分配内存。
2.2.2. 计划初始化时间
在计划初始化期间,cuFFT执行一系列步骤,包括通过启发式方法确定使用哪些内核以及加载内核模块。从CUDA 12.0开始,cuFFT更大比例的内核采用CUDA并行线程执行汇编形式(PTX代码)而非二进制形式(cubin对象)。当cuFFT计划初始化时,CUDA设备驱动程序会在运行时加载cuFFT内核的PTX代码并进一步编译为二进制代码。这一过程被称为即时编译(JIT compilation)。
JIT编译会略微增加cuFFT计划初始化时间,具体取决于变换大小和主机CPU速度(参见模块加载驱动API)。但JIT开销仅在使用计划创建函数进行计划初始化时首次生成二进制代码时发生。设备驱动程序会自动缓存生成的二进制代码副本,以避免在后续调用中重复编译。如有需要,可以自定义CUDA_CACHE_PATH或CUDA_CACHE_MAXSIZE来设置缓存文件夹和最大容量(详见CUDA环境变量),但通常默认设置即可满足需求。
2.3. 傅里叶变换类型
除了通用的复数到复数(C2C)变换外,cuFFT还高效实现了另外两种类型:实数到复数(R2C)和复数到实数(C2R)。在许多实际应用中,输入向量是实数值的。可以很容易证明,在这种情况下输出满足埃尔米特对称性(\(X_{k} = X_{N - k}^{\ast}\),其中星号表示复共轭)。反之亦然:对于复数-埃尔米特输入,逆变换将是纯实数值的。cuFFT利用这种冗余性,仅处理埃尔米特向量的前半部分。
单精度和双精度的变换执行函数分别定义为:
cufftExecC2C() / cufftExecZ2Z()- 单/双精度复数到复数变换。cufftExecR2C() / cufftExecD2Z()- 单/双精度实数到复数正向变换。cufftExecC2R() / cufftExecZ2D()- 单/双精度复数到实数的逆变换。
每个函数需要不同的输入数据布局(详情请参阅数据布局)。
注意
复数到实数(C2R)变换接受共轭对称的复数输入。对于一维信号,这要求第0个元素(以及当N为偶数时的第\(\frac{N}{2}\)个输入)必须是实数,即其虚部应为零。 对于d维信号,这意味着\(x_{(n_{1},n_{2},\ldots,n_{d})} = x_{(N_{1} - n_{1},N_{2} - n_{2},\ldots,N_{d} - n_{d})}^{\ast}\)。 否则,变换的行为将是未定义的。另请参阅Multidimensional Transforms。
函数 cufftXtExec() 和 cufftXtExecDescriptor() 可以对任何支持的类型执行变换。
2.3.1. 半精度cuFFT变换
半精度转换有以下限制:
最低支持的GPU架构为SM_53
大小限制为仅支持2的幂次方
不支持在实数到复数及复数到实数变换中对实部进行跨步操作
不支持多个GPU
不支持跨越超过40亿元素的转换
有关计划创建的详细信息,请参考cufftXtMakePlanMany函数。
CUDA工具包提供了cuda_fp16.h头文件,其中包含用于处理半精度算术运算的类型和内置函数。
2.3.2. Bfloat16精度cuFFT变换
cuFFT 支持使用 nv_bfloat16 数据类型的 bfloat16 精度。请注意,当以 bfloat16 精度计算 FFT 时,cuFFT 会结合使用单精度和 bfloat16 精度的算术运算。Bfloat16 精度的变换与半精度变换具有类似的限制:
最低GPU架构要求为SM_80
大小限制为仅支持2的幂次方
不支持对实数到复数及复数到实数变换的实部进行跨步操作
不支持多个GPU
不支持跨越超过40亿元素的转换
有关计划创建的详细信息,请参考cufftXtMakePlanMany函数。
CUDA工具包提供了cuda_bf16.h头文件,其中包含用于处理bfloat16精度算术的类型和内置函数。
2.4. 数据布局
在cuFFT库中,数据布局严格取决于配置和变换类型。对于一般的复数到复数变换,输入和输出数据应分别为单精度和双精度模式下的cufftComplex/cufftDoubleComplex数组。在C2R模式下,只需要一个仅包含非冗余复数元素的输入数组\((x_{1},x_{2},\ldots,x_{\lfloor\frac{N}{2}\rfloor + 1})\)。输出数组\((X_{1},X_{2},\ldots,X_{N})\)在此模式下由cufftReal/cufftDouble元素组成。最后,R2C需要一个实数值的输入数组\((X_{1},X_{2},\ldots,X_{N})\),并返回一个非冗余复数元素的数组\((x_{1},x_{2},\ldots,x_{\lfloor\frac{N}{2}\rfloor + 1})\)。
在实数到复数和复数到实数的变换中,输入数据的大小与输出数据的大小不同。对于非原地变换,会创建一个适当大小的单独数组。对于原地变换,用户应使用padded数据布局。此布局与FFTW兼容。
在padded布局中,输出信号起始于与输入数据相同的内存地址。因此,实数到复数转换的输入数据和复数到实数转换的输出数据必须进行填充。
一维变换的输入/输出数据预期大小总结如下表所示:
FFT类型 |
输入数据大小 |
输出数据大小 |
|---|---|---|
C2C |
\(x\) |
\(x\) |
C2R |
\(\left\lfloor \frac{x}{2} \right\rfloor + 1\) |
\(x\) |
R2C* |
\(x\) |
\(\left\lfloor \frac{x}{2} \right\rfloor + 1\) |
实数到复数的变换本质上是正向变换。对于需要FFTW兼容输出的原地实数到复数变换,输入大小必须填充为\(\left( {\lfloor\frac{N}{2}\rfloor + 1} \right)\)个复数元素。对于非原地变换,输入和输出大小分别对应逻辑变换尺寸\(N\)和非冗余尺寸\(\lfloor\frac{N}{2}\rfloor + 1\)。
复数到实数的变换是隐式逆变换。对于选择FFTW兼容输出(默认填充模式)的就地复数到实数FFT,输入大小假定为\(\lfloor\frac{N}{2}\rfloor + 1\)cufftComplex元素。请注意,当选择非单位输入和输出步长时,就地复数到实数FFT可能会覆盖任意虚部输入点值。异地复数到实数FFT将始终覆盖输入缓冲区。对于异地变换,输入和输出大小分别匹配逻辑变换的非冗余大小\(\lfloor\frac{N}{2}\rfloor + 1\)和大小\(N\)。
2.5. 多维变换
多维DFT将一个\(d\)维数组\(x_{\mathbf{n}}\)映射到其频域数组,其中\(\mathbf{n} = (n_{1},n_{2},\ldots,n_{d})\),映射关系如下:
\(X_{\mathbf{k}} = \sum\limits_{n = 0}^{N - 1}x_{\mathbf{n}}e^{-2\pi i\frac{\mathbf{k}\mathbf{n}}{\mathbf{N}}}\) |
其中 \(\frac{\mathbf{n}}{\mathbf{N}} = (\frac{n_{1}}{N_{1}},\frac{n_{2}}{N_{2}},\ldots,\frac{n_{d}}{N_{d}})\),求和符号表示嵌套求和集合
\(\sum\limits_{n_{1} = 0}^{N_{1} - 1}\sum\limits_{n_{2} = 0}^{N_{2} - 1}\ldots\sum\limits_{n_{d} = 0}^{N_{d} - 1}\) |
cuFFT支持一维、二维和三维变换,这些变换都可以通过相同的cufftExec*函数调用(参见Fourier变换类型)。
与一维情况类似,实值输入数据的频域表示满足埃尔米特对称性,定义为:\(x_{(n_{1},n_{2},\ldots,n_{d})} = x_{(N_{1} - n_{1},N_{2} - n_{2},\ldots,N_{d} - n_{d})}^{\ast}\)。
C2R和R2C算法利用这一特性,仅对信号数组的一半元素进行操作,具体针对:\(x_{\mathbf{n}}\)其中\(\mathbf{n} \in \{ 1,\ldots,N_{1}\} \times \ldots \times \{ 1,\ldots,N_{d - 1}\} \times \{ 1,\ldots,\lfloor\frac{N_{d}}{2}\rfloor + 1\}\)。
数据布局中描述的数据对齐通用规则同样适用于高维变换。下表总结了多维DFT的输入和输出数据大小:
维度 |
FFT类型 |
输入数据大小 |
输出数据大小 |
|---|---|---|---|
一维 |
C2C |
\(\mathbf{N}_{1}\) |
\(\mathbf{N}_{1}\) |
一维 |
C2R |
\(\lfloor\frac{\mathbf{N}_{1}}{2}\rfloor + 1\) |
\(\mathbf{N}_{1}\) |
一维 |
R2C |
\(\mathbf{N}_{1}\) |
\(\lfloor\frac{\mathbf{N}_{1}}{2}\rfloor + 1\) |
2D |
C2C |
\(\mathbf{N}_{1}\mathbf{N}_{2}\) |
\(\mathbf{N}_{1}\mathbf{N}_{2}\) |
2D |
C2R |
\(\mathbf{N}_{1}(\lfloor\frac{\mathbf{N}_{2}}{2}\rfloor + 1)\) |
\(\mathbf{N}_{1}\mathbf{N}_{2}\) |
2D |
R2C |
\(\mathbf{N}_{1}\mathbf{N}_{2}\) |
\(\mathbf{N}_{1}(\lfloor\frac{\mathbf{N}_{2}}{2}\rfloor + 1)\) |
3D |
C2C |
\(\mathbf{N}_{1}\mathbf{N}_{2}\mathbf{N}_{3}\) |
\(\mathbf{N}_{1}\mathbf{N}_{2}\mathbf{N}_{3}\) |
3D |
C2R |
\(\mathbf{N}_{1}\mathbf{N}_{2}(\lfloor\frac{\mathbf{N}_{3}}{2}\rfloor + 1)\) |
\(\mathbf{N}_{1}\mathbf{N}_{2}\mathbf{N}_{3}\) |
3D |
R2C |
\(\mathbf{N}_{1}\mathbf{N}_{2}\mathbf{N}_{3}\) |
\(\mathbf{N}_{1}\mathbf{N}_{2}(\lfloor\frac{\mathbf{N}_{3}}{2}\rfloor + 1)\) |
例如,为输出一个非原位实数到复数变换而静态声明一个三维数组,看起来会是这样:
cufftComplex odata[N1][N2][N3/2+1];
2.6. 高级数据布局
高级数据布局功能允许仅转换输入数组的子集,或仅输出到更大数据结构的一部分。可以通过调用函数来设置:
cufftResult cufftPlanMany(cufftHandle *plan, int rank, int *n, int *inembed,
int istride, int idist, int *onembed, int ostride,
int odist, cufftType type, int batch);
将inembed或onembed设置为NULL是一种特殊情况,等同于为每个参数传递n。这与基本数据布局相同,其他高级参数如istride将被忽略。
如需使用高级参数,则必须正确指定所有高级接口参数。高级参数以相关数据类型(cufftReal, cufftDoubleReal, cufftComplex, 或 cufftDoubleComplex)为单位定义。
高级布局可以视为对输入/输出数据数组访问之上的一层额外抽象。批次中信号编号b内坐标为[z][y][x]的元素将对应内存中的以下地址:
-
一维
input[ b * idist + x * istride ]output[ b * odist + x * ostride ] -
二维
input[ b * idist` + (x * inembed[1] + y) * istride ]output[ b * odist + (x * onembed[1] + y) * ostride ] -
3D
input[ b * idist + ((x * inembed[1] + y) * inembed[2] + z) * istride ]output[ b * odist + ((x * onembed[1] + y) * onembed[2] + z) * ostride ]
istride 和 ostride 参数分别表示在最内层维度上两个连续输入元素和输出元素之间的距离。在单个一维变换中,如果要使用每个输入元素进行变换,应将 istride 设置为 \(1\);如果每隔一个输入元素才用于变换,则应将 istride 设置为 \(2\)。类似地,在单个一维变换中,如果需要紧凑地连续输出最终元素,应将 ostride 设置为 \(1\);如果需要在最内层维度输出数据之间留出间距,则应将 ostride 设置为元素之间的距离。
inembed和onembed参数分别定义了输入数组和输出数组中每个维度的元素数量。inembed[rank-1]表示输入数据最内层维度(最低有效维度)的元素数量(不包括istride元素);因此输入数组最内层维度的总元素数为istride*inembed[rank-1]。inembed[0]或onembed[0]对应最外层维度(最高有效维度),由于idist或odist参数已提供该信息,实际上会被忽略。请注意,变换的每个维度大小应小于或等于对应维度的inembed和onembed值,即n[i] ≤ inembed[i],n[i] ≤ onembed[i],其中\(i \in \{ 0,\ldots,rank - 1\}\)。
idist 和 odist 参数表示输入和输出数据中两个连续批次首元素之间的距离。
2.7. 流式cuFFT变换
每个cuFFT计划都可以关联到一个CUDA流。一旦关联后,该计划所有内部阶段的启动都将通过指定的流执行。cuFFT执行的流式处理可以实现变换与内存拷贝之间的潜在重叠。(有关流的更多信息,请参阅NVIDIA CUDA编程指南)。如果计划未关联任何流,则启动将在stream(0)即默认CUDA流中执行。请注意,许多计划执行需要多次内核启动。
cuFFT在内部使用私有流来排序操作,包括事件同步。cuFFT不保证内部操作的顺序,该顺序仅相对于用户设置的流保持不变。
从CUDA 11.2版本(cuFFT 10.4.0)开始,cufftSetStream()在多GPU场景中已获得支持。然而,当使用流时,跨多GPU调用cufftXtMemcpy()仍然是同步操作。在早期版本的cuFFT中,多GPU场景下调用cufftSetStream()会返回错误。同样地,在通过cufftSetStream()设置流之后,调用某些多GPU函数如cufftXtSetCallback()也会导致错误(详情请参阅API函数文档)。
请注意,为了使用单一计划句柄重叠计划,用户需要管理工作区域缓冲区。每个并发计划执行都需要其独占的工作区域。工作区域可以通过cufftSetWorkArea函数设置。
2.8. 多GPU cuFFT变换
cuFFT支持使用最多16个连接到CPU的GPU来执行傅里叶变换,其计算分布在多个GPU上。已定义了一个API,允许用户编写新代码或修改现有代码以使用此功能。
一些现有函数,例如使用cufftCreate()创建计划的操作,同样适用于多GPU场景。多GPU例程在其名称中包含Xt标识。
GPU上的内存由辅助函数cufftXtMalloc()/cufftXtFree()和cufftXtMemcpy()通过cudaLibXtDesc描述符进行管理。
Performance is a function of the bandwidth between the GPUs, the computational ability of the individual GPUs, and the type and number of FFT to be performed. The highest performance is obtained using NVLink interconnect (https://www.nvidia.com/object/nvlink.html). The second best option is using PCI Express 3.0 between the GPUs and ensuring that both GPUs are on the same switch. Note that multiple GPU execution is not guaranteed to solve a given size problem in a shorter time than single GPU execution.
cuFFT的多GPU扩展构建在其可扩展的API之上。使用该API定义和执行变换的一般步骤如下:
cufftCreate()- 创建一个空计划,与单GPU情况相同cufftXtSetGPUs()- 定义要使用的GPU可选:
cufftEstimate{1d,2d,3d,Many}()- 估算所需工作区域的大小。这些函数与单GPU情况下使用的相同,尽管参数workSize的定义反映了使用的GPU数量。cufftMakePlan{1d,2d,3d,Many}()- 创建计划。这些函数与单GPU情况下使用的相同,尽管参数workSize的定义反映了使用的GPU数量。可选:
cufftGetSize{1d,2d,3d,Many}()- 对所需工作区大小的精确估算。这些函数与单GPU情况下使用的相同,尽管参数workSize的定义反映了使用的GPU数量。可选:
cufftGetSize()- 检查工作空间大小。这与单GPU情况下使用的函数相同,尽管参数workSize的定义反映了使用的GPU数量。可选:
cufftXtSetWorkArea()- 自行分配工作区。cufftXtMalloc()- 在GPU上分配描述符和数据cufftXtMemcpy()- 将数据复制到GPUcufftXtExecDescriptorC2C()/cufftXtExecDescriptorZ2Z()- 执行计划cufftXtMemcpy()- 从GPU复制数据cufftXtFree()- 释放由cufftXtMalloc()分配的所有内存cufftDestroy()- 释放cuFFT计划资源
2.8.1. 计划规范与工作区域
在单GPU情况下,通过调用cufftCreate()后接调用cufftMakePlan*()来创建计划。对于多GPU场景,需要通过调用cufftXtSetGPUs()来指定执行所用的GPU设备,且该调用必须位于cufftCreate()之后、cufftMakePlan*()之前。
请注意,当为单个GPU调用cufftMakePlan*()时,工作区位于该GPU上。在多GPU计划中,返回的工作区包含多个条目;每个GPU对应一个值。也就是说,workSize指向一个size_t数组,每个GPU对应一个条目。此外,步幅和批次适用于与计划关联的所有GPU上的整个计划。
一旦通过调用cufftMakePlan*()锁定计划后,可以在调用cufftXtExecDescriptor*()时指定不同的描述符来对不同数据集执行该计划,但新的描述符必须按照相同顺序使用相同的GPU。
与单GPU情况类似,cufftEstimateSize{Many,1d,2d,3d}()和cufftGetSize{Many,1d,2d,3d}()提供了多GPU计划所需工作区大小的估计值,此时workSize指向一个size_t数组,每个GPU对应一个条目。
类似地,cufftGetSize()返回的实际工作大小是一个size_t数组,在多GPU情况下每个GPU对应一个条目。
2.8.2. 辅助函数
多GPU cuFFT执行函数在执行前假设输入数据已复制到特定GPU,以及执行后输出数据位于哪些GPU上。cuFFT提供辅助函数帮助用户操作多GPU上的数据。这些函数必须在调用cufftMakePlan*()之后调用。
在单个GPU上,用户可以调用cudaMalloc()和cudaFree()来分配和释放GPU内存。为了在多GPU情况下提供类似功能,cuFFT包含了cufftXtMalloc()和cufftXtFree()函数。cufftXtMalloc()函数会返回一个描述符,用于指定这些内存的位置。
在单个GPU上,用户可调用cudaMemcpy()实现主机与GPU内存间的数据传输。为在多GPU场景下提供类似功能,cuFFT包含了cufftXtMemcpy()接口,支持用户在主机与多块GPU内存之间、甚至不同GPU内存之间进行数据拷贝。
所有单GPU cuFFT FFT运算返回的数据都采用自然顺序输出,即结果的排列方式与对数据执行离散傅里叶变换(DFT)时相同。某些快速傅里叶变换会产生中间结果,此时数据会保留在自然输出的某种排列顺序中。当批处理数量为1时,数据会以自然输出的某种排列顺序保留在GPU内存中。
当使用cufftXtMemcpy()将数据从GPU内存复制回主机内存时,无论GPU上的数据是自然顺序还是置换顺序,结果都会以自然顺序呈现。使用CUFFT_COPY_DEVICE_TO_DEVICE允许用户将单次变换后产生的置换数据格式复制到GPU上的自然顺序。
2.8.3. 多GPU上的2D和3D置换输入变换
对于在多个GPU上执行的单个2D或3D变换,当cufftXtMemcpy()将数据分发到各个GPU时,数组会沿X轴进行划分。例如,对于两个GPU的情况,X维度点的一半(针对所有Y和Z值)会被复制到每个GPU上。当计算变换时,数据会被重新排列,使其沿Y轴划分。也就是说,Y维度点的一半(针对所有X和Z值)会分布在每个GPU上。
当cuFFT为多GPU上的单次变换创建2D或3D计划时,它实际上会创建两个计划。一个计划要求输入数据在X轴上分割,另一个计划则要求数据在Y轴上分割。这样设计是因为许多算法会先计算正向FFT,然后对结果执行逐点操作,最后再计算逆FFT。若通过内存拷贝将数据恢复原始顺序会非常耗时。为了避免这种情况,cufftXtMemcpy和cufftXtExecDescriptor()会跟踪数据顺序,从而使用正确的操作。
cuFFT能够以任意顺序处理数据的能力使得以下序列成为可能。
cufftCreate()- 创建一个空计划,与单GPU情况相同cufftXtSetGPUs()- 定义要使用的GPUcufftMakePlan{1d,2d,3d,Many}()- 创建计划。cufftXtMalloc()- 在GPU上分配描述符和数据cufftXtMemcpy()- 将数据复制到GPUcufftXtExecDescriptorC2C()/cufftXtExecDescriptorZ2Z()- 计算正向FFTuserFunction()- 在频域中修改数据cufftXtExecDescriptorC2C()/cufftXtExecDescriptorZ2Z()- 计算逆FFT注意,在执行调用之间无需复制/重新排列数据
cufftXtMemcpy()- 将数据复制到主机cufftXtFree()- 释放由cufftXtMalloc()分配的所有内存cufftDestroy()- 释放cuFFT计划资源
2.8.4. 支持的功能
从 cuFFT 7.0 版本开始,支持将单 GPU 功能的一个子集用于多 GPU 执行。
需求与限制:
所有GPU必须具有相同的CUDA架构级别并支持统一虚拟地址空间。
在Windows系统上,GPU板卡必须运行在Tesla计算集群(TCC)模式下。
对于使用CUDA驱动API的应用程序,在多GPU上运行cuFFT仅与在每个GPU上使用主上下文的应用程序兼容。
不支持跨步输入和输出。
仅在配备NVLink的机器上支持在超过8个GPU(最多16个GPU)上运行cuFFT。
虽然批量计数大于一的变换不会施加额外限制,但单批次的变换存在一些约束。单批次FFT仅支持就地模式,并且根据FFT类型有额外限制。这种行为在下表中进行了总结:
batch=1 |
一维 |
二维 |
三维 |
|---|---|---|---|
|
|
|
|
|
不支持 |
|
|
|
不支持 |
|
|
通用指南如下:
函数
cufftXtSetGPUs()的参数whichGPUs决定了GPU在数据分解中的排序(第一个数据块将被放置在whichGPUs第一个元素所表示的GPU上)整个转换的数据必须适配分配给它的GPU内存容量。
-
对于在
n个GPU上的批量大小m:前
m % n个 GPU 执行 \(\left\lfloor \frac{m}{n} \right\rfloor+\ 1\) 次转换。剩余的GPU执行\(\left\lfloor \frac{m}{n} \right\rfloor\)次转换。
批量大小输出差异:
单GPU的cuFFT结果总是以自然顺序返回。当使用多个GPU执行多次变换时,结果同样以自然顺序返回。当使用多个GPU执行单次变换时,为减少通信时间,返回的结果会是常规结果的排列组合。这种行为总结如下表所示:
GPU数量 |
变换次数 |
GPU上的输出顺序 |
|---|---|---|
一 |
一个或多个转换 |
自然顺序 |
多个 |
一个 |
置换结果 |
多 |
多 |
自然顺序 |
为了在一维单变换情况下为多GPU运行生成GPU内存中的自然顺序结果,需要使用cufftXtMemcpy()并指定CUFFT_COPY_DEVICE_TO_DEVICE参数。
2D和3D多GPU变换支持以置换顺序结果作为输入执行变换。在此情况下执行后,输出将恢复为自然顺序。也可以使用cufftXtMemcpy()配合CUFFT_COPY_DEVICE_TO_DEVICE将2D或3D数据恢复为自然顺序。
请参阅cuFFT代码示例部分,了解单GPU和多GPU的示例。
2.9. cuFFT回调例程
回调例程是由用户提供的核函数例程,cuFFT在加载或存储数据时会调用它们。这允许用户在不额外调用核函数的情况下进行数据预处理或后处理。
注意
在CUDA 12.6更新2中,我们引入了对链接时优化(LTO)回调的支持,以取代已弃用的(旧版)回调。更多详情请参阅LTO加载和存储回调例程。
从CUDA 11.4开始,在所有GPU架构上对使用单独编译设备代码的回调功能(即传统回调)的支持已被弃用。回调功能将继续在所有GPU架构上得到支持。
2.9.1. cuFFT回调例程功能概述
cuFFT提供了一组API,允许用户通过CUDA函数在FFT处理前重定向或操作加载的数据,或在FFT完成后操作存储的数据。对于加载回调,cuFFT会调用回调例程传入输入数据的地址和待加载值的设备内存偏移量,回调例程则返回希望cuFFT使用的替代值。对于存储回调,cuFFT会调用回调例程传入计算得到的值、输出数据的地址以及待写入值的设备内存偏移量,回调例程可修改该值并存储修改后的结果。
为了向cuFFT提供回调功能,需要使用可扩展计划API创建一个计划。在调用cufftCreate之后,用户可以通过以下方式将加载回调例程、存储回调例程或两者与计划关联:
在调用
cufftMakePlan之前调用cufftXtSetJITCallback,用于LTO回调在调用
cufftMakePlan之后调用cufftXtSetCallback,用于旧版回调
调用者还可以选择指定一个设备指针,指向他们希望与计划关联的不透明结构。cuFFT库会将此指针传递给回调例程。调用者可以使用此结构来记住计划的维度和步长,或指向辅助数据的指针等。
在一定的限制条件下,回调例程可以申请共享内存供自身使用。如果请求的共享内存量可用,cufft将在调用回调例程时传递指向该内存的指针。
CUFFT 支持8种类型的回调例程,每种对应以下可能组合之一:加载或存储、实数或复数、单精度或双精度:
对于LTO回调,用户必须提供一个与指定例程类型函数原型匹配的LTO例程。否则,规划函数
cufftMakePlan将会失败。对于传统的回调函数,调用方有责任提供一个与指定例程类型函数原型相匹配的例程。
如果计划句柄已关联指定类型的回调函数,设置的回调函数将用新的回调替换它。
cuFFT的回调例程扩展基于可扩展的cuFFT API构建。定义和执行带回调的变换的一般步骤如下:
cufftCreate()- 创建一个空计划,与单GPU情况相同。(针对LTO回调)
cufftXtSetJITCallback()- 为此计划设置加载和/或存储LTO回调。cufftMakePlan{1d,2d,3d,Many}()- 创建计划。这些函数与单GPU情况下使用的相同。(针对旧版回调)
cufftXtSetCallback()- 为该计划设置加载和/或存储的旧版回调函数。cufftExecC2C() etc.- 执行计划。cufftDestroy()- 释放cuFFT计划资源。
对于维度大小无法分解为小于127的质数的变换,不支持回调函数。维度质因数仅限于2、3、5和7的计划可以安全调用__syncthreads()。对于其他计划,结果未定义。
注意
LTO回调API在64位Windows和LINUX操作系统的动态和静态cuFFT库中可用。LTO回调API要求动态库路径中存在兼容的nvJitLink和NVRTC库。更多详情请参阅LTO加载和存储回调例程。
传统回调API仅在64位LINUX操作系统上的静态cuFFT库中可用。
2.9.2. LTO加载与存储回调例程
针对特定工具包版本的cuFFT中的LTO回调需要使用来自相同或更高版本工具包(但需在同一主版本内)的nvJitLink库。
此外,为了为LTO回调例程指定自定义名称,cuFFT需要使用NVRTC库。cuFFT利用NVRTC编译一个包含自定义符号名的用户回调最小封装层。提供给cuFFT API的自定义符号名必须是一个有效的、以空字符结尾的C字符串,且包含未修饰的名称;目前不支持能改变符号名作用域的关键字(如namespace)或影响名称修饰的关键字(如extern "C")。
所使用的NVRTC库必须来自与nvJitLink库相同版本或更早的工具包,且两者必须属于同一主版本工具包。
例如,在工具包版本12.6中,cuFFT要求nvJitLink必须来自12.X版本的工具包,其中X >= 6,而NVRTC需要来自12.Y版本的工具包,其中0 <= Y <= X。
nvJitLink和NVRTC库都是动态加载的,应该存在于系统的动态链接路径中(例如Unix系统中的LD_LIBRARY_PATH或Windows系统中的PATH)。
LTO回调的代码示例可在公开的CUDA Library Samples github仓库中找到。
2.9.2.1. 指定LTO加载和存储回调例程
在cuFFT中使用LTO回调分为两个部分:
生成LTO回调(即将回调例程编译为LTO-IR)。
将LTO回调与cuFFT计划关联。
要生成 LTO回调,用户可以使用nvcc配合任意支持的标志(如-dlto或-gencode=arch=compute_XX,code=lto_XX,其中XX表示目标GPU架构)将回调设备函数编译为LTO-IR;或者,用户也可以通过NVRTC使用-dlto标志进行运行时编译来生成LTO回调。
请注意,PTX JIT是JIT LTO内核最终化流程的一部分,因此支持比当前系统架构更早的架构;用户可以将回调函数编译为目标架构XX的LTO-IR,并在架构为YY的GPU上执行使用这些回调函数的计划,其中XX <= YY。更多详情请参阅使用nvJitLink库实现运行时LTO的编译器支持和即时(JIT)编译。
例如,如果用户想为R2C变换指定一个加载回调函数,可以编写以下代码
__device__ cufftReal myOwnLTOCallback(void *dataIn,
unsigned long long offset,
void *callerInfo,
void *sharedPtr) {
cufftReal ret;
// use offset, dataIn, and optionally callerInfo to
// compute the return value
return ret;
}
要将回调编译为LTO-IR,用户可以执行以下操作
# Compile the code to SM60 LTO-IR into a fatbin file
nvcc -gencode=arch=compute_60,code=lto_60 -dc -fatbin callback.cu -o callback.fatbin
#Turn the fatbin data into a C array inside a header, for easy inclusion in host code
bin2c --name my_lto_callback_fatbin --type longlong callback.fatbin > callback_fatbin.h
关联 LTO回调与cuFFT计划时,用户可以利用新的API调用cufftXtSetJITCallback(),其工作方式与cufftXtSetCallback()类似,但有一些注意事项。
首先,在通过cufftCreate()创建计划后,必须调用cufftXtSetJITCallback(),然后再通过cufftMakePlan*()及类似例程调用计划初始化函数。
其次,目前不支持从计划中移除LTO回调(使用cufftXtClearCallback())。必须创建一个新计划。
#include <cufftXt.h>
#include "callback_fatbin.h"
int main() {
cufftResult status;
cufftHandle fft_plan;
...
status = cufftCreate(&fft_plan);
// NOTE: LTO callbacks must be set before plan creation and cannot be unset (yet)
size_t lto_callback_fatbin_size = sizeof(my_lto_callback_fatbin);
status = cufftXtSetJITCallback(fft_plan, "myOwnLTOCallback", (void*)my_lto_callback_fatbin, lto_callback_fatbin_size, CUFFT_CB_LD_REAL, (void **)&device_params));
status = cufftMakePlan1d(fft_plan, signal_size, CUFFT_C2R, batches, &work_size);
...
}
2.9.2.2. LTO回调函数详情
以下是用户提供的LTO回调例程的函数原型,cuFFT在转换前调用这些例程来加载数据。
typedef cufftComplex (*cufftJITCallbackLoadC)(void *dataIn,
unsigned long long offset,
void *callerInfo,
void *sharedPointer);
typedef cufftDoubleComplex (*cufftJITCallbackLoadZ)(void *dataIn,
unsigned long long offset,
void *callerInfo,
void *sharedPointer);
typedef cufftReal (*cufftJITCallbackLoadR)(void *dataIn,
unsigned long long offset,
void *callerInfo,
void *sharedPointer);
typedef cufftDoubleReal (*cufftJITCallbackLoadD)(void *dataIn,
unsigned long long offset,
void *callerInfo,
void *sharedPointer);
LTO加载回调的所有参数定义如下:
offset: 输入元素相对于输入数据起始位置的偏移量。这不是字节偏移量,而是从数据起始位置开始计算的元素个数。dataIn: 指向在cufftExecute调用中传入的输入数组起始位置的设备指针。callerInfo: 设备指针,指向通过cufftXtSetCallback调用传入的可选调用者指定数据。sharedPointer: 指向共享内存的指针,仅在用户调用过cufftXtSetCallbackSharedSize()时有效。
以下是用户提供的LTO回调例程的函数原型和类型定义,这些例程由cuFFT在完成变换后调用以存储数据。请注意,存储回调函数不返回值。这是因为存储回调函数不仅负责按需转换数据,还负责将数据写入目标位置。这使得存储回调能够重新排列数据,例如将零频结果移至输出中心。
typedef void (*cufftJITCallbackStoreC)(void *dataOut,
unsigned long long offset,
cufftComplex element,
void *callerInfo,
void *sharedPointer);
typedef void (*cufftJITCallbackStoreZ)(void *dataOut,
unsigned long long offset,
cufftDoubleComplex element,
void *callerInfo,
void *sharedPointer);
typedef void (*cufftJITCallbackStoreR)(void *dataOut,
unsigned long long offset,
cufftReal element,
void *callerInfo,
void *sharedPointer);
typedef void (*cufftJITCallbackStoreD)(void *dataOut,
unsigned long long offset,
cufftDoubleReal element,
void *callerInfo,
void *sharedPointer);
LTO存储回调的所有参数定义如下:
offset: 输出元素距离输出数据起始位置的偏移量。这不是字节偏移量,而是从数据起始位置开始计算的元素数量。dataOut: 指向在cufftExecute调用中传入的输出数组起始位置的设备指针。element: 由CUFFT为offset参数指定的元素计算出的实数或复数结果。callerInfo: 设备指针,指向在cufftXtSetCallback调用中传入的可选的调用者指定数据。sharedPointer: 指向共享内存的指针,仅当用户调用了cufftXtSetCallbackSharedSize()时有效。
2.9.3. 传统加载和存储回调例程
2.9.3.1. 指定传统加载和存储回调例程
为了将遗留回调例程与计划关联,需要获取指向该回调例程的设备指针。
例如,如果用户想为R2C变换指定一个加载回调函数,他们需要编写该回调函数的设备端代码,并定义一个全局设备变量来存储指向该函数的指针:
__device__ cufftReal myOwnCallback(void *dataIn,
size_t offset,
void *callerInfo,
void *sharedPtr) {
cufftReal ret;
// use offset, dataIn, and optionally callerInfo to
// compute the return value
return ret;
}
__device__ cufftCallbackLoadR myOwnCallbackPtr = myOwnCallback;
从主机端来看,用户需要获取传统回调例程的地址,该地址存储在myOwnCallbackPtr中。这可以通过cudaMemcpyFromSymbol实现,具体如下:
cufftCallbackLoadR hostCopyOfCallbackPtr;
cudaMemcpyFromSymbol(&hostCopyOfCallbackPtr,
myOwnCallbackPtr,
sizeof(hostCopyOfCallbackPtr));
hostCopyOfCallbackPtr 随后包含回调例程的设备地址,该地址应传递给 cufftXtSetCallback。请注意,对于多GPU转换,hostCopyOfCallbackPtr 需要是一个指针数组,并且需要为每个GPU调用 cudaMemcpyFromSymbol。请注意,由于变量使用限制,__managed__ 变量不适合传递给 cufftSetCallback(有关 __managed__ 变量的更多信息,请参阅NVIDIA CUDA编程指南)。
2.9.3.2. 传统回调例程函数详情
以下是函数原型和用户提供的传统回调例程的指针类型定义,cuFFT在转换前调用这些例程来加载数据。
typedef cufftComplex (*cufftCallbackLoadC)(void *dataIn,
size_t offset,
void *callerInfo,
void *sharedPointer);
typedef cufftDoubleComplex (*cufftCallbackLoadZ)(void *dataIn,
size_t offset,
void *callerInfo,
void *sharedPointer);
typedef cufftReal (*cufftCallbackLoadR)(void *dataIn,
size_t offset,
void *callerInfo,
void *sharedPointer);
typedef cufftDoubleReal (*cufftCallbackLoadD)(void *dataIn,
size_t offset,
void *callerInfo,
void *sharedPointer);
所有旧版加载回调的参数定义如下:
offset: 输入元素相对于输入数据起始位置的偏移量。这不是字节偏移量,而是从数据起始位置开始计算的元素个数。dataIn: 指向在cufftExecute调用中传入的输入数组起始位置的设备指针。callerInfo: 设备指针,指向在cufftXtSetCallback调用中传入的可选的调用者指定数据。sharedPointer: 指向共享内存的指针,仅在用户调用过cufftXtSetCallbackSharedSize()时有效。
以下是函数原型和用户提供的传统回调例程指针的typedef定义,这些回调例程由cuFFT在转换完成后调用以存储数据。请注意,存储回调函数不返回值。这是因为存储回调函数不仅负责按需转换数据,还负责将数据写入目标位置。这使得存储回调能够重新排列数据,例如将零频结果移动到输出中心。
typedef void (*cufftCallbackStoreC)(void *dataOut,
size_t offset,
cufftComplex element,
void *callerInfo,
void *sharedPointer);
typedef void (*cufftCallbackStoreZ)(void *dataOut,
size_t offset,
cufftDoubleComplex element,
void *callerInfo,
void *sharedPointer);
typedef void (*cufftCallbackStoreR)(void *dataOut,
size_t offset,
cufftReal element,
void *callerInfo,
void *sharedPointer);
typedef void (*cufftCallbackStoreD)(void *dataOut,
size_t offset,
cufftDoubleReal element,
void *callerInfo,
void *sharedPointer);
所有旧版存储回调的参数定义如下:
offset: 输出元素距离输出数据起始位置的偏移量。这不是字节偏移量,而是从数据起始位置开始计算的元素数量。dataOut: 指向在cufftExecute调用中传入的输出数组起始位置的设备指针。element: 由CUFFT为offset参数指定的元素计算出的实数或复数结果。callerInfo: 设备指针,指向在cufftXtSetCallback调用中传入的可选的调用者指定数据。sharedPointer: 指向共享内存的指针,仅当用户调用了cufftXtSetCallbackSharedSize()时有效。
2.9.4. cuFFT回调例程功能的编码注意事项
cuFFT支持在所有类型的变换、维度、批量或元素间跨度上使用回调。单精度和双精度变换均支持回调功能。
cuFFT支持广泛的参数范围,并根据给定计划的参数尝试优化性能。启动的内核数量、每个内核启动的块数以及每个块的线程数,将取决于cuFFT如何分解变换。对于某些配置,cuFFT每个线程可能会加载或存储(并处理)多个输入或输出。对于某些配置,线程可能以任意顺序加载或存储输入或输出,且cuFFT不保证给定线程处理的输入或输出是连续的。这些特性可能随变换大小、变换类型(例如C2C与C2R)、维度数量和GPU架构而变化。这些变化也可能随库版本的不同而改变。
当使用多个内核来实现转换时,第一个内核(负责加载的内核)的线程和块结构通常与最后一个内核(负责存储的内核)的线程和块结构不同。
回调函数的一个常见用途是通过选择性过滤或类型转换来减少读取或写入内存的数据量。当使用多个内核来实现变换时,cuFFT会交替使用工作空间和输出缓冲区来写入中间结果。这意味着输出缓冲区必须始终足够大以容纳整个变换过程。
对于维度可以分解为2、3、5或7的幂次的变换,cuFFT保证它会在内核中安全调用__syncthreads函数的位置调用加载和存储回调例程。调用方需负责确保回调例程处于回调代码已收敛的位置,以避免死锁。对于维度分解为更高素数的计划,回调例程调用__syncthreads的结果是未定义的。
请注意,网格内各块的执行顺序无法保证。因此,回调函数不应依赖内核中的任何特定执行顺序。例如,数据重排序操作(如FFT移位)可能会依赖于块的执行顺序,这种情况下将产生未定义的结果。
2.9.4.1. LTO回调例程的编码注意事项
cuFFT将在执行实数到复数(R2C, D2Z)和复数到复数(C2C, Z2Z)变换时,对输入中的每个点调用一次且仅调用一次LTO加载回调例程。与传统回调不同,对于复数到实数(C2R, Z2D)变换,LTO加载回调可能对每个元素调用多次。输入值不会被更新两次(即变换后的值将存储在寄存器而非内存中,即使是原地变换),但用户不应在其回调设备函数中依赖每个元素的调用次数。
与传统回调类似,LTO存储回调对于输出中的每个点只会调用一次。如果转换是原地进行的(即输入和输出数据位于相同的内存位置),则给定元素的存储回调不能覆盖其他元素。它只能覆盖给定元素,或者写入完全独立的输出缓冲区。
cuFFT目前还不支持多GPU变换的LTO回调功能。
2.9.4.2. 传统回调例程的编码注意事项
cuFFT 支持在任何数量的 GPU 上使用传统回调功能。
cuFFT将为输入中的每个点调用一次且仅一次加载回调例程。同样,它也将为输出中的每个点调用一次且仅一次存储回调例程。如果变换是原地进行的(即输入和输出数据位于相同的内存位置),则给定元素的存储回调不能覆盖其他元素。它可以选择覆盖给定元素,或者写入完全独立的输出缓冲区。
对于多GPU转换,传递给回调例程的索引是该GPU上数据的元素索引,而不是整个输入或输出数据数组的起始索引。
2.10. 线程安全
只要不同的主机线程使用不同的计划执行FFT且输出数据不重叠,cuFFT API就是线程安全的。
2.11. CUDA Graphs支持
在单GPU方案中支持将CUDA Graphs与cuFFT配合使用。从cuFFT 10.4.0版本开始,多GPU方案也支持该功能。与cuFFT方案关联的流必须满足使用流捕获创建图中所述的要求。
注意
从CUDA 11.8开始(包括CUDA 12.0及更高版本),CUDA Graphs不再支持以非原位模式转换加载数据的旧版回调例程。从CUDA 12.6 Update 2开始,LTO回调可用作旧版回调的替代方案,且不受此限制。cuFFT在11.4版本中弃用了基于单独编译设备代码的回调功能(旧版回调)。
2.12. 静态库与回调支持
从6.5版本开始,cuFFT库在Linux和Mac系统上也以静态库形式提供,分别为libcufft_static.a和libcufftw_static.a。Windows系统不支持静态库。静态的cufft和cufftw库依赖于线程抽象层库libculibos.a。
例如,在Linux系统上,要使用cuFFT动态库编译一个小型应用程序,可以使用以下命令:
nvcc mCufftApp.c -lcufft -o myCufftApp
对于Linux上的cufftw,要针对动态库编译一个小型应用程序,可以使用以下命令:
nvcc mCufftwApp.c -lcufftw -lcufft -o myCufftwApp
而要针对静态cuFFT库进行编译,则需要采取额外的步骤。该库需要进行设备链接。这一过程可以在构建和链接简单程序时完成,也可以作为单独步骤进行。完整流程详见Using Separarate Compilation in CUDA。
对于 cuFFT 和 cufftw 9.0 或更高版本,可以使用任何支持的架构进行设备链接:
静态cuFFT编译命令:
nvcc mCufftApp.c -lcufft_static -lculibos -o myCufftApp
静态cufftw编译命令:
nvcc mCufftwApp.c -lcufftw_static -lcufft_static -lculibos -o myCufftwApp
在9.0版本之前,正确链接需要指定支持的架构子集,如下列命令所示:
静态cuFFT编译命令:
nvcc mCufftApp.c -lcufft_static -lculibos -o myCufftApp\
-gencode arch=compute_20,\"code=sm_20\"\
-gencode arch=compute_30,\"code=sm_30\"\
-gencode arch=compute_35,\"code=sm_35\"\
-gencode arch=compute_50,\"code=sm_50\"\
-gencode arch=compute_60,\"code=sm_60\"\
-gencode arch=compute_60,\"code=compute_60\"
静态cufftw编译命令:
nvcc mCufftwApp.c -lcufftw_static -lcufft_static -lculibos -o myCufftwApp\
-gencode arch=compute_20,\"code=sm_20\"\
-gencode arch=compute_30,\"code=sm_30\"\
-gencode arch=compute_35,\"code=sm_35\"\
-gencode arch=compute_50,\"code=sm_50\"\
-gencode arch=compute_60,\"code=sm_60\"\
-gencode arch=compute_60,\"code=compute_60\"
请注意,只要存在二进制兼容的较低架构代码(例如SM52、SM61),cuFFT库可能不会包含某些架构的代码。这一点体现在上述链接命令中,在使用r9.0之前的版本时尤为重要。要确定cuFFT库是否包含特定SM架构,可以使用cuobjdump工具。例如,若想确认是否包含SM_50,可运行命令cuobjdump -arch sm_50 libcufft_static.a。部分内核仅针对特定架构构建(例如半精度运算内核仅适用于SM53及以上架构),这可能导致链接时出现缺少这些架构内核的警告,此类警告可安全忽略。
也可以使用原生主机C++编译器,并将设备链接作为单独步骤执行。更多详情请查阅NVCC文档。根据主机操作系统的不同,在链接行上可能需要一些额外的库,如pthread或dl。
请注意,在这种情况下,不需要cuda库。CUDA运行时会根据需要尝试显式打开cuda库。对于未安装CUDA驱动的系统,这允许应用程序优雅地处理此问题,并在仅支持CPU路径的情况下仍可能运行。
cuFFT静态库支持用户提供的传统回调例程。这些传统回调例程是CUDA设备代码,必须使用NVCC单独编译并与cuFFT库链接。详情请参阅NVCC文档中关于单独编译的部分。如果在编译回调函数时指定了SM架构,则必须指定cuFFT包含的其中一个SM架构。
2.12.1. 不支持旧版回调的静态库
从cuFFT 9.2版本开始,新增了一个cuFFT静态库变体libcufft_static_nocallback.a。这个新版本不包含旧版回调功能,并且仅能使用主机编译器进行链接。
2.13. 准确性与性能
离散傅里叶变换(DFT)可以通过矩阵向量乘法实现,这需要\(O(N^{2})\)次运算。然而,cuFFT库采用Cooley-Tukey算法来减少所需运算次数,从而优化特定变换尺寸的性能。该算法将DFT矩阵表示为稀疏基础矩阵的乘积。cuFFT库实现了以下基础模块:radix-2、radix-3、radix-5和radix-7。因此,任何可以分解为\(2^{a} \times 3^{b} \times 5^{c} \times 7^{d}\)(其中a、b、c和d为非负整数)的变换尺寸在cuFFT库中都能获得优化性能。对于其他质数m(m值小于128),也存在radix-m基础模块。当长度无法分解为2至127质数幂的乘积时,将使用Bluestein算法。由于Bluestein实现每个输出点需要比Cooley-Tukey实现更多的计算量,因此Cooley-Tukey算法的精度更优。纯Cooley-Tukey实现具有出色的精度,其相对误差与\(\log_{2}(N)\)成比例增长,其中\(N\)表示以点数为单位的变换尺寸。
对于由Cooley-Tukey代码路径处理的大小,通过应用以下约束条件可获得最高效的实现(按从最通用到最专业化的约束顺序列出,每个后续约束都可能带来额外的性能提升)。
由于半精度浮点运算所能表示的范围有限,半精度变换可能并不适用于所有类型的问题。请注意,FFT结果的第一个元素是所有输入元素的总和,对于某些输入可能会发生溢出。
只要在多次运行之间保持以下因素不变,cuFFT库产生的结果就是确定性的(即比特级可复现的):计划输入参数、cuFFT版本和GPU型号。
cuFFT批量处理计划要求输入数据包含所有批次的有效信号。批量模式下的性能优化可以合并来自不同批次的信号进行处理。cuFFT中使用的优化可能因版本而异。
适用范围 |
建议 |
备注 |
|---|---|---|
全部 |
使用单精度变换。 |
单精度变换相比双精度变换每次计算需要更少的带宽。 |
全部 |
将所有维度的尺寸限制为可表示为\(2^{a} \times 3^{b} \times 5^{c} \times 7^{d}\)的形式。 |
cuFFT库针对维度包含这些质因数的变换进行了高度优化的内核实现。通常最佳性能出现在使用2的幂次方时,其次是3的幂次方,然后是5和7。 |
全部 |
限制每个维度的尺寸以减少不同质因数的使用。 |
尺寸为\(2^{n}\)或\(3^{n}\)的变换通常比尺寸为\(2^{i} \times 3^{j}\)的变换更快,即使后者稍小,这是由于专用路径的组合所致。 |
全部 |
在执行单个变换时限制数据在内存中的连续性。在执行多个变换时使各个数据集保持连续 |
cuFFT库已针对此数据布局进行了优化。 |
全部 |
执行多个(即批量)转换。 |
在批量模式下会执行额外的优化。 |
实数到复数变换或复数到实数变换 |
确保x维度的问题大小是4的倍数。 |
该方案使用更高效的内核来实现共轭对称性。 |
实数到复数的变换或复数到实数的变换 |
使用 |
该方案使用的内核比 |
多GPU转换 |
在GPU之间使用PCI Express 3.0连接,并确保GPU位于同一交换机上。 |
GPU之间的互连速度越快,性能提升越明显。 |
2.14. 调用者分配工作区支持
cuFFT计划可能会使用额外的内存来存储中间结果。cuFFT库提供了多个函数来管理这种临时内存使用行为:
cufftSetAutoAllocationcufftEstimate1d,cufftEstimate2d,cufftEstimate3d和cufftEstimateManycufftGetSizecufftXtSetWorkAreaPolicy
前两个函数负责管理临时内存的分配和所有权。默认情况下,cuFFT总是在GPU内存中分配自己的工作区域。每个cuFFT句柄会单独分配数据。如果要顺序启动多个cuFFT计划,可以将同一内存块作为工作区域分配给所有这些计划,从而减少内存开销。
分配为工作区的内存需要GPU可见。除了通过cudaMalloc获取的常规内存外,使用CUDA统一虚拟寻址技术还允许cuFFT将以下类型内存作为工作区内存:固定主机内存、托管内存、以及执行计算GPU之外的其他GPU上的内存。虽然这提供了灵活性,但会带来性能损失,其程度取决于可用内存带宽。
cufftEstimateNd、cufftEstimateMany和cufftGetSize函数提供了用户需要分配工作空间缓冲区时所需内存大小的相关信息。
cufftXtSetWorkAreaPolicy function. This function allows fine tuning of work area memory usage.CUFFT_WORKAREA_MINIMAL policy, which instructs cuFFT to re-plan the existing plan without the need to use work area memory.从 cuFFT 9.2 开始,支持允许使用 CUFFT_WORKAREA_MINIMAL 策略的 FFT 变换如下:
支持类型为
C2C的变换,任何维度的大小可达4096。支持类型为
Z2Z的变换,在任何维度上大小可达2048。仅支持单GPU转换。
根据FFT变换的大小,当设置CUFFT_WORKAREA_MINIMAL策略时,可能会使用不同的FFT算法。
2.15. cuFFT 链接时优化内核
从CUDA 12.4开始,cuFFT提供了链接时优化(LTO)内核。这些内核在运行时作为cuFFT规划例程的一部分进行链接和最终确定。这使得cuFFT库能够生成针对底层架构和待解决特定问题优化的内核。
当前LTO内核覆盖范围包括:
支持64位寻址的内核(可处理跨越超过2^(32)-1个元素的FFT运算)。
部分单精度和双精度的R2C和C2R尺寸。
随着cuFFT未来版本的发布,LTO内核的数量和覆盖范围将会增加。我们鼓励用户测试LTO内核是否能提升其应用场景的性能。
用户可以通过使用cufftSetPlanProperty例程设置NVFFT_PLAN_PROPERTY_INT64_PATIENT_JIT计划属性来选择启用LTO内核。
为了完成LTO内核的最终优化,cuFFT依赖于CUDA工具包中提供的nvJitLink库。在运行时完成内核优化可能会导致规划时间增加(根据cuFFT方案和主机系统的硬件特性,延迟可能达到数百毫秒),但换来的将是优化后内核更快的执行速度。请注意,nvJitLink会缓存运行时链接的内核,以加速重复规划流程中后续的内核优化过程。
如果由于任何原因内核的运行时链接失败,cuFFT将回退到离线编译的内核来计算FFT。
注意
针对特定工具包版本的cuFFT LTO内核需要使用相同或更高版本但主版本号相同的nvJitLink库。例如,12.4版本的cuFFT要求nvJitLink必须来自CUDA Toolkit 12.X系列,其中X >= 4。
nvJitLink库是动态加载的,应该存在于系统的动态链接路径中(例如Unix系统中的LD_LIBRARY_PATH,或Windows系统中的PATH)。
3. cuFFT API 参考文档
本章通过描述输入/输出参数、数据类型和错误代码,详细说明了cuFFT库函数的行为。cuFFT库在首次调用API函数时初始化,当所有用户创建的FFT计划被销毁后,cuFFT会自动关闭。
3.1. 返回值 cufftResult
除CUFFT_SUCCESS外,所有cuFFT库返回值均表示当前API调用失败,用户应重新配置以纠正问题。可能的返回值定义如下:
typedef enum cufftResult_t {
CUFFT_SUCCESS = 0, // The cuFFT operation was successful
CUFFT_INVALID_PLAN = 1, // cuFFT was passed an invalid plan handle
CUFFT_ALLOC_FAILED = 2, // cuFFT failed to allocate GPU or CPU memory
CUFFT_INVALID_TYPE = 3, // No longer used
CUFFT_INVALID_VALUE = 4, // User specified an invalid pointer or parameter
CUFFT_INTERNAL_ERROR = 5, // Driver or internal cuFFT library error
CUFFT_EXEC_FAILED = 6, // Failed to execute an FFT on the GPU
CUFFT_SETUP_FAILED = 7, // The cuFFT library failed to initialize
CUFFT_INVALID_SIZE = 8, // User specified an invalid transform size
CUFFT_UNALIGNED_DATA = 9, // No longer used
CUFFT_INCOMPLETE_PARAMETER_LIST = 10, // Missing parameters in call
CUFFT_INVALID_DEVICE = 11, // Execution of a plan was on different GPU than plan creation
CUFFT_PARSE_ERROR = 12, // Internal plan database error
CUFFT_NO_WORKSPACE = 13 // No workspace has been provided prior to plan execution
CUFFT_NOT_IMPLEMENTED = 14, // Function does not implement functionality for parameters given.
CUFFT_LICENSE_ERROR = 15, // Used in previous versions.
CUFFT_NOT_SUPPORTED = 16 // Operation is not supported for parameters given.
} cufftResult;
建议用户检查cuFFT函数的返回值以确认是否存在错误,如cuFFT代码示例所示。
3.2. cuFFT 基础方案
这些API例程负责初始化cufftHandle。传递给规划函数的任何已初始化句柄属性将被忽略。
3.2.1. cufftPlan1d()
-
cufftResult cufftPlan1d(cufftHandle *plan, int nx, cufftType type, int batch);
-
为指定的信号大小和数据类型创建一维FFT计划配置。
batch输入参数告诉cuFFT需要配置多少个一维变换。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。- Parameters
-
plan[In] – 指向一个未初始化的
cufftHandle对象的指针。nx[In] – 变换尺寸(例如256表示256点FFT)。
type[In] – 变换数据类型(例如,
CUFFT_C2C表示单精度复数到复数转换)。batch[In] – 大小为
nx的变换数量。对于多重变换,请考虑使用cufftPlanMany。plan[Out] – 包含一个cuFFT一维计划句柄值。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效句柄。当计划被锁定时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx或batch参数不是支持的大小。
3.2.2. cufftPlan2d()
-
cufftResult cufftPlan2d(cufftHandle *plan, int nx, int ny, cufftType type);
-
根据指定的信号大小和数据类型创建2D FFT计划配置。
对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。- Parameters
-
plan[In] – 指向一个未初始化的
cufftHandle对象的指针。nx[In] – 变换在x维度上的大小。这是变换中变化最慢的维度(在内存中是跨步存储的)。
ny[In] – y维度的变换大小。这是变换中变化最快的维度(内存中是连续的)。
type[In] – 变换数据类型(例如,
CUFFT_C2R表示单精度复数转实数)。plan[Out] – 包含一个cuFFT 2D计划句柄值。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。当计划被锁定时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx或ny参数中的任意一个或两个都不是支持的大小。
3.2.3. cufftPlan3d()
-
cufftResult cufftPlan3d(cufftHandle *plan, int nx, int ny, int nz, cufftType type);
-
根据指定的信号大小和数据类型创建3D FFT计划配置。此函数与
cufftPlan2d()相同,只是它多接收第三个大小参数nz。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。- Parameters
-
plan[In] – 指向一个未初始化的
cufftHandle对象的指针。nx[In] – 变换在x维度上的大小。这是变换中变化最慢的维度(在内存中是跨步存储的)。
ny[In] – y维度上的变换尺寸。
nz[In] – 变换在z维度上的大小。这是变换中变化最快的维度(内存中是连续的)。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数转复数)。plan[Out] – 包含一个cuFFT 3D计划句柄值。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效句柄。当计划被锁定时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个
nx、ny或nz参数不是受支持的大小。
3.2.4. cufftPlanMany()
-
cufftResult cufftPlanMany(cufftHandle *plan, int rank, int *n, int *inembed, int istride, int idist, int *onembed, int ostride, int odist, cufftType type, int batch);
-
创建一个维度为
rank的FFT计划配置,数组n中指定了各维度大小。batch输入参数告诉cuFFT需要配置多少个变换。通过此函数,可以创建1维、2维或3维的批量计划。cufftPlanMany()API 通过高级数据布局参数支持更复杂的输入和输出数据布局,这些参数包括:inembed、istride、idist、onembed、ostride和odist。如果
inembed和onembed设置为NULL,则忽略所有其他步幅信息,并使用默认步幅。默认情况下假定数据数组是连续的。所有数组都假定位于CPU内存中。
请注意,当
inembed和onembed为NULL时,cufftPlanMany函数的行为与FFTW库中的对应函数fftw_plan_many_dft有所不同。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。- Parameters
-
plan[In] – 指向一个未初始化的
cufftHandle对象的指针。rank[In] – 变换的维度(1、2 或 3)。
n[In] – 大小为
rank的数组,描述每个维度的大小,其中n[0]表示变换的最外层维度大小,n[rank-1]表示最内层(连续)维度大小。inembed[In] – 指向大小为
rank的指针,用于指示输入数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离。
idist[输入] – 表示输入数据批次中两个连续信号第一个元素之间的距离。
onembed[In] – 指向大小为
rank的指针,用于指示输出数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。ostride[In] – 表示输出数组中最不显著(即最内层)维度上两个连续输出元素之间的距离。
odist[In] – 表示输出数据批次中两个连续信号第一个元素之间的距离。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数)。batch[In] – 此转换的批量大小。
plan[Out] – 包含一个cuFFT计划句柄。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。当计划被锁定时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.3. cuFFT可扩展计划
这些API例程将句柄创建与计划生成分开。这样可以在实际生成计划之前更改计划设置,这些设置可能会影响计划生成阶段的结果。
3.3.1. cufftCreate()
-
cufftResult cufftCreate(cufftHandle *plan)
-
仅创建一个不透明的句柄,并在主机上分配小型数据结构。
cufftMakePlan*()调用实际执行计划生成。- Parameters
-
plan[In] – 指向一个
cufftHandle对象的指针。plan[Out] – 包含一个cuFFT计划句柄值。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_ALLOC_FAILED – 计划资源分配失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.3.2. cufftDestroy()
-
cufftResult cufftDestroy(cufftHandle plan)
-
释放与cuFFT计划关联的所有GPU资源并销毁内部计划数据结构。当不再需要某个计划时,应调用此函数以避免浪费GPU内存。 对于多GPU计划的情况,应先销毁后创建的计划,最后销毁先创建的计划。
- Parameters
-
plan[In] – 要销毁的计划的
cufftHandle对象。
- Return values
-
CUFFT_SUCCESS – cuFFT成功销毁了FFT计划。
CUFFT_INVALID_PLAN –
plan参数不是有效的句柄。
3.3.3. cufftMakePlan1d()
-
cufftResult cufftMakePlan1d(cufftHandle plan, int nx, cufftType type, int batch, size_t *workSize);
-
在调用
cufftCreate()之后,会为指定的信号大小和数据类型创建一维FFT计划配置。batch输入参数告诉cuFFT需要配置多少个一维变换。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。如果在本次调用之前已调用
cufftXtSetGPUs()并指定了多个GPU,那么workSize将包含多个大小值。有关多GPU配置的更多详情,请参阅相关章节。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。nx[In] – 变换尺寸(例如256点FFT对应256)。对于多GPU配置,该值必须为2的幂次方。
type[In] – 变换数据类型(例如,
CUFFT_C2C表示单精度复数到复数变换)。对于多GPU场景,这必须是一个复数到复数的变换。batch[In] – 大小为
nx的变换数量。对于多重变换,请考虑使用cufftMakePlanMany。*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区域大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。当计划被锁定或不满足多GPU限制时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED` – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx或batch参数不是支持的大小。
3.3.4. cufftMakePlan2d()
-
cufftResult cufftMakePlan2d(cufftHandle plan, int nx, int ny, cufftType type, size_t *workSize);
-
在调用
cufftCreate()后,会根据指定的信号大小和数据类型创建2D FFT计划配置。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。如果在本次调用之前已调用
cufftXtSetGPUs()并设置了多个GPU,那么workSize将包含多个大小值。有关多GPU配置的更多细节,请参阅相关章节。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。nx[In] – 变换在x维度上的大小。这是变换中变化最慢的维度(在内存中是跨步存储的)。对于多GPU系统,该值必须能分解为小于或等于127的质数。
ny[In] – 变换在y维度上的大小。这是变换中变化最快的维度(内存中是连续的)。对于2个GPU,该值必须能分解为小于等于127的质数。
type[In] – 变换数据类型(例如,
CUFFT_C2R表示单精度复数转实数)。workSize[输入] – 指向工作区域大小(以字节为单位)的指针。例如,对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区域大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx或ny参数中的任意一个或两个都不是支持的大小。
3.3.5. cufftMakePlan3d()
-
cufftResult cufftMakePlan3d(cufftHandle plan, int nx, int ny, int nz, cufftType type, size_t *workSize);
-
在调用
cufftCreate()之后,根据指定的信号大小和数据类型创建3D FFT计划配置。此函数与cufftPlan2d()相同,只是它多接收第三个大小参数nz。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。如果在本次调用之前已调用
cufftXtSetGPUs()并指定了多个GPU,那么workSize将包含多个大小值。有关多GPU配置的更多详情,请参阅相关章节。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。nx[In] – 变换在x维度上的大小。这是变换中变化最慢的维度(在内存中是跨步存储的)。对于多GPU配置,该值必须能分解为小于或等于127的质数。
ny[In] – y 维度上的变换尺寸。对于多GPU配置,该值必须可分解为小于或等于127的质数。
nz[In] – z 维度的变换尺寸。这是变换中变化最快的维度(内存中是连续的)。对于多GPU配置,该值必须可分解为小于或等于127的质数。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数)。workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN –
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个
nx、ny或nz参数不是受支持的大小。
3.3.6. cufftMakePlanMany()
-
cufftResult cufftMakePlanMany(cufftHandle plan, int rank, int *n, int *inembed, int istride, int idist, int *onembed, int ostride, int odist, cufftType type, int batch, size_t *workSize);
-
调用
cufftCreate()后,会创建一个维度为rank的FFT计划配置,其大小由数组n指定。batch输入参数告诉cuFFT需要配置多少个变换。通过此函数,可以创建1维、2维或3维的批量计划。cufftPlanMany()API 通过高级数据布局参数支持更复杂的输入和输出数据布局,这些参数包括:inembed、istride、idist、onembed、ostride和odist。如果
inembed和onembed设置为NULL,则忽略所有其他步幅信息,并使用默认步幅。默认情况下假定数据数组是连续的。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。如果在本次调用之前已调用
cufftXtSetGPUs()并设置了多个GPU,那么workSize将包含多个大小值。有关多GPU的更多详情,请参阅相关章节。所有数组都假定位于CPU内存中。
- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。rank[输入] – 变换的维度(1、2 或 3)
n[In] – 大小为
rank的数组,描述每个维度的大小,其中n[0]表示最外层维度的大小,n[rank-1]表示最内层(连续)维度的大小。对于多GPU且秩为1的情况,大小必须是2的幂。对于多GPU且秩为2或3的情况,大小必须能分解为小于或等于127的质数。inembed[In] – 指向大小为
rank的指针,表示输入数据在内存中的存储维度,其中inembed[0]是最外层维度的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离
idist[输入] - 表示输入数据批次中两个连续信号第一个元素之间的距离
onembed[In] – 指向大小为
rank的指针,用于指示输出数据在内存中的存储维度,其中onembed[0]表示最外层维度的存储大小。如果设置为NULL,则所有其他高级数据布局参数将被忽略。ostride[In] – 表示输出数组中最不重要(即最内层)维度上两个连续输出元素之间的距离
odist[输入] – 表示输出数据批次中两个连续信号第一个元素之间的距离
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数转换)。对于2个GPU的情况,这必须是一个复数到复数的变换。batch[In] – 此转换的批量大小。
*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区域大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。当计划被锁定或不满足多GPU限制时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.3.7. cufftMakePlanMany64()
-
cufftResult cufftMakePlanMany64(cufftHandle plan, int rank, long long int *n, long long int *inembed, long long int istride, long long int idist, long long int *onembed, long long int ostride, long long int odist, cufftType type, long long int batch, size_t *workSize);
-
调用
cufftCreate()后,会创建一个维度为rank的FFT计划配置,其大小由数组n指定。batch输入参数告诉cuFFT需要配置多少个变换。通过此函数,可以创建1维、2维或3维的批量计划。此API与
cufftMakePlanMany完全相同,不同之处在于指定大小和步长的参数是64位整数。该API使得执行非常大的变换成为可能。cuFFT包含使用32位索引的内核和使用64位索引的内核。cuFFT规划会尽可能选择32位内核,以避免因64位运算而产生的任何开销。该接口支持所有尺寸和类型的变换,但有两点例外。对于尺寸超过4G元素的变换,数组
n中指定的维度必须能分解为小于等于127的质因数。对于尺寸超过4G元素的实数到复数及复数到实数变换,变化最快的维度必须是偶数。cufftPlanMany64()API 通过高级数据布局参数支持更复杂的输入和输出数据布局,这些参数包括:inembed、istride、idist、onembed、ostride和odist。如果
inembed和onembed设置为NULL,则忽略所有其他步幅信息,并使用默认步幅。默认情况下假定数据数组是连续的。对于给定的句柄,此调用只能使用一次。如果计划被锁定(即该句柄之前已用于不同的
cufftPlan或cufftMakePlan调用),它将失败并返回CUFFT_INVALID_PLAN。如果在本次调用之前已调用
cufftXtSetGPUs()并指定了多个GPU,那么workSize将包含多个大小值。有关多GPU配置的更多详情,请参阅相关章节。所有数组都假定位于CPU内存中。
- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。rank[In] – 变换的维度(1、2 或 3)。
n[In] – 大小为
rank的数组,描述每个维度的尺寸。对于多GPU且秩为1的情况,尺寸必须是2的幂次方。对于多GPU且秩为2或3的情况,尺寸必须能分解为小于等于127的质数。inembed[In] – 指向大小为
rank的指针,用于指示输入数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离。
idist[输入] – 表示输入数据批次中两个连续信号第一个元素之间的距离。
onembed[In] – 指向大小为
rank的指针,用于指示输出数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。ostride[In] – 表示输出数组中最不显著(即最内层)维度上两个连续输出元素之间的距离。
odist[In] – 表示输出数据批次中两个连续信号第一个元素之间的距离。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数转换)。对于2个GPU的情况,这必须是一个复数到复数的变换。batch[In] – 此转换的批量大小。
*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区域大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。当计划被锁定或不满足多GPU限制时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.3.8. cufftXtMakePlanMany()
-
cufftResult cufftXtMakePlanMany(cufftHandle plan, int rank, long long int *n, long long int *inembed, long long int istride, long long int idist, cudaDataType inputtype, long long int *onembed, long long int ostride, long long int odist, cudaDataType outputtype, long long int batch, size_t *workSize, cudaDataType executiontype);
-
调用
cufftCreate()后,会创建一个维度为rank的FFT计划配置,其大小由数组n指定。batch输入参数告诉cuFFT需要配置多少个变换。通过此函数,可以创建1维、2维或3维的批量计划。类型说明符
inputtype、outputtype和executiontype决定了要执行的变换类型和精度。并非所有参数组合都被支持。目前这三个参数需要保持精度一致。参数inputtype和outputtype需要匹配变换类型:复数到复数、实数到复数或复数到实数。参数executiontype需要匹配精度且必须是复数类型。例如:对于半精度实数到复数变换,参数inputtype、outputtype和executiontype的值应分别为CUDA_R_16F、CUDA_C_16F和CUDA_C_16F。类似地,bfloat16复数到实数变换会使用CUDA_C_16BF作为inputtype和executiontype,而CUDA_R_16BF作为outputtype。cufftXtMakePlanMany()API 通过高级数据布局参数支持更复杂的输入和输出数据布局,这些参数包括:inembed、istride、idist、onembed、ostride和odist。如果
inembed和onembed设置为NULL,则忽略所有其他步幅信息,并使用默认步幅。默认情况下假定数据数组是连续的。如果在本次调用之前已调用
cufftXtSetGPUs()并设置了多个GPU,那么workSize将包含多个大小值。有关多GPU配置的更多细节,请参阅相关章节。所有数组都假定位于CPU内存中。
- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。rank[In] – 变换的维度(1、2 或 3)。
n[In] – 大小为
rank的数组,描述每个维度的大小,其中n[0]表示最外层维度的大小,n[rank-1]表示最内层(连续)维度的大小。对于多GPU且秩为1的情况,大小必须是2的幂次。对于多GPU且秩为2或3的情况,大小必须能分解为小于等于127的质数。inembed[In] – 指向大小为
rank的指针,表示输入数据在内存中的存储维度,其中inembed[0]是最外层维度的存储大小。如果设为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离。
idist[输入] – 表示输入数据批次中两个连续信号第一个元素之间的距离。
inputtype[In] – 输入数据的类型。
onembed[In] – 一个大小为
rank的指针,用于指示输出数据在内存中的存储维度,其中onembed[0]表示最外层维度的存储尺寸。如果设置为NULL,则所有其他高级数据布局参数都将被忽略。ostride[In] – 表示输出数组中最不显著(即最内层)维度上两个连续输出元素之间的距离。
odist[In] – 表示输出数据批次中两个连续信号第一个元素之间的距离。
outputtype[In] – 输出数据的类型。
batch[In] – 此转换的批量大小。
*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
executiontype[In] – 用于计算的数据类型。
*workSize[输出] – 指向工作区域大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功创建了FFT计划。
CUFFT_INVALID_PLAN –
plan参数不是有效的句柄。当不满足多GPU限制时,句柄无效。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.4. cuFFT 计划属性
用户可以通过计划属性进一步自定义cuFFT计划。这些属性可以根据需要针对每个计划进行设置、查询和重置,使用本节列出的例程。
当前支持的属性如下:
属性 |
基础类型 |
描述 |
行为 |
|---|---|---|---|
|
长整型 |
|
|
3.4.1. cufftSetPlanPropertyInt64()
-
cufftResult cufftSetPlanPropertyInt64(cufftHandle plan, cufftProperty property, const long long int propertyValueInt64);
-
将cuFFT计划与由键
property标识的属性关联。该属性的值由propertyValueInt64给出,它是一个有符号的长整型整数。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。property[In] – 属性标识符,类型为
cufftPlanProperty。propertyValueInt64[In] – 要为属性设置的值,一个长整型有符号整数。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功设置了该属性。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_NOT_SUPPORTED - 该属性不被支持,或者当前无法设置(例如某些属性在调用计划例程后就不能再设置,参见cuFFT计划属性)。
CUFFT_INVALID_VALUE - 用于设置属性的无效属性或值
3.4.2. cufftGetPlanPropertyInt64()
-
cufftResult cufftGetPlanPropertyInt64(cufftHandle plan, cufftProperty property, long long int *propertyValueInt64);
-
获取与cuFFT计划
plan关联的、由键property标识的属性值。该属性值为有符号长整型,将被设置到propertyValueInt64所指向的地址空间中。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。property[In] – 属性标识符,类型为
cufftPlanProperty。propertyValueInt64[In] – 指向要设置为属性值的数值的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功获取了属性值。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_NOT_SUPPORTED - 不支持该属性。
CUFFT_INVALID_VALUE – 无效的属性,或指针
propertyValueInt64为空
3.4.3. cufftResetPlanProperty()
-
cufftResult cufftResetPlanProperty(cufftHandle plan, cufftProperty property);
-
将关联到cuFFT计划
plan的、由键property标识的属性值重置为其默认值。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。property[In] – 属性标识符,类型为
cufftPlanProperty。
- Return values
-
CUFFT_SUCCESS – cuFFT成功重置了属性值。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_NOT_SUPPORTED - 该属性不被
plan支持,或当前无法重置(参见cuFFT Plan Properties中的Behavior列)。CUFFT_INVALID_VALUE – 无效属性
3.5. cuFFT 工作区预估大小
在执行计划过程中,cuFFT需要一个工作区域来临时存储中间结果。cufftEstimate*()调用会根据指定参数返回所需工作区域大小的估计值,该估计基于默认计划设置。不同问题规模对存储空间的需求差异很大,特别是2的幂次方在临时存储方面效率极高。然而,大质数会使用不同算法,其所需存储空间可能达到类似规模2的幂次方的八倍。这些例程返回的workSize估计值可能仍小于实际需求值,特别是当n值不是2、3、5或7的幂次方倍数时。cufftGetSize*()例程会提供更精确的数值,但这些值可能仍偏保守。
3.5.1. cufftEstimate1d()
-
cufftResult cufftEstimate1d(int nx, cufftType type, int batch, size_t *workSize);
-
在执行计划过程中,cuFFT需要一个工作区来临时存储中间结果。此调用根据指定参数并假设采用默认计划设置,返回所需工作区大小的估计值。
- Parameters
-
nx[In] – 变换尺寸(例如256表示256点FFT)。
type[In] – 变换数据类型(例如,
CUFFT_C2C表示单精度复数到复数转换)。batch[In] – 大小为
nx的变换数量。对于多重变换,建议使用cufftEstimateMany。*workSize[输入] – 指向工作空间大小的指针,单位为字节。
*workSize[输出] – 指向工作空间大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx参数不是支持的大小。
3.5.2. cufftEstimate2d()
-
cufftResult cufftEstimate2d(int nx, int ny, cufftType type, size_t *workSize);
-
在执行计划过程中,cuFFT需要一个工作区来临时存储中间结果。此调用根据指定的参数并假设采用默认计划设置,返回所需工作区大小的估计值。
- Parameters
-
nx[In] – x维度的变换大小(行数)。
ny[In] – y维度上的变换尺寸(列数)。
type[In] – 变换数据类型(例如,
CUFFT_C2R表示单精度复数转实数)。*workSize[输入] - 指向工作空间大小的指针,以字节为单位。
*workSize[输出] – 指向工作空间大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx或ny参数中的任意一个或两个都不是支持的大小。
3.5.3. cufftEstimate3d()
-
cufftResult cufftEstimate3d(int nx, int ny, int nz, cufftType type, size_t *workSize);
-
在执行计划过程中,cuFFT需要一个工作区来临时存储中间结果。此调用会根据指定的参数并假设采用默认计划设置,返回所需工作区大小的预估。
- Parameters
-
nx[In] – x维度上的变换尺寸。
ny[In] – y维度上的变换尺寸。
nz[In] – z维度上的变换大小。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数)。*workSize[输入] – 指向工作空间大小的指针,以字节为单位。
*workSize[输出] – 指向工作空间大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个
nx、ny或nz参数不是受支持的大小。
3.5.4. cufftEstimateMany()
-
cufftResult cufftEstimateMany(int rank, int *n, int *inembed, int istride, int idist, int *onembed, int ostride, int odist, cufftType type, int batch, size_t *workSize);
-
在执行计划过程中,cuFFT需要一个工作区来临时存储中间结果。此调用根据指定参数并假设采用默认计划设置,返回所需工作区大小的估计值。
cufftEstimateMany()API 通过高级数据布局参数支持更复杂的输入和输出数据布局,这些参数包括:inembed、istride、idist、onembed、ostride和odist。所有数组都假定位于CPU内存中。
- Parameters
-
rank[In] – 变换的维度(1、2 或 3)。
n[In] – 大小为
rank的数组,描述每个维度的尺寸。inembed[In] – 指向大小为
rank的指针,用于指示输入数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离。
idist[输入] – 表示输入数据批次中两个连续信号第一个元素之间的距离。
onembed[In] – 指向大小为
rank的指针,用于指示输出数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。ostride[In] – 表示输出数组中最不显著(即最内层)维度上两个连续输出元素之间的距离。
odist[In] – 表示输出数据批次中两个连续信号第一个元素之间的距离。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数)。batch[In] – 此转换的批量大小。
*workSize[输入] – 指向工作空间大小的指针,以字节为单位。
*workSize[输出] – 指向工作空间大小的指针
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.6. cuFFT 工作区精细估算大小
cufftGetSize*() 例程比 cufftEstimate*() 例程能更准确地估算计划所需的工作区大小,因为它们会考虑所有可能已设置的计划参数。如cuFFT工作区预估大小章节所述,返回的workSize值可能偏保守,特别是当n值不是2、3、5或7的幂次倍数时。
3.6.1. cufftGetSize1d()
-
cufftResult cufftGetSize1d(cufftHandle plan, int nx, cufftType type, int batch, size_t *workSize);
-
相较于
cufftEstimate1d(),此调用能根据指定参数并考虑可能已设置的任何计划配置,提供更准确的工作区域大小预估。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。nx[In] – 变换尺寸(例如256表示256点FFT)。
type[In] – 变换数据类型(例如,
CUFFT_C2C表示单精度复数到复数转换)。batch[In] – 大小为
nx的变换数量。对于多重变换,请考虑使用cufftGetSizeMany。*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作空间大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx参数不是支持的大小。
3.6.2. cufftGetSize2d()
-
cufftResult cufftGetSize2d(cufftHandle plan, int nx, int ny, cufftType type, size_t *workSize);
-
相较于
cufftEstimate2d(),此调用能基于指定参数并考虑可能已设置的任何计划配置,更精确地估算计划所需的工作区大小。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。nx[In] – x维度的变换大小(行数)。
ny[In] – y维度上的变换尺寸(列数)。
type[In] – 变换数据类型(例如,
CUFFT_C2R表示单精度复数转实数)。*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作空间大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE –
nx或ny参数中的任意一个或两个都不是支持的大小。
3.6.3. cufftGetSize3d()
-
cufftResult cufftGetSize3d(cufftHandle plan, int nx, int ny, int nz, cufftType type, size_t *workSize);
-
相较于
cufftEstimate3d(),此调用能根据指定参数并考虑可能已设置的任何计划配置,更精确地估算计划所需的工作区大小。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。nx[In] – x维度上的变换尺寸。
ny[In] – y维度上的变换尺寸。
nz[In] – z维度上的变换尺寸。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数)。*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作空间大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个
nx、ny或nz参数不是受支持的大小。
3.6.4. cufftGetSizeMany()
-
cufftResult cufftGetSizeMany(cufftHandle plan, int rank, int *n, int *inembed, int istride, int idist, int *onembed, int ostride, int odist, cufftType type, int batch, size_t *workSize);
-
相较于
cufftEstimateSizeMany(),此调用能根据指定参数并考虑可能已设置的任何计划配置,提供更准确的工作区域大小预估。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。rank[In] – 变换的维度(1、2 或 3)。
n[In] – 大小为
rank的数组,描述每个维度的尺寸。inembed[In] – 指向大小为
rank的指针,用于指示输入数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离。
idist[输入] – 表示输入数据批次中两个连续信号第一个元素之间的距离。
onembed[In] – 指向大小为
rank的指针,用于指示输出数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。ostride[In] – 表示输出数组中最不显著(即最内层)维度上两个连续输出元素之间的距离。
odist[In] – 表示输出数据批次中两个连续信号第一个元素之间的距离。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数)。batch[In] – 此转换的批量大小。
*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.6.5. cufftGetSizeMany64()
-
cufftResult cufftGetSizeMany64(cufftHandle plan, int rank, long long int *n, long long int *inembed, long long int istride, long long int idist, long long int *onembed, long long int ostride, long long int odist, cufftType type, long long int batch, size_t *workSize);
-
相较于
cufftEstimateSizeMany(),此调用能基于指定参数并考虑可能已设置的任何计划配置,提供更精确的工作区大小预估。此API与
cufftMakePlanMany完全相同,不同之处在于指定大小和步长的参数是64位整数。该API使得执行非常大的变换成为可能。cuFFT包含使用32位索引的内核和使用64位索引的内核。cuFFT规划会尽可能选择32位内核,以避免因64位运算而产生的任何开销。该接口支持所有尺寸和类型的变换,但有两个例外情况。对于总大小超过4G元素的变换,数组
n中指定的维度必须能分解为小于或等于127的质数。对于总大小超过4G元素的实数到复数和复数到实数变换,变化最快的维度必须是偶数。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。rank[In] – 变换的维度(1、2 或 3)。
n[In] – 大小为
rank的数组,描述每个维度的尺寸。inembed[In] – 指向大小为
rank的指针,用于指示输入数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离。
idist[输入] – 表示输入数据批次中两个连续信号第一个元素之间的距离。
onembed[In] – 指向大小为
rank的指针,用于指示输出数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。ostride[In] – 表示输出数组中最不显著(即最内层)维度上两个连续输出元素之间的距离。
odist[In] – 表示输出数据批次中两个连续信号第一个元素之间的距离。
type[In] – 变换数据类型(例如,
CUFFT_R2C表示单精度实数到复数)。batch[In] – 此转换的批量大小。
*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.6.6. cufftXtGetSizeMany()
-
cufftResult cufftXtGetSizeMany(cufftHandle plan, int rank, long long int *n, long long int *inembed, long long int istride, long long int idist, cudaDataType inputtype, long long int *onembed, long long int ostride, long long int odist, cudaDataType outputtype, long long int batch, size_t *workSize, cudaDataType executiontype);
-
相较于
cufftEstimateSizeMany(),此调用能基于与cufftXtMakePlanMany函数签名匹配的指定参数,并考虑可能已设置的所有计划配置,从而更精确地估算计划所需的工作区大小。有关
inputtype、outputtype和executiontype参数有效组合的更多信息,请参阅cufftXtMakePlanMany函数的文档。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。rank[In] – 变换的维度(1、2 或 3)。
n[In] – 大小为
rank的数组,描述每个维度的尺寸。inembed[In] – 指向大小为
rank的指针,用于指示输入数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。istride[In] – 表示在最不显著(即最内层)维度上两个连续输入元素之间的距离。
idist[输入] – 表示输入数据批次中两个连续信号第一个元素之间的距离。
inputtype[In] (cudaDataType) – 输入数据的类型。
onembed[In] – 指向大小为
rank的指针,用于指示输出数据在内存中的存储维度。如果设置为NULL,则所有其他高级数据布局参数将被忽略。ostride[In] – 表示输出数组中最不显著(即最内层)维度上两个连续输出元素之间的距离。
odist[In] – 表示输出数据批次中两个连续信号第一个元素之间的距离。
outputtype[In] (cudaDataType) – 输出数据的类型。
batch[In] – 此转换的批量大小。
*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
executiontype[In] (cudaDataType) – 用于计算的数据类型。
*workSize[输出] – 指向工作区大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_SIZE – 一个或多个参数不是支持的大小。
3.7. cufftGetSize()
-
cufftResult cufftGetSize(cufftHandle plan, size_t *workSize);
-
一旦计划生成完成,无论是通过原始API还是可扩展API,此调用将返回支持该计划所需的工作区域实际大小。选择在应用程序内管理工作区域分配的调用者必须在计划生成后,以及任何可能在计划生成后更改所需工作空间大小的
cufftSet*()调用之后使用此调用。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。*workSize[输入] – 指向工作区大小(以字节为单位)的指针。例如对于两个GPU,worksize必须声明为包含两个元素。
*workSize[输出] – 指向工作区大小的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
3.8. cuFFT调用者分配工作区支持
3.8.1. cufftSetAutoAllocation()
-
cufftResult cufftSetAutoAllocation(cufftHandle plan, int autoAllocate);
-
cufftSetAutoAllocation()表示调用者打算为已生成的计划分配和管理工作区域。cuFFT的默认行为是在计划生成时分配工作区域。如果在调用cufftMakePlan*()之前已将cufftSetAutoAllocation()的autoAllocate参数设置为0("false"),则cuFFT不会分配工作区域。这是希望管理工作区域分配的调用者的首选操作顺序。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。autoAllocate[In] - 指示是否分配工作区域。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
3.8.2. cufftSetWorkArea()
-
cufftResult cufftSetWorkArea(cufftHandle plan, void *workArea);
-
cufftSetWorkArea()会覆盖与计划关联的工作区域指针。如果工作区域是自动分配的,cuFFT 将释放自动分配的空间。cufftExecute*()调用假定工作区域指针有效,并且指向设备内存中的连续区域,该区域不与任何其他工作区域重叠。如果不满足这些条件,结果将不确定。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。*workArea[输入] – 指向
workArea的指针。对于多GPU场景,必须提供多个工作区指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.8.3. cufftXtSetWorkAreaPolicy()
-
cufftResult cufftXtSetWorkAreaPolicy(cufftHandle plan, cufftXtWorkAreaPolicy policy, size_t *workSize);
-
cufftXtSetWorkAreaPolicy()表示调用者打算更改给定计划句柄的工作区大小。cuFFT的默认行为是在计划生成时分配工作区,其默认大小取决于计划类型和其他参数。如果调用cufftXtSetWorkAreaPolicy()时将policy参数设置为CUFFT_WORKAREA_MINIMAL,cuFFT将尝试重新规划该句柄以使用零字节的工作区内存。如果cufftXtSetWorkAreaPolicy()调用成功,则自动分配的工作区内存将被释放。目前不支持策略
CUFFT_WORKAREA_PERFORMANCE、CUFFT_WORKAREA_USER以及参数workSize,这些功能保留用于未来cuFFT版本。此函数在计划句柄的生命周期内只能调用一次。
- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。policy[In] – 要应用的工作区域策略类型。
*workSize[In] – 保留供未来使用。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_SIZE – FFT大小不符合所选策略的要求。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.9. cuFFT执行
3.9.1. cufftExecC2C() 和 cufftExecZ2Z()
-
cufftResult cufftExecC2C(cufftHandle plan, cufftComplex *idata, cufftComplex *odata, int direction);
-
cufftResult cufftExecZ2Z(cufftHandle plan, cufftDoubleComplex *idata, cufftDoubleComplex *odata, int direction);
-
cufftExecC2C()(cufftExecZ2Z()) 执行一个单精度(双精度)复数到复数的变换计划,变换方向由direction参数指定。cuFFT使用idata参数指向的GPU内存作为输入数据。该函数将傅里叶系数存储在odata数组中。如果idata和odata相同,则此方法执行原地变换。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。idata[In] – 指向待转换的复数输入数据(位于GPU内存中)的指针。
odata[In] – 指向复杂输出数据的指针(位于GPU内存中)。
direction[In] – 变换方向:
CUFFT_FORWARD或CUFFT_INVERSE.odata[Out] – 包含复数傅里叶系数。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
idata、odata或direction中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.9.2. cufftExecR2C() 和 cufftExecD2Z()
-
cufftResult cufftExecR2C(cufftHandle plan, cufftReal *idata, cufftComplex *odata);
-
cufftResult cufftExecD2Z(cufftHandle plan, cufftDoubleReal *idata, cufftDoubleComplex *odata);
-
cufftExecR2C()(cufftExecD2Z()) 执行单精度(双精度)实数到复数的隐式正向cuFFT变换计划。cuFFT使用idata参数指向的GPU内存作为输入数据。该函数将非冗余傅里叶系数存储在odata数组中。idata和odata指针在单精度变换中需要对齐到cufftComplex数据类型,在双精度变换中需要对齐到cufftDoubleComplex数据类型。如果idata和odata相同,则该方法执行原地变换。请注意如Parameter cufftType中所述的原地变换与非原地变换之间的数据布局差异。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。idata[In] – 指向待转换的实型输入数据(位于GPU内存中)的指针。
odata[In] – 指向复杂输出数据的指针(位于GPU内存中)。
odata[Out] – 包含复数傅里叶系数。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了工作空间的大小。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
idata和odata中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.9.3. cufftExecC2R() 和 cufftExecZ2D()
-
cufftResult cufftExecC2R(cufftHandle plan, cufftComplex *idata, cufftReal *odata);
-
cufftResult cufftExecZ2D(cufftHandle plan, cufftDoubleComplex *idata, cufftDoubleReal *odata);
-
cufftExecC2R()(cufftExecZ2D()) 执行单精度(双精度)复数到实数的隐式逆cuFFT变换计划。cuFFT使用idata参数指向的GPU内存作为输入数据。输入数组仅包含非冗余的复数傅里叶系数。此函数将实数输出值存储在odata数组中。两个指针在单精度变换中都需要对齐到cufftComplex数据类型,在双精度变换中则对齐到cufftDoubleComplex类型。如果idata和odata相同,此方法将执行原地变换。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。idata[In] – 指向待转换的复数输入数据(位于GPU内存中)的指针。
odata[In] – 指向实际输出数据的指针(位于GPU内存中)。
odata[Out] – 包含实际的输出数据。
- Return values
-
CUFFT_SUCCESS – cuFFT成功执行了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
idata和odata中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.9.4. cufftXtExec()
-
cufftResult cufftXtExec(cufftHandle plan, void *input, void *output, int direction);
-
函数
cufftXtExec可执行任何精度的cuFFT变换。对于复数到实数和实数到复数的变换,direction参数将被忽略。cuFFT使用input参数指向的GPU内存作为输入数据。该函数将傅里叶系数存储在output数组中。如果input和output相同,则此方法执行原地变换。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。input[In] – 指向待转换输入数据(位于GPU内存中)的指针。
output[In] – 指向输出数据的指针(位于GPU内存中)。
direction[In] – 变换方向:
CUFFT_FORWARD或CUFFT_INVERSE。对于复数到实数和实数到复数的变换将被忽略。output[Out] – 包含复数傅里叶系数。
- Return values
-
CUFFT_SUCCESS – cuFFT成功执行了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
idata、odata或direction中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.9.5. cufftXtExecDescriptor()
-
cufftResult cufftXtExecDescriptor(cufftHandle plan, cudaLibXtDesc *input, cudaLibXtDesc *output, int direction);
-
函数
cufftXtExecDescriptor()可执行任何精度的cuFFT变换。对于复数到实数和实数到复数的变换,direction参数将被忽略。cuFFT使用由描述符cudaLibXtDesc *input指向的GPU内存作为输入数据,并使用cudaLibXtDesc *output作为输出数据。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。input[In] – 指向待转换的复数输入数据(位于GPU内存中)的指针。
output[In] – 指向复杂输出数据的指针(位于GPU内存中)。
direction[In] – 变换方向:
CUFFT_FORWARD或CUFFT_INVERSE。对于复数到实数和实数到复数的变换将被忽略。idata[Out] – 包含复数傅里叶系数。
- Return values
-
CUFFT_SUCCESS – cuFFT成功执行了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
idata和direction中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_DEVICE - 描述符中指定了无效的GPU索引。
3.10. cuFFT与多GPU
3.10.1. cufftXtSetGPUs()
-
cufftResult cufftXtSetGPUs(cufftHandle plan, int nGPUs, int *whichGPUs);
-
cufftXtSetGPUs()用于指定计划中要使用的GPU。与单GPU情况类似,cufftCreate()创建计划,而cufftMakePlan*()执行计划生成。在10.4.0之前的cuFFT版本中,如果计划已关联非默认流,此调用将返回错误。请注意,调用
cufftXtSetGPUs()必须在调用cufftCreate()之后,且在调用cufftMakePlan*()之前进行。cufftXtSetGPUs()函数的参数whichGPUs决定了GPU在数据分解中的排序(第一个数据块将被放置在whichGPUs数组第一个元素所表示的GPU上)。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。nGPUs[In] – 要使用的GPU数量。
whichGPUs[In] – 指定要使用的GPU。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功设置了要使用的GPU。
CUFFT_INVALID_PLAN –
plan参数不是有效的句柄,或者在10.4.0之前的cuFFT版本中,该计划已与非默认流相关联。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_VALUE – 请求的GPU数量小于2或大于8。
CUFFT_INVALID_DEVICE – 指定的GPU索引无效。
CUFFT_INVALID_SIZE – 创建
plan时所使用的变换尺寸不符合最小尺寸标准。
3.10.2. cufftXtSetWorkArea()
-
cufftResult cufftXtSetWorkArea(cufftHandle plan, void **workArea);
-
cufftXtSetWorkArea()会覆盖与计划关联的工作区域。如果工作区域是自动分配的,cuFFT 将释放自动分配的空间。cufftXtExec*()调用假定工作区域有效,并且它指向每个设备内存中的连续区域,不与其他任何工作区域重叠。如果不是这种情况,结果将不确定。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。workArea[In] – 指向工作区指针的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功设置了要使用的GPU。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_DEVICE - 无法选择与计划关联的GPU。
3.10.3. cuFFT 多GPU执行
3.10.3.1. cufftXtExecDescriptorC2C() 和 cufftXtExecDescriptorZ2Z()
-
cufftResult cufftXtExecDescriptorC2C(cufftHandle plan, cudaLibXtDesc *input, cudaLibXtDesc *output, int direction);
-
cufftResult cufftXtExecDescriptorZ2Z(cufftHandle plan, cudaLibXtDesc *input, cudaLibXtDesc *output, int direction);
-
cufftXtExecDescriptorC2C()(cufftXtExecDescriptorZ2Z()) 执行一个单精度(双精度)复数到复数的变换计划,变换方向由direction参数指定。cuFFT使用cudaLibXtDesc *input指向的GPU内存作为输入数据。由于仅支持多GPU就地操作,该函数也将结果存储在cudaLibXtDesc *input数组中。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。*input[In] – 指向待转换的复数输入数据(位于GPU内存中)的指针。
*output[In] – 指向复杂输出数据的指针(位于GPU内存中)。
direction[In] – 变换方向:
CUFFT_FORWARD或CUFFT_INVERSE.input[Out] – 包含复数傅里叶系数。
- Return values
-
CUFFT_SUCCESS – cuFFT成功执行了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
input或direction中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_DEVICE – 描述符中指定了无效的GPU索引。
3.10.3.2. cufftXtExecDescriptorR2C() 和 cufftXtExecDescriptorD2Z()
-
cufftResult cufftXtExecDescriptorR2C(cufftHandle plan, cudaLibXtDesc *input, cudaLibXtDesc *output);
-
cufftResult cufftXtExecDescriptorD2Z(cufftHandle plan, cudaLibXtDesc *input, cudaLibXtDesc *output);
-
cufftXtExecDescriptorR2C()(cufftXtExecDescriptorD2Z()) 执行单精度(双精度)实数到复数变换计划。cuFFT使用cudaLibXtDesc *input指向的GPU内存作为输入数据。由于仅支持多GPU就地操作,该函数也将结果存储在cudaLibXtDesc *input数组中。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。*input[In] – 指向待转换的复数输入数据(位于GPU内存中)的指针。
*output[In] – 指向复杂输出数据的指针(位于GPU内存中)。
input[Out] – 包含复数傅里叶系数
- Return values
-
CUFFT_SUCCESS – cuFFT成功执行了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
input和direction中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_DEVICE – 描述符中指定了无效的GPU索引。
3.10.3.3. cufftXtExecDescriptorC2R() 和 cufftXtExecDescriptorZ2D()
-
cufftResult cufftXtExecDescriptorC2R(cufftHandle plan, cudaLibXtDesc *input, cudaLibXtDesc *output);
-
cufftResult cufftXtExecDescriptorZ2D(cufftHandle plan, cudaLibXtDesc *input, cudaLibXtDesc *output);
-
cufftXtExecDescriptorC2R()(cufftXtExecDescriptorZ2D()) 执行由direction参数指定的单精度(双精度)复数到实数变换方向的变换计划。cuFFT使用cudaLibXtDesc *input指向的GPU内存作为输入数据。由于仅支持多GPU就地操作,该函数也将结果存储在cudaLibXtDesc *input数组中。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。*input[In] – 指向待转换的复数输入数据(位于GPU内存中)的指针。
*output[In] – 指向复杂输出数据的指针(位于GPU内存中)。
input[Out] – 包含复数傅里叶系数。
- Return values
-
CUFFT_SUCCESS – cuFFT成功执行了FFT计划。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 参数
input和direction中至少有一个无效。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_EXEC_FAILED – cuFFT 在GPU上执行变换失败。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_DEVICE - 描述符中指定了无效的GPU索引。
3.10.4. 内存分配与数据移动函数
多GPU cuFFT执行函数在执行前假设输入数据已按特定布局复制到各GPU,执行后输出数据也分布在特定GPU上。以下函数协助完成数据的分配、设置和检索。这些函数必须在调用cufftMakePlan*()之后使用。
3.10.4.1. cufftXtMalloc()
-
cufftResult cufftXtMalloc(cufftHandle plan, cudaLibXtDesc **descriptor, cufftXtSubFormat format);
-
cufftXtMalloc()分配一个描述符,并为与计划关联的GPU中的数据分配所有内存,返回指向该描述符的指针。请注意,描述符包含一个设备指针数组,以便应用程序可以在GPU上对数据进行预处理或后处理。枚举参数cufftXtSubFormat_t指示缓冲区将用于输入还是输出。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。**descriptor[In] – 指向
cudaLibXtDesc对象指针的指针。format[In] – cufftXtSubFormat 值。
**descriptor[Out] – 指向
cudaLibXtDesc对象指针的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功允许用户分配描述符和GPU内存。
CUFFT_INVALID_PLAN –
plan参数不是有效句柄,或者不是多GPU的plan。CUFFT_ALLOC_FAILED – 为计划分配GPU资源失败。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_DEVICE - 描述符中指定了无效的GPU索引。
3.10.4.1.1. 参数 cufftXtSubFormat
cufftXtSubFormat_t 是一个枚举类型,用于指示缓冲区将用于输入还是输出以及数据的排列顺序。
typedef enum cufftXtSubFormat_t {
CUFFT_XT_FORMAT_INPUT, //by default input is in linear order across GPUs
CUFFT_XT_FORMAT_OUTPUT, //by default output is in scrambled order depending on transform
CUFFT_XT_FORMAT_INPLACE, //by default inplace is input order, which is linear across GPUs
CUFFT_XT_FORMAT_INPLACE_SHUFFLED, //shuffled output order after execution of the transform
CUFFT_FORMAT_UNDEFINED
} cufftXtSubFormat;
3.10.4.2. cufftXtFree()
-
cufftResult cufftXtFree(cudaLibXtDesc *descriptor);
-
cufftXtFree()释放描述符及其关联的所有内存。该描述符和内存必须是通过先前调用cufftXtMalloc()返回的。- Parameters
-
*descriptor[输入] – 指向一个
cudaLibXtDesc对象的指针。
- Return values
-
CUFFT_SUCCESS – cuFFT成功允许用户释放描述符及关联的GPU内存。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
3.10.4.3. cufftXtMemcpy()
-
cufftResult cufftXtMemcpy(cufftHandle plan, void *dstPointer, void *srcPointer, cufftXtCopyType type);
-
cufftXtMemcpy()在主机与GPU之间或GPU之间复制数据。枚举参数cufftXtCopyType_t指定传输的类型和方向。不支持对多GPU批量FFT计划使用CUFFT_COPY_DEVICE_TO_DEVICE传输类型调用cufftXtMemcpy函数。请注意,从CUDA 11.2(cuFFT 10.4.0)开始,
cufftSetStream()支持多GPU计划。当将流与计划关联时,cufftXtMemcpy()在多个GPU之间仍保持同步。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。dstPointer[输入] – 指向目标地址的指针。
srcPointer[输入] – 指向源地址的指针。
type[In] –
cufftXtCopyType值。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功允许用户在主机和GPU之间或GPU之间复制内存。
CUFFT_INVALID_PLAN -
plan参数不是有效的句柄。CUFFT_INVALID_VALUE – 向API传递了一个或多个无效参数。
CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
CUFFT_INVALID_DEVICE – 描述符中指定了无效的GPU索引。
3.10.4.3.1. 参数 cufftXtCopyType
cufftXtCopyType_t 是一个用于多GPU函数的枚举类型,用于指定cufftXtMemcpy()的拷贝类型。
CUFFT_COPY_HOST_TO_DEVICE 将数据从连续的主机缓冲区复制到多个设备缓冲区,采用cuFFT要求的输入数据布局。dstPointer 必须指向一个 cudaLibXtDesc 结构体,而 srcPointer 必须指向主机内存缓冲区。
CUFFT_COPY_DEVICE_TO_HOST 将数据从多个设备缓冲区复制到连续的主机缓冲区,布局方式与cuFFT输出数据的格式一致。dstPointer 必须指向主机内存缓冲区,而 srcPointer 必须指向 cudaLibXtDesc 结构体。
CUFFT_COPY_DEVICE_TO_DEVICE 将数据从多个设备缓冲区(按照cuFFT输出数据的布局)复制到多个设备缓冲区(按照cuFFT输入数据要求的布局)。dstPointer 和 srcPointer 必须指向不同的 cudaLibXtDesc 结构体(即不同的内存位置)。也就是说,这种复制不能是原地操作。请注意,目前不支持对2D和3D数据执行设备到设备的 cufftXtMemcpy() 操作。
typedef enum cufftXtCopyType_t {
CUFFT_COPY_HOST_TO_DEVICE,
CUFFT_COPY_DEVICE_TO_HOST,
CUFFT_COPY_DEVICE_TO_DEVICE
} cufftXtCopyType;
3.10.5. 通用多GPU描述符类型
3.10.5.1. cudaXtDesc
一种描述符类型,用于多个GPU例程中,包含有关GPU及其内存位置的信息。
struct cudaXtDesc_t{
int version; //descriptor version
int nGPUs; //number of GPUs
int GPUs[MAX_CUDA_DESCRIPTOR_GPUS]; //array of device IDs
void *data[MAX_CUDA_DESCRIPTOR_GPUS]; //array of pointers to data, one per GPU
size_t size[MAX_CUDA_DESCRIPTOR_GPUS]; //array of data sizes, one per GPU
void *cudaXtState; //opaque CUDA utility structure
};
typedef struct cudaXtDesc_t cudaXtDesc;
3.10.5.2. cudaLibXtDesc
一种描述符类型,用于多个GPU例程中,包含有关所用库的信息。
struct cudaLibXtDesc_t{
int version; //descriptor version
cudaXtDesc *descriptor; //multi-GPU memory descriptor
libFormat library; //which library recognizes the format
int subFormat; //library specific enumerator of sub formats
void *libDescriptor; //library specific descriptor e.g. FFT transform plan object
};
typedef struct cudaLibXtDesc_t cudaLibXtDesc;
3.11. cuFFT回调函数
3.11.1. cufftXtSetJITCallback()
-
cufftResult cufftXtSetJITCallback(cufftHandle plan, const char *callbackSymbolName, const void *callbackFatbin, size_t callbackFatbinSize, cufftXtCallbackType type, void **caller_info)
-
cufftXtSetJITCallback()指定了与计划一起使用的加载或存储LTO回调函数。此调用仅在调用
cufftCreate()之后有效,但在调用执行计划生成的cufftMakePlan*()之前有效。如果计划中已存在此类型的LTO回调函数,则新的回调例程将替换它。如果新回调需要共享内存,您必须调用
cufftXtSetCallbackSharedSize并指定回调函数所需的共享内存大小。如果更改了回调函数,cuFFT不会保留与先前回调关联的共享内存大小。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。callbackSymbolName[In] – 以null结尾的C字符串,包含(未修饰的)回调符号名称(即LTO回调例程的名称)。该符号名称将被运行时编译,不支持诸如
extern "C"或namespace等修饰符。callbackFatbin[输入] – 指向主机内存中回调设备函数所在位置的指针,该函数已通过nvcc或NVRTC编译为LTO-IR。
callbackFatbinSize[输入] – 由
callbackFatbin指向的数据的字节大小。type[In] – 回调例程的类型。
callerInfo[In] – 可选的设备指针数组,指向调用者特定信息,每个GPU对应一个。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功将回调函数与计划关联。
CUFFT_INVALID_PLAN –
plan参数无效(例如该句柄已被用于制定计划)。CUFFT_INVALID_TYPE – 回调类型无效。
CUFFT_INVALID_VALUE – 指向回调设备函数的指针无效或大小为
0。CUFFT_NOT_SUPPORTED - 该功能目前尚未支持(例如:带有LTO回调的多GPU场景)。
CUFFT_INTERNAL_ERROR – cuFFT遇到意外错误,可能发生在运行时链接过程中;错误代码将在未来版本中扩展。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.11.2. cufftXtSetCallback()
-
cufftResult cufftXtSetCallback(cufftHandle plan, void **callbackRoutine, cufftXtCallbackType type, void **callerInfo)
-
cufftXtSetCallback()指定了与计划配合使用的加载或存储传统回调函数。此调用仅在调用执行计划生成的cufftMakePlan*()之后有效。如果该计划已关联此类型的传统回调函数,则新回调例程将替换它。如果新回调需要共享内存,您必须调用cufftXtSetCallbackSharedSize并指定所需的共享内存量。cuFFT 不会保留与先前回调关联的共享内存量。- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。callbackRoutine[In] – 回调例程指针数组,每个GPU对应一个。
type[In] – 回调例程的类型。
callerInfo[In] – 可选的设备指针数组,指向调用者特定信息,每个GPU对应一个。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功将回调函数与计划关联。
CUFFT_INVALID_PLAN –
plan参数不是有效的句柄,或者在10.4.0之前的cuFFT版本中,该计划已与非默认流相关联。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
CUFFT_SETUP_FAILED – cuFFT库初始化失败。
3.11.3. cufftXtClearCallback()
-
cufftResult cufftXtClearCallback(cufftHandle plan, cufftXtCallbackType type)
-
cufftXtClearCallback()指示cuFFT在执行计划时停止调用指定的旧版回调类型。仅清除指定的回调。如果未设置过该类型的回调,则返回码为CUFFT_SUCCESS。请注意,此方法不适用于LTO回调。
- Parameters
-
plan[In] – 由
cufftCreate返回的cufftHandle。type[In] – 回调例程的类型。
- Return values
-
CUFFT_SUCCESS – cuFFT成功将回调函数与计划解除关联。
CUFFT_INVALID_PLAN –
plan参数不是有效的句柄,或者在10.4.0之前的cuFFT版本中,该计划已与非默认流相关联。CUFFT_INTERNAL_ERROR - 检测到内部驱动程序错误。
3.12. cufftSetStream()
-
cufftResult cufftSetStream(cufftHandle plan, cudaStream_t stream);
-
将CUDA流与cuFFT计划关联。计划执行期间启动的所有内核现在都通过关联的流完成,从而实现与其他流中活动(例如数据复制)的重叠。该关联将持续存在,直到计划被销毁或通过再次调用
cufftSetStream()更改流。请注意,从CUDA 11.2(cuFFT 10.4.0)开始,
cufftSetStream()支持多GPU计划。当将流与计划关联时,cufftXtMemcpy()在多个GPU之间仍保持同步。对于早期版本的cuFFT,cufftSetStream()在多GPU计划中会返回错误。请注意,从CUDA 12.2(cuFFT 11.0.8)开始,在多GPU方案中,
stream可以与任何GPU上的任何上下文关联。然而,重复调用cufftSetStream()并使用来自不同上下文的流会产生较小的时间开销。当重复调用cufftSetStream使用来自相同CUDA上下文的流时,可获得最佳性能。- Parameters
-
plan[In] – 要与流关联的
cufftHandle对象。stream[输入] – 一个通过
cudaStreamCreate()创建的有效CUDA流;0表示默认流。
- Return values
-
CUFFT_SUCCESS – 流已成功关联到该计划。
CUFFT_INVALID_PLAN -
plan参数不是有效句柄,或者在10.4.0之前的cuFFT版本中计划是多GPU的。
3.13. cufftGetVersion()
-
cufftResult cufftGetVersion(int *version);
-
返回cuFFT的版本号。
- Parameters
-
*version[In] – 指向版本号的指针。
*version[Out] – 包含版本号。
- Return values
-
CUFFT_SUCCESS – cuFFT 成功返回了版本号。
3.14. cufftGetProperty()
-
cufftResult cufftGetProperty(libraryPropertyType type, int *value);
-
返回动态链接的CUFFT库中由
type描述的属性数值到*value中。- Parameters
-
type[In] – CUDA库属性。
value[Out] – 包含所请求属性的整数值。
- Return values
-
CUFFT_SUCCESS – 属性值已成功返回。
CUFFT_INVALID_TYPE - 属性类型无法识别。
CUFFT_INVALID_VALUE –
value的值为NULL。
3.15. cuFFT 类型
3.15.1. 参数 cufftType
cuFFT库支持复数和实数数据变换。cufftType数据类型是一个枚举类型,列出了cuFFT支持的各种变换数据类型。
typedef enum cufftType_t {
CUFFT_R2C = 0x2a, // Real to complex (interleaved)
CUFFT_C2R = 0x2c, // Complex (interleaved) to real
CUFFT_C2C = 0x29, // Complex to complex (interleaved)
CUFFT_D2Z = 0x6a, // Double to double-complex (interleaved)
CUFFT_Z2D = 0x6c, // Double-complex (interleaved) to double
CUFFT_Z2Z = 0x69 // Double-complex to double-complex (interleaved)
} cufftType;
3.15.2. 转换方向的参数
cuFFT库根据复指数项的符号定义了正向和反向快速傅里叶变换。
#define CUFFT_FORWARD -1
#define CUFFT_INVERSE 1
cuFFT执行非归一化的快速傅里叶变换;也就是说,对输入数据集执行正向FFT,然后对结果集执行反向FFT,得到的数据等于输入数据乘以元素数量。用户可以根据需要自行决定是否对任一变换结果乘以数据集大小的倒数进行缩放。
3.15.3. 回调的类型定义
cuFFT库支持单精度或双精度、实数或复数数据、加载或存储的所有组合的回调函数。这些在参数cufftXtCallbackType中进行了枚举。
typedef enum cufftXtCallbackType_t {
CUFFT_CB_LD_COMPLEX = 0x0,
CUFFT_CB_LD_COMPLEX_DOUBLE = 0x1,
CUFFT_CB_LD_REAL = 0x2,
CUFFT_CB_LD_REAL_DOUBLE = 0x3,
CUFFT_CB_ST_COMPLEX = 0x4,
CUFFT_CB_ST_COMPLEX_DOUBLE = 0x5,
CUFFT_CB_ST_REAL = 0x6,
CUFFT_CB_ST_REAL_DOUBLE = 0x7,
CUFFT_CB_UNDEFINED = 0x8
} cufftXtCallbackType;
3.15.3.1. LTO回调的类型定义
LTO回调函数的原型和指针类型定义如下:
typedef cufftComplex (*cufftJITCallbackLoadC)(void *dataIn, unsigned long long offset, void *callerInfo, void *sharedPointer);
typedef cufftDoubleComplex (*cufftJITCallbackLoadZ)(void *dataIn, unsigned long long offset, void *callerInfo, void *sharedPointer);
typedef cufftReal (*cufftJITCallbackLoadR)(void *dataIn, unsigned long long offset, void *callerInfo, void *sharedPointer);
typedef cufftDoubleReal(*cufftJITCallbackLoadD)(void *dataIn, unsigned long long offset, void *callerInfo, void *sharedPointer);
typedef void (*cufftJITCallbackStoreC)(void *dataOut, unsigned long long offset, cufftComplex element, void *callerInfo, void *sharedPointer);
typedef void (*cufftJITCallbackStoreZ)(void *dataOut, unsigned long long offset, cufftDoubleComplex element, void *callerInfo, void *sharedPointer);
typedef void (*cufftJITCallbackStoreR)(void *dataOut, unsigned long long offset, cufftReal element, void *callerInfo, void *sharedPointer);
typedef void (*cufftJITCallbackStoreD)(void *dataOut, unsigned long long offset, cufftDoubleReal element, void *callerInfo, void *sharedPointer);
注意offset参数的类型差异(unsigned long long)与传统回调(使用size_t)之间的区别。
3.15.3.2. 传统回调的类型定义
传统的回调函数原型和指针类型定义如下:
typedef cufftComplex (*cufftCallbackLoadC)(void *dataIn, size_t offset, void *callerInfo, void *sharedPointer);
typedef cufftDoubleComplex (*cufftCallbackLoadZ)(void *dataIn, size_t offset, void *callerInfo, void *sharedPointer);
typedef cufftReal (*cufftCallbackLoadR)(void *dataIn, size_t offset, void *callerInfo, void *sharedPointer);
typedef cufftDoubleReal(*cufftCallbackLoadD)(void *dataIn, size_t offset, void *callerInfo, void *sharedPointer);
typedef void (*cufftCallbackStoreC)(void *dataOut, size_t offset, cufftComplex element, void *callerInfo, void *sharedPointer);
typedef void (*cufftCallbackStoreZ)(void *dataOut, size_t offset, cufftDoubleComplex element, void *callerInfo, void *sharedPointer);
typedef void (*cufftCallbackStoreR)(void *dataOut, size_t offset, cufftReal element, void *callerInfo, void *sharedPointer);
typedef void (*cufftCallbackStoreD)(void *dataOut, size_t offset, cufftDoubleReal element, void *callerInfo, void *sharedPointer);
3.15.4. 其他cuFFT类型
3.15.4.1. cufftHandle
-
type cufftHandle
-
一种用于存储和访问cuFFT计划的句柄类型。用户在创建cuFFT计划后会获得该句柄,并使用此句柄来执行计划。
typedef unsigned int cufftHandle;
3.15.4.2. cufftReal
单精度浮点实数数据类型。
typedef float cufftReal;
3.15.4.3. cufftDoubleReal
一种双精度浮点实数数据类型。
typedef double cufftDoubleReal;
3.15.4.4. cufftComplex
一种单精度浮点复数数据类型,由交错的实部和虚部组成。
typedef cuComplex cufftComplex;
3.15.4.5. cufftDoubleComplex
一种双精度浮点复数数据类型,由交错的实部和虚部组成。
typedef cuDoubleComplex cufftDoubleComplex;
3.16. 常见类型
3.16.1. cuda数据类型
cudaDataType 数据类型是CUDA库支持的类型枚举。
typedef enum cudaDataType_t
{
CUDA_R_16F= 2, // 16 bit real
CUDA_C_16F= 6, // 16 bit complex
CUDA_R_32F= 0, // 32 bit real
CUDA_C_32F= 4, // 32 bit complex
CUDA_R_64F= 1, // 64 bit real
CUDA_C_64F= 5, // 64 bit complex
CUDA_R_8I= 3, // 8 bit real as a signed integer
CUDA_C_8I= 7, // 8 bit complex as a pair of signed integers
CUDA_R_8U= 8, // 8 bit real as an unsigned integer
CUDA_C_8U= 9 // 8 bit complex as a pair of unsigned integers
} cudaDataType;
3.16.2. libraryPropertyType
libraryPropertyType 数据类型是库属性类型的枚举。(例如,CUDA 版本 X.Y.Z 将产生 MAJOR_VERSION=X, MINOR_VERSION=Y, PATCH_LEVEL=Z)
typedef enum libraryPropertyType_t
{
MAJOR_VERSION,
MINOR_VERSION,
PATCH_LEVEL
} libraryPropertyType;
4. 多GPU数据组织
本章将解释在多GPU转换前后,数据如何在GPU之间进行分布。为简化说明,本章假设调用者已指定使用GPU 0和GPU 1来执行转换。
4.1. 批处理变换的多GPU数据组织
对于批量转换操作,每个单独的转换任务会在单个GPU上执行。如果可能,系统会将这些批次均匀分配到各个GPU上。假设在一个由m个任务组成的批次上使用n个GPU运行,且m不能被n整除时,前m % n个GPU将各自执行\(\left\lfloor \frac{m}{n} \right\rfloor+\ 1\)个转换任务,其余GPU则执行\(\left\lfloor \frac{m}{n} \right\rfloor\)个任务。例如,在4个GPU上执行15个转换任务的批次时,前三个GPU各执行4个任务,最后一个GPU执行3个任务。这种方法消除了GPU间数据交换的需求,当批次大小能被GPU数量整除时,可实现近乎完美的线性扩展。
4.2. 单次2D和3D变换的多GPU数据组织
在多个GPU上执行的单一变换需要将数据在GPU之间进行划分。然后执行过程分阶段进行。例如,对于2个GPU的情况,在维度为偶数的2D和3D变换中,每个GPU负责处理(rank - 1)个维度的一半变换。随后GPU之间会交换数据,以便处理最终的维度。
由于2D和3D变换支持非2的幂次尺寸,数据可能无法在多个GPU之间均匀分布。一般情况下,对于n个GPU的场景,若某个维度尺寸m不是n的整数倍,其分配方式为:前m % n个GPU会额外获得一行数据(2D变换时)或一个平面数据(3D变换时)。
以4块GPU上的2D变换为例,使用C语言中声明的数组data[x][y],其中x为65,y为99。在变换前,表面被分配为:GPU 0获得尺寸为[17][99]的表面,GPU 1至3获得尺寸为[16][99]的表面。变换后,每块GPU再次获得部分表面,但这次是按y维度划分。GPU 0至2获得尺寸为[65][25]的表面,GPU 3获得尺寸为[65][24]的表面。
对于在4个GPU上进行的3D变换,考虑一个在C语言中声明为data[x][y][z]的数组,其中x为103,y为122,z为64。在变换前,数据体被分配为:GPU 0到2各自接收维度为[26][122][64]的数据体,而GPU 3接收维度为[25][122][64]的数据体。变换完成后,每个GPU再次获得部分表面数据,但这次是按y维度划分:GPU 0和1获得维度为[103][31][64]的数据体,GPU 2和3获得维度为[103][30][64]的数据体。
4.3. 单维变换的多GPU数据组织
默认情况下,对于一维变换,数据在GPU之间的初始分布方式与二维和三维情况类似。对于在两个GPU上进行的x维度变换,GPU 0接收的数据范围为0...(x/2-1),而GPU 1接收的数据范围为(x/2)...(x-1)。类似地,当使用4个GPU时,数据会均匀分布在所有4个GPU上。
在开始计算之前,数据会在多个GPU之间重新分配。如果应用程序在转换前不需要对数据进行预处理,可以在从主机内存复制时执行这种重新分配。为此,应用程序可以使用子格式CUFFT_XT_FORMAT_1D_INPUT_SHUFFLED通过cufftXtMalloc创建数据描述符。这可以显著减少执行转换所需的时间。
cuFFT通过将变换大小分解为因子Factor1和Factor2来执行多个GPU一维变换,并将数据视为大小为Factor1 x Factor2的网格。计算一维FFT的四个步骤是:对大小为Factor2的数据进行Factor1次变换、GPU之间的数据交换、逐点旋转因子乘法,以及对大小为Factor1的数据进行Factor2次变换。
为了通过计算与数据交换重叠来提高效率,cuFFT将整个变换分解为独立的段或字符串,这些段可以在其他段处理的同时进行处理。这种算法的一个副作用是变换的输出不是线性顺序的。GPU内存中的输出以字符串形式存在,每个字符串由Factor2个大小相等的子字符串组成。每个子字符串包含从上一个子字符串起始点后Factor1个元素开始的连续结果。每个字符串在前一个字符串起始点后子字符串大小的元素处开始。字符串按顺序排列,前半部分在GPU 0上,后半部分在GPU 1上。参见以下示例:
transform size = 1024
number of strings = 8
Factor1 = 64
Factor2 = 16
substrings per string for output layout is Factor2 (16)
string size = 1024/8 = 128
substring size = 128/16 = 8
stride between substrings = 1024/16 = Factor1 (64)
On GPU 0:
string 0 has substrings with indices 0...7 64...71 128...135 ... 960...967
string 1 has substrings with indices 8...15 72...79 136...143 ... 968...975
...
On GPU 1:
string 4 has substrings with indices 32...39 96...103 160...167 ... 992...999
...
string 7 has substrings with indices 56...63 120...127 184...191 ... 1016...1023
cufftXtQueryPlan API允许调用者检索一个包含字符串数量、分解因子以及(在2的幂次大小情况下)一些有用的掩码和移位元素的结构体。以下示例展示了如何调用cufftXtQueryPlan。它还展示了如何从主机输入数组中的索引转换为设备上的对应索引,反之亦然。
/*
* These routines demonstrate the use of cufftXtQueryPlan to get the 1D
* factorization and convert between permuted and linear indexes.
*/
/*
* Set up a 1D plan that will execute on GPU 0 and GPU1, and query
* the decomposition factors
*/
int main(int argc, char **argv){
cufftHandle plan;
cufftResult stat;
int whichGPUs[2] = { 0, 1 };
cufftXt1dFactors factors;
stat = cufftCreate( &plan );
if (stat != CUFFT_SUCCESS) {
printf("Create error %d\n",stat);
return 1;
}
stat = cufftXtSetGPUs( plan, 2, whichGPUs );
if (stat != CUFFT_SUCCESS) {
printf("SetGPU error %d\n",stat);
return 1;
}
stat = cufftMakePlan1d( plan, size, CUFFT_C2C, 1, workSizes );
if (stat != CUFFT_SUCCESS) {
printf("MakePlan error %d\n",stat);
return 1;
}
stat = cufftXtQueryPlan( plan, (void *) &factors, CUFFT_QUERY_1D_FACTORS );
if (stat != CUFFT_SUCCESS) {
printf("QueryPlan error %d\n",stat);
return 1;
}
printf("Factor 1 %zd, Factor2 %zd\n",factors.factor1,factors.factor2);
cufftDestroy(plan);
return 0;
}
/*
* Given an index into a permuted array, and the GPU index return the
* corresponding linear index from the beginning of the input buffer.
*
* Parameters:
* factors input: pointer to cufftXt1dFactors as returned by
* cufftXtQueryPlan
* permutedIx input: index of the desired element in the device output
* array
* linearIx output: index of the corresponding input element in the
* host array
* GPUix input: index of the GPU containing the desired element
*/
cufftResult permuted2Linear( cufftXt1dFactors * factors,
size_t permutedIx,
size_t *linearIx,
int GPUIx ) {
size_t indexInSubstring;
size_t whichString;
size_t whichSubstring;
// the low order bits of the permuted index match those of the linear index
indexInSubstring = permutedIx & factors->substringMask;
// the next higher bits are the substring index
whichSubstring = (permutedIx >> factors->substringShift) &
factors->factor2Mask;
// the next higher bits are the string index on this GPU
whichString = (permutedIx >> factors->stringShift) & factors->stringMask;
// now adjust the index for the second GPU
if (GPUIx) {
whichString += factors->stringCount/2;
}
// linear index low order bits are the same
// next higher linear index bits are the string index
*linearIx = indexInSubstring + ( whichString << factors->substringShift );
// next higher bits of linear address are the substring index
*linearIx += whichSubstring << factors->factor1Shift;
return CUFFT_SUCCESS;
}
/*
* Given a linear index into a 1D array, return the GPU containing the permuted
* result, and index from the start of the data buffer for that element.
*
* Parameters:
* factors input: pointer to cufftXt1dFactors as returned by
* cufftXtQueryPlan
* linearIx input: index of the desired element in the host input
* array
* permutedIx output: index of the corresponding result in the device
* output array
* GPUix output: index of the GPU containing the result
*/
cufftResult linear2Permuted( cufftXt1dFactors * factors,
size_t linearIx,
size_t *permutedIx,
int *GPUIx ) {
size_t indexInSubstring;
size_t whichString;
size_t whichSubstring;
size_t whichStringMask;
int whichStringShift;
if (linearIx >= factors->size) {
return CUFFT_INVALID_VALUE;
}
// get a useful additional mask and shift count
whichStringMask = factors->stringCount -1;
whichStringShift = (factors->factor1Shift + factors->factor2Shift) -
factors->stringShift ;
// the low order bits identify the index within the substring
indexInSubstring = linearIx & factors->substringMask;
// first determine which string has our linear index.
// the low order bits indentify the index within the substring.
// the next higher order bits identify which string.
whichString = (linearIx >> factors->substringShift) & whichStringMask;
// the first stringCount/2 strings are in the first GPU,
// the rest are in the second.
*GPUIx = whichString/(factors->stringCount/2);
// next determine which substring within the string has our index
// the substring index is in the next higher order bits of the index
whichSubstring = (linearIx >>(factors->substringShift + whichStringShift)) &
factors->factor2Mask;
// now we can re-assemble the index
*permutedIx = indexInSubstring;
*permutedIx += whichSubstring << factors->substringShift;
if ( !*GPUIx ) {
*permutedIx += whichString << factors->stringShift;
} else {
*permutedIx += (whichString - (factors->stringCount/2) ) <<
factors->stringShift;
}
return CUFFT_SUCCESS;
}
5. FFTW转换指南
cuFFT与FFTW的不同之处在于,FFTW拥有多种计划方案和单一的执行函数,而cuFFT虽然计划方案较少,但提供了多个执行函数。cuFFT的执行函数决定了计算精度(单精度或双精度)以及输入值是复数还是实数。下表展示了两者接口之间的对应关系。
FFTW 函数 |
cuFFT 函数 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6. FFTW 接口对接 cuFFT
NVIDIA为cuFFT库提供了FFTW3接口。这使得使用FFTW的应用程序只需对程序源代码进行最小修改即可利用NVIDIA GPU。要使用该接口,首先需要完成以下两个步骤
建议您将包含文件
fftw3.h替换为cufftw.h无需链接双精度/单精度库(如
fftw3/fftw3f库),改为同时链接cuFFT和cuFFTW库确保搜索路径包含存放
cuda_runtime_api.h的目录
当应用程序通过FFTW3接口正常运行后,用户可能希望修改代码以在GPU之间传输数据,并使用FFTW转换指南中记录的程序来获得最佳性能。
以下表格展示了cuFFT支持的FFTW3组件和功能。
FFTW手册中的章节 |
支持 |
不支持 |
|---|---|---|
复数 |
|
|
精确度 |
双精度 |
不支持长双精度 |
内存分配 |
|
|
多线程FFTW |
|
|
基于MPI的分布式内存FFTW |
|
请注意,以下每个双精度函数都有一个对应的单精度版本,其中字母fftw被替换为fftwf。
FFTW手册中的章节 |
支持 |
不支持 |
|---|---|---|
使用计划 |
|
|
基础接口 |
||
复杂离散傅里叶变换 |
|
|
规划器标志 |
规划器标志会被忽略,无论设置如何都会返回相同的执行计划 |
|
真实数据离散傅里叶变换 |
|
|
读取数据 DFT 数组格式 |
不支持 |
|
读取到真实转换 |
不支持 |
|
读取到真实转换类型 |
不支持 |
|
高级接口 |
||
高级复杂离散傅里叶变换 |
|
|
高级实数据离散傅里叶变换 |
|
|
高级实数到实数变换 |
不支持 |
|
Guru 界面 |
||
交错和拆分数组 |
交错格式 |
拆分格式 |
Guru向量和变换尺寸 |
|
|
Guru 复杂DFT |
|
|
Guru 真实数据 DFTs |
不支持 |
|
Guru 实对实转换 |
不支持 |
|
64位大师界面 |
|
|
新数组执行函数 |
|
拆分格式和实对实函数 |
智慧 |
|
7. 已弃用的功能
从CUDA 12.0开始:
不再支持GPU架构SM35和SM37。最低要求的架构是SM50。
从CUDA 11.8开始:
CUDA Graphs 不再支持在非原位模式转换中加载数据的旧版回调例程。从 CUDA 12.6 更新 2 开始,可以使用 LTO 回调作为旧版回调的替代方案,且不受此限制。
从CUDA 11.4开始:
在所有GPU架构上,已弃用使用单独编译的设备代码(传统回调)的回调功能支持。回调功能将继续在所有GPU架构上得到支持。
从CUDA 11.0开始:
不再支持GPU架构SM30。最低要求的架构是SM35。
不再支持GPU架构SM35、SM37(开普勒)以及SM50、SM52(麦克斯韦)。
函数 cufftSetCompatibilityMode 已在 9.1 版本中被移除。
8. 公告
8.1. 注意事项
本文档仅供信息参考之用,不应视为对产品功能、状态或质量的保证。NVIDIA公司(“NVIDIA”)对本文件所含信息的准确性或完整性不作任何明示或暗示的陈述或保证,并对其中可能存在的错误不承担任何责任。NVIDIA对于因使用此类信息而产生的后果、或因使用该信息导致的第三方专利或其他权利侵权概不负责。本文件不构成对开发、发布或交付任何材料(定义见下文)、代码或功能的承诺。
NVIDIA保留随时对本文件进行更正、修改、增强、改进以及任何其他变更的权利,恕不另行通知。
客户在下单前应获取最新的相关信息,并确认这些信息是最新且完整的。
除非NVIDIA与客户授权代表签署的单独销售协议中另有约定,否则NVIDIA产品的销售均以订单确认时提供的NVIDIA标准销售条款和条件为准(以下简称"销售条款")。NVIDIA特此明确反对将任何客户通用条款适用于本文件所述NVIDIA产品的采购。本文件不直接或间接构成任何合同义务。
NVIDIA产品并非设计、授权或保证适用于医疗、军事、航空、航天或生命支持设备,也不适用于那些可以合理预期NVIDIA产品故障或失灵会导致人身伤害、死亡、财产或环境损害的应用场景。NVIDIA对于在此类设备或应用中使用和/或包含NVIDIA产品不承担任何责任,因此客户需自行承担相关风险。
NVIDIA不声明或保证基于本文档的产品适用于任何特定用途。NVIDIA未必会对每个产品的所有参数进行测试。客户应全权负责评估和确定本文档所含信息的适用性,确保产品适合并满足客户计划的应用需求,并执行必要的应用测试以避免应用或产品出现故障。客户产品设计中的缺陷可能会影响NVIDIA产品的质量和可靠性,并可能导致超出本文档范围的其他或不同的条件和/或要求。对于任何因以下原因导致的故障、损坏、成本或问题,NVIDIA不承担任何责任:(i) 以违反本文档的任何方式使用NVIDIA产品或(ii) 客户产品设计。
本文档不授予任何NVIDIA专利权、版权或其他NVIDIA知识产权的明示或暗示许可。NVIDIA发布的关于第三方产品或服务的信息,不构成NVIDIA对这些产品或服务的使用许可或担保认可。使用此类信息可能需要获得第三方基于其专利或其他知识产权的许可,或需要获得NVIDIA基于其专利或其他知识产权的许可。
本文件中的信息仅可在获得NVIDIA事先书面批准、未经改动完整复制且完全符合所有适用的出口法律法规,并附带所有相关条件、限制和声明的情况下进行复制。
本文件及所有NVIDIA设计规格、参考板、文件、图纸、诊断工具、清单和其他文档(统称及单独称为"材料")均以"现状"提供。NVIDIA不对材料作出任何明示或默示的保证,包括但不限于对不侵权、适销性和特定用途适用性的默示保证免责。在法律允许的最大范围内,NVIDIA不就因使用本文件导致的任何损害承担责任,包括但不限于任何直接、间接、特殊、附带、惩罚性或后果性损害,无论损害成因如何,也无论责任理论为何,即使NVIDIA已被告知发生此类损害的可能性。不论客户因任何原因可能遭受的任何损害,NVIDIA对客户就本文所述产品的全部及累计责任应受产品销售条款的限制。
8.2. OpenCL
OpenCL是苹果公司的商标,经Khronos Group Inc.授权使用。
8.3. 商标
NVIDIA和NVIDIA标识是美国及其他国家NVIDIA公司的商标或注册商标。其他公司及产品名称可能是其各自关联公司的商标。