一、流程

1.使用cufftHandle创建句柄

2.使用cufftPlan1d(),cufftPlan3d(),cufftPlan3d(),cufftPlanMany()对句柄进行配置,主要是配置句柄对应的信号长度,信号类型,在内存中的存储形式等信息。

cufftPlan1d():针对单个 1 维信号

cufftPlan2d():针对单个 2 维信号

cufftPlan3d():针对单个 3 维信号

cufftPlanMany():针对多个信号同时进行 fft

3.使用cufftExec()函数执行 fft

4.使用cufftDestroy()函数释放 GPU 资源

二、单个 1 维信号的 fft

假设要执行 fft 的信号data_dev的长度为N,并且已经传输到 GPU 显存中,data_dev数据的类型为cufftComplex,可以用一下方式产生主机段的data_dev。

cufftComplex *data_Host = (cufftComplex*)malloc(NX*BATCH * sizeof(cufftComplex)); // 主机端数据头指针
// 初始数据
for (int i = ; i < NX; i++)
{
data_Host[i].x = float((rand() * rand()) % NX) / NX;
data_Host[i].y = float((rand() * rand()) % NX) / NX;
}

然后用cudaMemcpy()将主机端的data_host拷贝到设备端的data_dev,即可用下述方法执行 fft :

cufftHandle plan; // 创建cuFFT句柄
cufftPlan1d(&plan, N, CUFFT_C2C, BATCH);
cufftExecC2C(plan, data_dev, data_dev, CUFFT_FORWARD); // 执行 cuFFT,正变换

cufftPlan1d()

  • 第一个参数就是要配置的 cuFFT 句柄;
  • 第二个参数为要进行 fft 的信号的长度;
  • 第三个CUFFT_C2C为要执行 fft 的信号输入类型及输出类型都为复数;CUFFT_C2R表示输入复数,输出实数;CUFFT_R2C表示输入实数,输出复数;CUFFT_R2R表示输入实数,输出实数;
  • 第四个参数BATCH表示要执行 fft 的信号的个数,新版的已经使用cufftPlanMany()来同时完成多个信号的 fft。

cufftExecC2C()

  • 第一个参数就是配置好的 cuFFT 句柄;
  • 第二个参数为输入信号的首地址;
  • 第三个参数为输出信号的首地址;
  • 第四个参数CUFFT_FORWARD表示执行的是 fft 正变换;CUFFT_INVERSE表示执行 fft 逆变换。

需要注意的是,执行完逆 fft 之后,要对信号中的每个值乘以 1/N

三、代码实现

#include <iostream>
#include <time.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <cufft.h> #define NX 3335 // 有效数据个数
#define N 5335 // 补0之后的数据长度
#define BATCH 1
#define BLOCK_SIZE 1024
using std::cout;
using std::endl; /**
* 功能:判断两个 cufftComplex 数组的是否相等
* 输入:idataA 输入数组A的头指针
* 输入:idataB 输出数组B的头指针
* 输入:size 数组的元素个数
* 返回:true | false
*/
bool IsEqual(cufftComplex *idataA, cufftComplex *idataB, const int size)
{
for (int i = ; i < size; i++)
{
if (abs(idataA[i].x - idataB[i].x) > 0.000001 || abs(idataA[i].y - idataB[i].y) > 0.000001)
return false;
} return true;
} /**
* 功能:实现 cufftComplex 数组的尺度缩放,也就是乘以一个数
* 输入:idata 输入数组的头指针
* 输出:odata 输出数组的头指针
* 输入:size 数组的元素个数
* 输入:scale 缩放尺度
*/
static __global__ void cufftComplexScale(cufftComplex *idata, cufftComplex *odata, const int size, float scale)
{
const int threadID = blockIdx.x * blockDim.x + threadIdx.x; if (threadID < size)
{
odata[threadID].x = idata[threadID].x * scale;
odata[threadID].y = idata[threadID].y * scale;
}
} int main()
{
cufftComplex *data_dev; // 设备端数据头指针
cufftComplex *data_Host = (cufftComplex*)malloc(NX*BATCH * sizeof(cufftComplex)); // 主机端数据头指针
cufftComplex *resultFFT = (cufftComplex*)malloc(N*BATCH * sizeof(cufftComplex)); // 正变换的结果
cufftComplex *resultIFFT = (cufftComplex*)malloc(NX*BATCH * sizeof(cufftComplex)); // 先正变换后逆变换的结果 // 初始数据
for (int i = ; i < NX; i++)
{
data_Host[i].x = float((rand() * rand()) % NX) / NX;
data_Host[i].y = float((rand() * rand()) % NX) / NX;
} dim3 dimBlock(BLOCK_SIZE); // 线程块
dim3 dimGrid((NX + BLOCK_SIZE - ) / dimBlock.x); // 线程格 cufftHandle plan; // 创建cuFFT句柄
cufftPlan1d(&plan, N, CUFFT_C2C, BATCH); // 计时
clock_t start, stop;
double duration;
start = clock(); cudaMalloc((void**)&data_dev, sizeof(cufftComplex)*N*BATCH); // 开辟设备内存
cudaMemset(data_dev, , sizeof(cufftComplex)*N*BATCH); // 初始为0
cudaMemcpy(data_dev, data_Host, NX * sizeof(cufftComplex), cudaMemcpyHostToDevice); // 从主机内存拷贝到设备内存 cufftExecC2C(plan, data_dev, data_dev, CUFFT_FORWARD); // 执行 cuFFT,正变换
cudaMemcpy(resultFFT, data_dev, N * sizeof(cufftComplex), cudaMemcpyDeviceToHost); // 从设备内存拷贝到主机内存 cufftExecC2C(plan, data_dev, data_dev, CUFFT_INVERSE); // 执行 cuFFT,逆变换
cufftComplexScale << <dimGrid, dimBlock >> > (data_dev, data_dev, N, 1.0f / N); // 乘以系数
cudaMemcpy(resultIFFT, data_dev, NX * sizeof(cufftComplex), cudaMemcpyDeviceToHost); // 从设备内存拷贝到主机内存 stop = clock();
duration = (double)(stop - start) * / CLOCKS_PER_SEC;
cout << "时间为 " << duration << " ms" << endl; cufftDestroy(plan); // 销毁句柄
cudaFree(data_dev); // 释放空间 cout << IsEqual(data_Host, resultIFFT, NX) << endl; return ;
}

四、用fftw和cufft实现傅里叶变换

1.创建C++的文件命名为fftw.cpp,配置fftw环境(环境配置移步:这里),复制以下代码

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "fftw3.h"
#include <windows.h>
#include <Eigen/Dense>
#include <iostream>
#include <opencv2/core/eigen.hpp>
#include <opencv2/opencv.hpp>
#include <iostream> using namespace cv;
using namespace std;
using namespace Eigen; #define COLS 3
#define ROWS 3 #pragma comment(lib, "libfftw3-3.lib") // double版本
//#pragma comment(lib, "libfftw3f-3.lib")// float版本
// #pragma comment(lib, "libfftw3l-3.lib")// long double版本 extern "C" void iteration_mat1(); /**********************************主函数****************************************/
int main()
{ fftw_complex*result_temp_din, *result_temp_out;
fftw_plan p; result_temp_din = (fftw_complex *)fftw_malloc(sizeof(fftw_complex)*COLS*ROWS);
result_temp_out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex)*COLS*ROWS);
cout << "fftw" << endl; for (size_t j = ; j < ROWS; j++)
{
for (size_t i = ; i < COLS; i++)
{ result_temp_din[i + j*COLS][] = (i+)*(j+);
cout << result_temp_din[i + j*COLS][] << " ";
result_temp_din[i + j*COLS][] = ;
}
} //forward fft
p = fftw_plan_dft_2d(ROWS, COLS, result_temp_din, result_temp_out, FFTW_FORWARD, FFTW_ESTIMATE); fftw_execute(p);
cout << endl;
for (size_t j = ; j < ROWS; j++)
{
for (size_t i = ; i < COLS; i++)
{
cout << result_temp_out[i + j*COLS][] << " ";//实部
cout << result_temp_out[i + j*COLS][] << endl;//虚部
}
} cout << "cuda" << endl;
iteration_mat1();
system("pause");
return ;
}

2.创建cuda文件命名为cufft.cu,配置环境(环境配置移步:这里),复制以下代码

注: cufftPlan2d(&p, ROWS, COLS, CUFFT_C2C); 看清楚rows和cols,千万别出错!

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <cufft.h>
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <iostream> using namespace std;
using namespace cv; #define COLS 3
#define ROWS 3 extern "C" void iteration_mat1()
{
cufftComplex *result_temp_din = (cufftComplex*)malloc(COLS*ROWS * sizeof(cufftComplex));
cufftHandle p;
//输入赋值数据
for (size_t j = ; j < ROWS; j++)
{
for (size_t i = ; i < COLS; i++)
{
result_temp_din[i + j*COLS].x = (i + )*(j + );
cout << result_temp_din[i + j*COLS].x << " ";
result_temp_din[i + j*COLS].y = ;
}
}
cout << endl; size_t pitch; cufftComplex *t_result_temp_din;
cudaMallocPitch((void**)&t_result_temp_din, &pitch, COLS * sizeof(cufftComplex), ROWS); cufftComplex *t_result_temp_out;
cudaMallocPitch((void**)&t_result_temp_out, &pitch, COLS * sizeof(cufftComplex), ROWS); //将值辅到Device
//cudaMemcpy2D(t_result_temp_din, pitch, result_temp_din, COLS * sizeof(cufftComplex), COLS * sizeof(cufftComplex), ROWS, cudaMemcpyHostToDevice);
cudaMemcpy(t_result_temp_din,result_temp_din, ROWS * sizeof(cufftComplex)* COLS, cudaMemcpyHostToDevice); //forward fft 制定变换规则
cufftPlan2d(&p, ROWS, COLS, CUFFT_C2C); //执行变换
cufftExecC2C(p, (cufftComplex*)t_result_temp_din, (cufftComplex*)t_result_temp_out, CUFFT_FORWARD); //将值辅到host
cudaMemcpy(result_temp_din, t_result_temp_out, ROWS * sizeof(cufftComplex)* COLS, cudaMemcpyDeviceToHost);
//cudaMemcpy2D(result_temp_din, pitch, t_result_temp_out, COLS * sizeof(cufftComplex), sizeof(cufftComplex)* ROWS, COLS, cudaMemcpyDeviceToHost); //提取实部和虚部
for (size_t j = ; j < ROWS; j++)
{
for (size_t i = ; i < COLS; i++)
{
cout << result_temp_din[i + j*COLS].x << " ";//实部
cout << result_temp_din[i + j*COLS].y << endl;//虚部
}
} }

3.执行结果:

FFTW3学习笔记3:FFTW 和 CUFFT 的使用对比的更多相关文章

  1. Java NIO 学习笔记(七)----NIO/IO 的对比和总结

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  2. FFTW3学习笔记2:FFTW(快速傅里叶变换)中文参考

    据说FFTW(Fastest Fourier Transform in the West)是世界上最快的FFT.为了详细了解FFTW以及为编程方便,特将用户手册看了一下,并结合手册制作了以下FFTW中 ...

  3. FFTW3学习笔记1:VS2015下配置FFTW3(快速傅里叶变换)库

    一.FFTW简介 FFTW ( the Faster Fourier Transform in the West) 是一个快速计算离散傅里叶变换的标准C语言程序集,其由MIT的M.Frigo 和S. ...

  4. PHP学习笔记(8)验证码使用session对比

    知识点: 1. session获取其他页面的变量: (1)先在画验证码php里开启session_start(),$_SESSION['随便起名']=验证码字符串, (2)再在submit提交到act ...

  5. Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  6. Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  7. Java NIO 学习笔记(四)----文件通道和网络通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  8. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  9. Java NIO 学习笔记(二)----聚集和分散,通道到通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

随机推荐

  1. R1(上)—R关联规则分析之Arules包详解

    Arules包详解 包基本信息 发布日期:2014-12-07 题目:挖掘关联规则和频繁项集 描述:提供了一个表达.处理.分析事务数据和模式(频繁项集合关联规则)的基本框架. URL:http://R ...

  2. 【CodeForces】899 E. Segments Removal

    [题目]E. Segments Removal [题意]给定n个数字,每次操作删除最长的连续相同数字(等长删最左),求全部删完的最少次数.n<=2*10^6,1<=ai<=10^9. ...

  3. 2017ACM暑期多校联合训练 - Team 8 1006 HDU 6138 Fleet of the Eternal Throne (字符串处理 AC自动机)

    题目链接 Problem Description The Eternal Fleet was built many centuries ago before the time of Valkorion ...

  4. 大聊Python----装饰器

    什么是装饰器? 装饰器其实和函数没啥区别,都是用def去定义的,其本质就是函数,而功能就是装饰其他的函数,说白了就是为其他函数提供附加功能 装饰器有什么作用? 比如你是一个公司的员工,你所写的程序里有 ...

  5. Xcode 获取本地IP

    // // // #define MAXADDRS 32 extern char *ip_names[MAXADDRS]; void InitAddresses(); void GetIPAddres ...

  6. WAMP允许外部访问的修改方法

    apache配置文件httpd.conf里的 "Require local"改" Require all granted"

  7. jq时间日期插件的使用-datetimepicker

    分三步  首先引入各种包 然后搞哥容器用id  然后加入一段js 实例: 下载:http://files.cnblogs.com/files/wordblog/datetimepicker-maste ...

  8. VueJS ElementUI el-table 的 formatter 和 scope template 不能同时存在

    暂时可以通过 在 scope template 中自己处理格式化解决 相关issue: 2548

  9. 41、和为S的连续正数序列

    一.题目 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久,他 ...

  10. localhost或127.0.0.1或192.168.1.*被转到129129.com上的问题

    系统启动里会有个httpd的apache程序在运行,自启禁用掉后.windows下有个apache文件夹,干掉就可以. 个别GHOST XP程序里面会装这种流氓程序.