C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比

这篇具有很好参考价值的文章主要介绍了C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

任务介绍

时隔多年仍然逃不掉写C的命运……因为这个任务周期不短还踩了好多坑,必须记录一下了。
任务简单要求就是使用C语言编写一个GPU加速的快速傅里叶变换(FFT)
分为GPU加速的FFT代码改写、未使用GPU的FFT编写、运算速度对比、运算结果测试(与matlab结果对比),只要按照我文章写的顺序做就行

环境所需相关软件下载与安装

Visual Studio 2010
要运行C语言代码就要先下载Microsoft Visual Studio 编辑器,我的电脑是Win10系统,但为了与项目环境和大家的使用习惯保持一致,使用的是Visual Studio 2010版本,网上有安装包的下载,这里提供一个我保存的中文版安装包
链接:https://pan.baidu.com/s/12Eyw5Woh11gtP6hyxbpUmQ
提取码:em9h
安装过程还挺顺利的,网上也有很多安装教程,就是安装时间挺久,我安装在D盘,最后应用图标在"D:\Visual Studio 2010\Common7\IDE\devenv.exe",安装在其他路径的只要找到IDE文件夹就能找到。

CUDA7.5
然后需要下载CUDA 来调用GPU。首先保证自己的电脑是有显卡GPU的(GPU和CUDA版本对应关系网上可以找到,这里插一个写挺好的文章链接CUDA和GPU版本对应),我的笔记本是双显卡,独立显卡是NVIDIA GeForce GTX 1660Ti,最高能用CUDA11.7版本的

集成显卡:为电脑基本显卡,在正常情况下电脑会一直使用集成显卡,以降低电脑运行负担,减少散热,提高笔记本使用寿命。
独立显卡:为电脑的高级显卡,多用来运行大型游戏以及部分软件。
要改为优先使用独立显卡的话,就打开NIVDIA控制面板,在左面管理3D设置中全局设置->首选图形处理器->下拉选择高性能NIVDIA处理器,就设置成功了

CUDA和Visual Studio 2010是有版本兼容关系的,在安装好Visual Studio 2010后安装CUDA需要选择对应版本,网上找到Visual Studio 2013是安装的CUDA7.5,但我想装一个能兼容的最高级版,就从9.0试到7.5 (CUDA历史版本下载链接),要是版本不兼容在安装CUDA的时候会出现不兼容的提示,就没办法继续安装,那个图我没保存,但安装的时候看到就能明白(安装教程简单,最好安装在默认路径,网上可找到教程),最终确定兼容版本是CUDA7.5+Visual Studio 2010

环境配置
软件安装完后配置环境,这里我基本是按照这篇文章教程配置的
windows+VS2013+CUDA7.5配置
只有在这篇文章中的第3步我创建项目的方式不太一样
我是打开VS2010创建一个空项目,在源文件中添加一个新建项,选择C++文件,这里注意:不加后缀名生成的源文件是.cpp后缀名的,这是C++代码文件后缀名,使用CUDA的C代码要命名为.cu的格式,不使用CUDA的C代码命名为.c格式C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比
如果在这篇文章第3.f步的项类型里没有找到CUDA C++的话,就右键 【工程】-【生成自定义】-勾选上CUDA 7.5就行,如下图
C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比

最后最好拿他给的测试代码试跑一下,能成功就算环境配置成功

C语言:不调用库的GPU加速FFT代码

开始网上找到这篇文章CUDA实现FFT并行计算
在我简单调试更改后可以正常运行,很开心,我对代码加了些注释,这里把这个能正常运行的代码贴上,分为两个部分代码。
先是需要调用的函数Complex.cu文件,我跟主文件放在一起

#define PI 3.1415926

class Complex 
{
public:
    double real;
    double imag;

    Complex() 
	{

    }

    // Wn 获取n次单位复根中的主单位根
    __device__ static Complex W(int n) 
	{
        Complex res = Complex(cos(2.0 * PI / n), sin(2.0 * PI / n));
        return res;
    }

    // Wn^k 获取n次单位复根中的第k个
    __device__ static Complex W(int n, int k) 
	{
        Complex res = Complex(cos(2.0 * PI * k / n), sin(2.0 * PI * k / n));
        return res;
    }
    
    // 实例化并返回一个复数(只能在Host调用)
    static Complex GetComplex(double real, double imag) 
	{
        Complex r;
        r.real = real;
        r.imag = imag;
        return r;
    }

    // 随机返回一个复数
    static Complex GetRandomComplex() 
	{
        Complex r;
        r.real = (double)rand() / rand();
        r.imag = (double)rand() / rand();
        return r;
    }

    // 随即返回一个实数
    static Complex GetRandomReal() 
	{
        Complex r;
        r.real = (double)rand() / rand();
        r.imag = 0;
        return r;
    }

 //   // 随即返回一个纯虚数
 //   static Complex GetRandomPureImag() 
	//{
 //       Complex r;
 //       r.real = 0;
 //       r.imag = (double)rand() / rand();
 //       return r;
 //   }

    // 构造函数(只能在Device上调用)
    __device__ Complex(double real, double imag) 
	{
        this->real = real;
        this->imag = imag;
    }
    
    // 运算符重载
    __device__ Complex operator+(const Complex &other) 
	{
        Complex res(this->real + other.real, this->imag + other.imag);
        return res;
    }

    __device__ Complex operator-(const Complex &other) 
	{
        Complex res(this->real - other.real, this->imag - other.imag);
        return res;
    }

    __device__ Complex operator*(const Complex &other) 
	{
        Complex res(this->real * other.real - this->imag * other.imag, this->imag * other.real + this->real * other.imag);
        return res;
    }
};


然后是主文件

// 一维FFT
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "Complex.cu"  // 自定义的复数数据结构
#include <iostream>
#include <string>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
#include<mmSystem.h>
#pragma comment(lib,"winmm.lib")
using namespace std;


// 根据数列长度n获取二进制位数
int GetBits(int n) 
{
    int bits = 0;
    while (n >>= 1) 
	{
        bits++;
    }
    return bits;
}

// 在二进制位数为bits的前提下求数值i的二进制逆转
__device__ int BinaryReverse(int i, int bits) 
{
    int r = 0;
    do 
	{
        r += i % 2 << --bits;
    } while (i /= 2);
    return r;
}

// 蝴蝶操作, 输出结果直接覆盖原存储单元的数据, factor是旋转因子
__device__ void Bufferfly(Complex *a, Complex *b, Complex factor) 
{
    Complex a1 = (*a) + factor * (*b);
    Complex b1 = (*a) - factor * (*b);
    *a = a1;
    *b = b1; 
}

__global__ void FFT(Complex nums[], Complex result[], int n, int bits) 
{
    int tid = threadIdx.x + blockDim.x * blockIdx.x;  
	//threadIdx,线程ID的索引
	//blockDim:线程块的维度
	//blockIdx,线程块ID的索引
    if (tid >= n) return;
    for (int i = 2; i < 2 * n; i *= 2)
	{
        if (tid % i == 0) 
		{
            int k = i;
            if (n - tid < k) k = n - tid;
            for (int j = 0; j < k / 2; ++j) 
			{
                Bufferfly(&nums[BinaryReverse(tid + j, bits)], &nums[BinaryReverse(tid + j + k / 2, bits)], Complex::W(k, j));
            }
        }
        __syncthreads();    //未定义标识符
    }
    result[tid] = nums[BinaryReverse(tid, bits)];
}

// 打印数列
void printSequence(Complex nums[], const int N) 
{
    printf("[");
    for (int i = 0; i < N; ++i) 
	{
        double real = nums[i].real, imag = nums[i].imag;
        if (imag == 0) printf("%.16f", real);
        else 
		{
            if (imag > 0) printf("%.16f+%.16fi", real, imag);
            else printf("%.16f%.16fi", real, imag);
        }
        if (i != N - 1) printf(", ");
    }
    printf("]\n");
}

int main() 
{
    //srand(time(0));  // 设置当前时刻为随机数种子
    const int TPB = 1024;  // 每个Block的线程数,即blockDim.x,每个块中的线程数量 <= 1024
    const int N = 4000000;  // 数列大小
    const int bits = GetBits(N);
    DWORD Start, End;

    // 随机生成实数数列
    Complex *nums = (Complex*)malloc(sizeof(Complex) * N), *dNums, *dResult;  //申请一块内存,其大小是N个complex长度的总和。然后把这块内存的首地址强转成complex *指针变量类型,赋给*nums
    for (int i = 0; i < N; ++i) 
	{   
		nums[i] = Complex::GetRandomReal();  //没有在类的声明里给出GetRandomReal的定义,那么在类外定义GetRandomReal时, 就要加冒号,表示这个GetRandomReal()函数是类Complex的成员函数
    }


    //printf("数列长度为: %d\n", N);
    /*printf("Before FFT: \n");
    printSequence(nums, N);*/
    
    // 保存开始时间
    Start = timeGetTime();
    //printf("开始时间为: %d\n", Start);

    // 分配device内存,拷贝数据到device
    cudaMalloc((void**)&dNums, sizeof(Complex) * N);
    cudaMalloc((void**)&dResult, sizeof(Complex) * N);
    cudaMemcpy(dNums, nums, sizeof(Complex) * N, cudaMemcpyHostToDevice);//cudaMemcpy用于在主机(Host)和设备(Device)之间往返的传递数据
    //主机到设备:cudaMemcpy(d_A,h_A,nBytes,cudaMemcpyHostToDevice)
    

    // 调用kernel,在GPU进行的函数通常称为核函数,一般通过__global__修饰(在核函数里,都用双下划线来修饰)
    int threadPerBlock = TPB;
	//dim3 threadPerBlock = dim3(TPB);//threadPerBlock代表线程块内含有的线程数目thread
	//printf("%d\n%d\n%d\n",threadPerBlock.x, threadPerBlock.y, threadPerBlock.z);
	/*printf("%d\n", threadPerBlock);*/

	 int blockNum = (N + threadPerBlock - 1) / threadPerBlock;
    //dim3 blockNum = dim3((N + threadPerBlock.x - 1) / threadPerBlock.x); //blockNum代表block线程块数目
	//printf("%d\n", blockNum);
	 //printf("%d\n%d\n%d\n",blockNum.x, blockNum.y, blockNum.z);

	FFT <<< blockNum, threadPerBlock >>>(dNums, dResult, N, bits);

	cudaError_t err;
    err = cudaGetLastError(); // `cudaGetLastError` 会捕获上面代码中的最近的一个错误
    if (err != cudaSuccess) 
	{
		printf("Error: %s\n", cudaGetErrorString(err));
	}

    // 拷贝回结果
    cudaMemcpy(nums, dResult, sizeof(Complex) * N, cudaMemcpyDeviceToHost); 
    //设备到主机:cudaMemcpy(h_A,d_A,nBytes,cudaMemcpyDeviceToHost)

    // 计算用时
    End = timeGetTime();
	//printf("结束时间为: %d\n", End);
    /*printf("After FFT: \n");
    printSequence(nums, N);*/
    printf("变换用时: %dms\n", End - Start);
    

    // 释放内存
    free(nums);
    cudaFree(dNums);
    cudaFree(dResult);
}

关于CUDA加速应用程序的教程这篇文章介绍的很详细,看一遍差不多就串起来了
CUDA C/C++ 教程一:加速应用程序
再贴一个看的时候记的一个草稿纸……
C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比
timeGetTime()计算时间精度比较高,我用了这个,最终计算4*10^6序列长度的数据,这个代码运算时间竟然要七秒多……要知道不用GPU的FFT才160ms左右(这个代码在下面会详细介绍),反正这个运算时间让我直接裂开,减没了输出内容,将配置从Debug改为Release,选用独立显卡运算试了好多都不顶用,又一行又一行看代码,也没看出哪里有毛病。
后来看到说调用GPU运算数据量太少,CPU传到GPU也要耗费时间,使用GPU要运算数据量大的才有优势,好嘛,我就加序列长度,没加到10^8就爆内存了,一看运算时间还是比不用GPU的FFT慢十倍多,我彻底萎掉了。

C语言:调用fftw库的未使用GPU的FFT代码

我是看到前辈调用了fftw的库写FFT简直简便了好多,这个库分为32位的和64位的,网上可以下载,我还是贴一下我的下载链接fftw32位和64位库百度网盘下载
链接:https://pan.baidu.com/s/1kILG2Y10KUGh7g1iIOZOqw
提取码:bpgn
我的电脑是使用64位的库,安装教程参考这篇文章FFTW、Eigen库在VisualStudio中的导入和使用
文章里有fftw的详细导入教程,在打开开发人员命令提示符中,我的是下图(因为之前装过vs2015,但不影响,都能用)

C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比

在最后一步放的位置就是下图

C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比

未调用GPU的FFT代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include "fftw3.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <MMSystem.h>
#pragma comment(lib,"winmm.lib")
#define N 4000000

void main()
{
	int i;
	fftw_complex *din, *out;
	DWORD start, end;
	fftw_plan p;
	din = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*N);
	out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*N);
	if(din == NULL || out == NULL)
	{
		printf("Error:insufficient avaliable memory\n");
	}
	else
	{
		for(i=0; i<N; i++)
		{
			din[i][0] = i+1;
			din[i][1] = 0;
		}
	}
	start = timeGetTime();
	p = fftw_plan_dft_1d(N, din, out, FFTW_FORWARD, FFTW_ESTIMATE);
	fftw_execute(p);
	fftw_destroy_plan(p);
	fftw_cleanup();
	end = timeGetTime();
	printf("所用时间为%dms", end - start);
	//for(i=0; i<N; i++)
	//{
	//	printf("%f,%fi\n", din[i][0], din[i][1]);
	//}

	//printf("\n");
	//for(i=0; i<N; i++)
	//{
	//	printf("%f,%fi\n", out[i][0], out[i][1]);
	//}

	if(din != NULL) fftw_free(din);
	if(out != NULL) fftw_free(out);
	getchar();
}

同样数据量运算时间才一百多毫秒!!!啊!!
C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比

C语言:调用cufft库的GPU加速FFT

调用库速度那么快,那使用CUDA有没有库?好家伙我一搜果然有,这个cufft就是我要找的库,开始还找不到在哪里下载,找到个官网还只有linx版本的公开可以下载,到处去问人家博主,这个文章《CUFFT使用启蒙》一下子让我意识到这个cufft库就在CUDA里啊啊啊啊!!,我C盘一搜,果然有cufft.h这个文件,果断用上,代码主要是这个文章《FFTW cuFFT的使用记录》的,我改过的代码如下:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "cufft.h"
#include <string>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
#include <mmSystem.h>
#pragma comment(lib,"winmm.lib")
using namespace std;
//#include "Complex.cu"
#include<iostream>
#include <math.h>


//#include"fftw3.h"
#pragma comment(lib, "libfftw3-3.lib") // double版本
// #pragma comment(lib, "libfftw3f-3.lib")// float版本
// #pragma comment(lib, "libfftw3l-3.lib")// long double版本



#define CHECK(call)\
{\
	if ((call) != cudaSuccess)\
			{\
		printf("Error: %s:%d, ", __FILE__, __LINE__);\
		printf("code:%d, reason: %s\n", (call), cudaGetErrorString(cudaGetLastError()));\
		exit(1);\
			}\
}


const double PI = 3.141592653; 

//void test_FFTW();

int main() {

	const int NX = 800;
	const int fs = 4000000;
	double T = 2e-6;
	
	
	const int BATCH = 1;
	//DWORD Start, End;
	int bei = fs/NX;
	double timebei = T/NX;
	double aaa = 1.0/NX;
	double Lk = 1/T;
	//printf("%f\n", aaa);
	//printf("%.20lf",timebei);
	cufftHandle plan;  //创建句柄
	cufftComplex *data;
	cufftComplex *data_cpu;

	double t[NX]; 

	data_cpu = (cufftComplex *)malloc(sizeof(cufftComplex) * NX * BATCH);
	if (data_cpu == NULL) return -1;

	CHECK(cudaMalloc((void**)&data, sizeof(cufftComplex) * NX * BATCH));
	
	CHECK(cufftPlan1d(&plan, NX, CUFFT_C2C, BATCH)); //对句柄进行配置,主要是配置句柄对应的信号长度,信号类型,在内存中的存储形式等信息。
	//第一个参数就是要配置的 cuFFT 句柄;
    //第二个参数为要进行 fft 的信号的长度;
    //第三个CUFFT_C2C为要执行 fft 的信号输入类型及输出类型都为复数;
    //第四个参数BATCH表示要执行 fft 的信号的个数,新版的已经使用cufftPlanMany()来同时完成多个信号的 fft

	//Start = timeGetTime();
	//输入数据


	for (int i = 0; i < NX; ++i) 
	{
		t[i]=i*timebei;
		//printf("%.15lf\n", t[i]);

		data_cpu[i].x = cos(PI*8*0.25*fs/(3*T*T)*pow(t[i]-T/2.0, 3)+2*PI*0.25*fs*t[i]);
		data_cpu[i].y = sin(PI*8*0.25*fs/(3*T*T)*pow(t[i]-T/2.0, 3)+2*PI*0.25*fs*t[i]);
	}


	//for (int i = 0; i < NX; ++i) 
	//{
	//	//printf("%f  %f\n", data_cpu[i].x, data_cpu[i].y);
	//	double mo = sqrt(data_cpu[i].x * data_cpu[i].x + data_cpu[i].y * data_cpu[i].y);
	//	double time = i*timebei;
	//	printf("%.15f  %.20f\n", time, mo);
	//}

	//Start = timeGetTime();

	//数据传输cpu->gpu
	CHECK(cudaMemcpy(data, data_cpu, sizeof(cufftComplex) * NX * BATCH, cudaMemcpyHostToDevice));
	CHECK(cudaDeviceSynchronize());
	
	CHECK(cufftExecC2C(plan, data, data, CUFFT_FORWARD)); 
	//CHECK(cufftExecC2C(plan, data, data, CUFFT_INVERSE) != CUFFT_SUCCESS);
	//第一个参数就是配置好的 cuFFT 句柄;
    //第二个参数为输入信号的首地址;
    //第三个参数为输出信号的首地址;
    //第四个参数CUFFT_FORWARD表示执行的是 fft 正变换;CUFFT_INVERSE表示执行 fft 逆变换。
    //!!!需要注意的是,执行完逆 fft 之后,要对信号中的每个值乘以 1/N



	//数据传输gpu->cpu
	CHECK(cudaMemcpy(data_cpu, data, sizeof(cufftComplex) * NX * BATCH, cudaMemcpyDeviceToHost));
	CHECK(cudaDeviceSynchronize());

	for (int i = 0; i < NX; ++i) 
	{
		//printf("%f  %f\n", data_cpu[i].x, data_cpu[i].y);
		double mo = sqrt(data_cpu[i].x * data_cpu[i].x + data_cpu[i].y * data_cpu[i].y);
		//double Fre = i*F;
		
		printf("%d  %.15f\n", i*bei, mo*aaa);
	}

	//End = timeGetTime();
    //printf("变换用时: %dms\n", End - Start);

	cufftDestroy(plan);  //释放 GPU 资源
	cudaFree(data);

	//printf("CUFFT_FORWARD:\n");
	



	system("pause");
	return 0;

}

这个4*10^6序列长度运算时间只需要10毫秒!!!!真的如官网所说的提升十倍的速度,具体为啥人家的快……我也不知道,我只是一个可怜弱小的C代码小白
上面的代码输入的是我测试结果的非线性调频信号(NLFM)二次函数形式(对于这个信号函数的matlab代码如下),正经代码都在,只是注释掉了,按需更改即可。

输入信号NLFM的matlab代码:

%% NLFM

T = 2e-6;         %发射脉宽
fs = 400e6;       %采样频率
n = T*fs;         %采样点数
t = linspace(0,T,n);

f0 = 0.25*fs;%载频[1/16fs-1/4fs]
B = 0.25*fs; %带宽[1/20fs-1/4fs]
K = 8*B/(3*T^2);
s = exp(1i*pi*K*(t-T/2).^3+1i*2*pi*f0*t);

%% 绘图
figure(1);
plot(t,s);%画时域图
xlabel("t/s")

gnuplot安装画图,maltab编写的FFT运算结果对比

用gnuplot画出C代码的输出数据,我是看到这篇文章这么做gnuplot画图
在文章的最后,我就也去安装了个gnuplot来画图,安装包和教程网上找吧,我不贴了,用的时候把代码正确运行一遍后,就按他文章中下图运行,不过不用运行第一句tcc就行C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比
以NLFM信号为例(别问我为什么是这个,因为我最后测试的信号是这个)
输出结果如下图
C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比
matlab输出结果是:
C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比
是不是几乎一摸一样!!啊哈哈哈哈,我成功啦~~

matlab测试信号和测试时的坑

我总共用matlab测试了三种信号,原来c代码里用的等差数列看不出来,这三个matlab信号我贴在下面:

clear
%% 正弦信号

% fs=1000;%采样率
% f1=200;%信号频率
% T=1;%时宽1s
% n=round(T*fs);%采样点个数
% t=linspace(0,T,n);%时域横坐标
% x = 3*sin(2*pi*f1*t);%形成三频信号,注意第二个频率信号幅度为2,直流幅度为3

%% LFM

% T = 2e-6;         %发射脉宽
% fs = 400e6;       %采样频率
% n = T*fs;         %采样点数
% t = linspace(0,T,n);
% 
% f0 = 0.25*fs;%载频[1/16fs-1/4fs]
% B = 0.25*fs; %带宽[1/20fs-1/4fs]
% K = B/T;                                      %调频率
% %s = cos(2*pi*f0*t+2*pi*1/2*K*t.^2)+1i*sin(2*pi*f0*t+2*pi*1/2*K*t.^2);
% s = exp(1i*2*pi*f0*t+1i*2*pi*1/2*K*t.^2);     %LFM信号

%% NLFM

T = 2e-6;         %发射脉宽
fs = 400e6;       %采样频率
n = T*fs;         %采样点数
t = linspace(0,T,n);

f0 = 0.25*fs;%载频[1/16fs-1/4fs]
B = 0.25*fs; %带宽[1/20fs-1/4fs]
K = 8*B/(3*T^2);
s = exp(1i*pi*K*(t-T/2).^3+1i*2*pi*f0*t);

%% 绘图
figure(1);
plot(t,s);%画时域图
xlabel("t/s")
grid on
X = fft(s./(n));
%X = fftshift(fft(s./n)); %用fft得出离散傅里叶变换
aa = abs(X);
%f=linspace(0,T,n);
f=linspace(0,fs,n);%频域横坐标,注意奈奎斯特采样定理,最大原信号最大频率不超过采样频率的一半
figure(2)
plot(f,aa);%画双侧频谱幅度图
xlabel("f/Hz")
ylabel("幅度")
grid on

现在还是那个NLFM信号,自己改前面注释就行
首先我先测试了正弦信号,用前面调用cufft库的C代码,gnuplot画出的结果图,与maltab画的图对比。
这里要说明一下,C语言代码运算结果没有像matlab一样用fftshift做了对称变换,我matlab对应代码里已经改了,开始的时候让我奇怪的很,明明没有计算错误就是峰值位置不对,原来是matlab用了fftshift……无语
还有在写测试信号代码时,我算是明白了C代码的各种数值精度表示的区别,其中一个double类型除以double类型结果竟然都是大零蛋,我就在前面数值声明里除好,后面就是相乘的形式,1要改成1.0,就结果正确了,我也不知道专业的C代码大佬对于这种情况都是怎么处理的,我命名的变量名很垃圾凑合看吧,反正代码能跑……

第二个测试信号是线性调频信号(LFM),这个信号有用虚部表示,卡了我一天不知道C该怎么表示虚部,后来反应过来用欧拉公式改成实部和虚部都是三角函数的形式
原来matlab里LFM信号是这样

s = exp(1i*2*pi*f0*t+1i*2*pi*1/2*K*t.^2);     %LFM信号

拆成实部和虚部的形式就是这样

s = cos(2*pi*f0*t+2*pi*1/2*K*t.^2)+1i*sin(2*pi*f0*t+2*pi*1/2*K*t.^2);

在C里面就是data_cpu[i].x 和data_cpu[i].y赋值的时候分别对应实部和虚部就行,虚部不用加i,等于是实部和虚部分开运算了

……嗯……就写到这里吧,该吃晚饭了,罗嗦了好多……文章来源地址https://www.toymoban.com/news/detail-462739.html

到了这里,关于C语言使用CUDA中cufft函数做GPU加速FFT运算,与调用fftw函数的FFT做运算速度对比的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32】STM32F4调用DSP库实现FFT运算

    最近在整理之前的stm32笔记,打算把一些有价值的笔记发到CSDN分享一下。 在进行模拟/数字信号的转换过程中,当采样频率F大于信号中最高频率 fmax 的 2 倍时(F2*fmax),采样之后的数字信号完整地保留了原始信号中的信息。 设采样频率(单位时间可以采多少个信号样本)为 F

    2023年04月24日
    浏览(30)
  • CUDA编程:矩阵乘运算从CPU到GPU

    本文内容涉及到CUDA矩阵1D运算、2D运算、共享内存、CUBLAS的使用。 文中的全部code: https://github.com/CalvinXKY/BasicCUDA/tree/master/matrix_multiply V100上的测试对比: 运行内容“./matMul wA=1024 hA=256 wB=128 hB=1024” 矩阵 C = A x B的数学运算,是线性代数里面最基本的内容, 计算的基本公式如下

    2024年04月08日
    浏览(45)
  • GPU加速02:超详细Python Cuda零基础入门教程,没有显卡也能学!

    Python是当前最流行的编程语言,被广泛应用在深度学习、金融建模、科学和工程计算上。作为一门解释型语言,它运行速度慢也常常被用户诟病。著名Python发行商Anaconda公司开发的Numba库为程序员提供了Python版CPU和GPU编程工具,速度比原生Python快数十倍甚至更多。使用Numba进行

    2024年02月02日
    浏览(50)
  • Jetson Orin NX 开发指南(5): 安装 OpenCV 4.6.0 并配置 CUDA 以支持 GPU 加速

    Jetson 系列的开发板 CPU 性能不是很强,往往需要采用 GPU 加速的方式处理图像数据,因此本文主要介绍如何安装带有 GPU 加速的 OpenCV,其中 GPU 加速通过 CUDA 来实现。 参考博客 Ubuntu 20.04 配置 VINS-Fusion-gpu + OpenCV 4.6.0-CSDN博客Ubuntu 20.04 配置 VINS-Fusion-gpu + OpenCV 4.6.0https://blog.csdn.

    2024年02月04日
    浏览(39)
  • 如何调用GPU训练模型【详细教程1】——CUDA和cuDNN的安装

    目录 一、前言 二、安装CUDA、cuDNN和PyTorch CUDA的安装 cuDNN的安装 三、验证是否安装成功 在进行深度学习模型训练时,可以使用CPU训练,但通常比较慢,也可以采用GPU进行加速训练,从而缩短训练时间。 目前支持深度学习的显卡只有NIVDIA,AMD是不支持的,因此AMD显卡的用户不

    2024年02月13日
    浏览(31)
  • GPU版本pytorch的安装,配套环境python、Cuda、Anaconda安装和版本选择,及常见问题调用gpu返回false

    前言 :第一次装这个我也很懵,就想记录一下交流经验,这个安装最麻烦的是需要各个 版本 都需要 对应 。我也看了很多教程网上基本上安装都是cpu版本,就官网链接安装下来也是cpu版本,然后就不能调用显卡。 本教程使用python3.9、pytorch1.8.0、Cuda11.2版本、Cudnn8.8.1,这个

    2024年02月03日
    浏览(37)
  • 解决PyTorch无法调用GPU,torch.cuda.is_available()显示False的问题

    在Aliyun ECS上部署Demucs项目时,发现torch始终无法调用GPU。 python -c \\\"import torch;print(torch.cuda.is_available())\\\" 一直显示是False。 需要修改Demucs项目默认的 environment-cuda.yml ,覆盖指定这几个参数 完整文件如下: 如果之前已经用默认的yml文件创建了环境,需先把原来的环境删掉。修改

    2024年02月04日
    浏览(42)
  • Docker【部署 07】镜像内安装tensorflow-gpu及调用GPU多个问题处理Could not find cuda drivers+unable to find libcuda.so...

    Other than the name, the two packages have been identical since TensorFlow 2.1 也就是说安装2.1版本的已经自带GPU支持。 不同型号的GPU及驱动版本有所区别,环境驱动及CUDA版本如下: 在Docker容器中的程序无法识别CUDA环境变量,可以尝试以下步骤来解决这个问题: 检查CUDA版本:首先,需要确认

    2024年02月04日
    浏览(36)
  • 配置Cuda及Cudnn,在Anaconda创建虚拟环境,安装GPU版Pytorch,并在Jupyter noterbook及Pycharm中调用【极其详细】

    目录  一、配置Cuda及Cudnn (一)下载Cuda 1、查看电脑自带的Cuda版本 2、下载相应版本Cuda安装包 3、安装Cuda 4、配置变量 (二)下载Cudnn 二、创建虚拟环境 三、虚拟环境中安装GPU版Pytorch (一)有关环境的基本指令 (二)安装GPU版Pytorch 四、将虚拟环境在Jupyter noterbook及Pycha

    2024年04月28日
    浏览(40)
  • stm32F103RCT6使用FFT运算分析波形详解(细致教学)

    最近学校电赛队伍招新,出的招新题就是低频示波器的。之前一直没有弄懂FFT,借着这次机会实现了一下,做了一个小示波器 FFT原理简述 FFT,就是快速傅里叶变换,这个操作能够将时域信号转化成频域信号,然后对信号进行分析 这样说可能有点抽象。讲细点就是指能够直观

    2024年02月14日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包