目录

  • 任务介绍
    • 环境所需相关软件下载与安装
    • C语言:不调用库的GPU加速FFT代码
    • C语言:调用fftw库的未使用GPU的FFT代码
    • C语言:调用cufft库的GPU加速FFT
    • gnuplot安装画图,maltab编写的FFT运算结果对比
      • matlab测试信号和测试时的坑

任务介绍

时隔多年仍然逃不掉写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格式
如果在这篇文章第3.f步的项类型里没有找到CUDA C++的话,就右键 【工程】-【生成自定义】-勾选上CUDA 7.5就行,如下图

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

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

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

#define PI 3.1415926class 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,每个块中的线程数量 <= 1024const int N = 4000000;  // 数列大小const int bits = GetBits(N);DWORD Start, End;// 随机生成实数数列Complex *nums = (Complex*)malloc(sizeof(Complex) * N), *dNums, *dResult;  //申请一块内存,其大小是N个complex长度的总和。然后把这块内存的首地址强转成complex *指针变量类型,赋给*numsfor (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内存,拷贝数据到devicecudaMalloc((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++ 教程一:加速应用程序
再贴一个看的时候记的一个草稿纸……

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,但不影响,都能用)

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

未调用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 4000000void 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语言:调用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->gpuCHECK(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->cpuCHECK(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代码:

%% NLFMT = 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就行
以NLFM信号为例(别问我为什么是这个,因为我最后测试的信号是这个)
输出结果如下图

matlab输出结果是:

是不是几乎一摸一样!!啊哈哈哈哈,我成功啦~~

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信号%% NLFMT = 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,等于是实部和虚部分开运算了

……嗯……就写到这里吧,该吃晚饭了,罗嗦了好多……

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

  1. 单相逆变器S函数仿真,控制算法采用 S函数编写,在模型运行时调用S函数。 逆变器输出电压品质非常高

    单相逆变器S函数仿真,控制算法采用 S函数编写,在模型运行时调用S函数. 逆变器输出电压品质非常高! ID:1765668638115213幸福生活

  2. CUDA编程之快速入门-----GPU加速原理和编程实现

    转载:https://www.cnblogs.com/skyfsm/p/9673960.html CUDA(Compute Unified Device Architecture)的中文全称为计算统一 ...

  3. GPU编程 CUDA C++ 分子动力学模拟【GPU加速版】迷你代码

    分子动力学模拟对一个具有一定初始条件和边界条件且具有相互作用(分子力场molecular force feild)的多粒子系统的运动方程进行数值积分,得到系统在相空间(phase space)中的一条 ...

  4. php函数get和set,php中外部类调用_get函数和_set函数的方法

    本篇文章给大家分享的内容是关于php中外部类调用_get函数和_set函数的方法,有详细的代码,希望可以帮助到有需要的朋友. 外部调用类的私有属性 先来了解一下PHP类中的__get和__set函数 ...

  5. 深度学习Win10安装配置CUDA和cuDNN实现显卡GPU加速

    背景: 最近进入到深度学习的行列中,由于笔记本带有独显,并且在网上看到许多博客都说深度学习通过GPU加速可以提高训练的速度,所以我就在想我也不能浪费了我的独显,最后决定尝试配置CUDA和cuDNN来体 ...

  6. c语言想调用函数怎么弄43,如果想要调用这个函数,还应该怎么写

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 ** * CRC32.h * * 功能:CRC32校验计算 * 作者:hellovfp * 时间:2018.1.23 * 最后修改:2018.4.15 * ...

  7. Effective C++条款09:绝不在构造和析构过程中调用virtual函数

    Effective C++条款09:绝不在构造和析构过程中调用virtual函数(Never call virtual functions during construction or destruc ...

  8. 【Android 逆向】函数拦截原理 ( 通过修改 GOT 全局偏移表拦截函数 | 通过在实际被调用的函数中添加跳转代码实现函数拦截 )

    文章目录 一.通过修改 GOT 全局偏移表拦截函数 二.通过在实际被调用的函数中添加跳转代码实现函数拦截 一.通过修改 GOT 全局偏移表拦截函数 使用 GOT 全局偏移表 拦截函数 , 只需要将 G ...

  9. 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 远程调用 目标进程中 libc.so 动态库中的 mmap 函数 二 | 准备参数 | 远程调用 mmap 函数 )

    文章目录 一.准备 mmap 函数的参数 二.mmap 函数远程调用 一.准备 mmap 函数的参数 上一篇博客 [Android 逆向]Android 进程注入工具开发 ( 注入代码分析 | 远程调 ...

最新文章

  1. 1191: 冰法斗神龙 暴力枚举
  2. python中range和xrange的异同
  3. php mysql 内存溢出_关于MySQL的整型数据的内存溢出问题的应对方法_MySQL
  4. Python安装工具包踩过的坑
  5. python逻辑表达式3+45and_python入门到精通(一)| python基础语法与各种运算符的使用...
  6. insert into 插入空值_常州插入式涡轮流量计旋进旋涡流量计价格-玮偌自控系统...
  7. 扩展GeoServer数据源
  8. PostgreSQL命令导入sql文件
  9. Java工程师需要掌握哪些知识和专业技能呢?
  10. java基础之ArrayLis类
  11. php收费视频网站实现,超好影视网站PHP源码,一键采集视频资源,对接第三方免签码支付,能设置每个视频能否收费...
  12. java 递归层级拼接_使用递归方法拼接层级树
  13. 电商后台管理系统技术总结(黑马)
  14. Job for DmServiceDMSERVER.service failed because the control process exited with error code. Se
  15. Vue+DataV+Echarts组件创建炫酷科技大屏~(注释多多)
  16. [matlab]利用cftool进行曲线拟合
  17. 牛顿下山法(C语言实现)
  18. Pandas初体验——头歌平台答案
  19. 解决二义性问题解决 java_C++中常见的两种二义性问题及其解决方式
  20. 最长公共子串与最长公共子序列

热门文章

  1. 这是袁隆平40年前稻田教学的样子!网友用AI修复珍贵视频寄托哀思
  2. linux教程超详细,linux搭建node超详细教程
  3. 【JoJo的摄影笔记】相机分类新约
  4. Linux内核 之 IPC概述
  5. 网易云课堂 Service Worker 运用与实践
  6. java的入口函数_java入口函数有几种
  7. 云启资本陈昱:开源软件公司市场估值已超平均市值 20 倍
  8. NCAE(全国工业和信息化应用人才考试 )-- 服务外包 JAVA 软件开发复习整理(一)
  9. 如何从挫败感到成就感,身为程序员每天都在经历这些!
  10. 水生植物的Java莫斯