NIVIDIA高性能计算CUDA笔记(三) cuFFT的简介及实现案例

NIVIDIA高性能计算CUDA笔记(三) cuFFT的简介及实现案例

1. cuFFT库的简介(Introduction of cuFFT libaray)

​ Fourier变换是数字信号处理领域一个很重要的数学变换,它用来实现将信号实现将信号从时域到频域的变换,在物理学、数论、组合数学、信号处理、概率、统计、密码学、声学、光学等领域有广泛的应用。离散傅里叶变换(Discrete Fourier Transform,DFT)是连续傅里叶变换在离散系统中的表示形式,由于DFT的计算量很大,因此在很长一段时间内其应用受到了很大的限制。20世纪60年代(1965年)由Cooley和Tukey提出了快速傅里叶变换(Fast Fourier Transform,FFT)算法,它是DFT的快速算法,使得离散傅里叶变换和卷积这类难度很大的计算工作的复杂度从N2量级降到了Nlog2N量级,大大提高了DFT的运算速度,从而使DFT在实际应用中得到了广泛的应用。

​ cuFFT是NVIDIA提供的GPU加速的Fourier变换FFT库,能极大提升涉及FFT计算的科学计算、信号处理和深度学习等任务的速度。下表概括了器主要特征和应用场景:

cuFFT的特征具体描述
基本功能提供GPU加速的1D、2D、3D复数/实数FFT计算
核心优势相比CPU实现,利用GPU并行性可获得显著加速
编程接口提供类似的FFTW的API,便于熟悉CPU FFT的用户迁移
高级功能支持批量执行、流异步、半/单/双精度、多GPU计算
主要应用领域深度学习、计算物理学、医学成像、信号处理等

2. 基于cuFFT库的Fourier变换步骤(workflow of Fourier Transform based cuFFT)

在CUDA上进行傅里叶变换一般需要做以下几步工作:

  1. 在主机端,准备输入数据;
  2. 在GPU设备端上分配内存,并将数据从主机复制到设备;(cudaMalloc,cudaMemcpy的接口 )
  3. 创建一个\(plan\), 调用函数\(cufftPlane1D/cufftPlane2D/cufftPlan3D\)可以创建一个简单的Fourier变换。调用函数\(cufftPlanMany\)则可以创建支持更多配置操作的变换计划。
    • \(cufftPlan1d()\): 针对单个1维信号
    • \(cufftPlan2d()\):针对单个2维信号
    • \(cufftPlan3d()\):针对单个3维信号
  4. 执行\(plane\)。这一步可以使用\(cufftExecC2C()\)\(cufftExecR2C()\)\(cufftExecC2R()\)等函数完成上一步完成\(plane\)的计算任务。
  5. 执行完成以下若不再需要该\(plan\),则调用\(cufftDestroy()\)函数销毁该\(plan\)及为其分配的计算资源。

3. cuFFT的傅里叶变换API接口类型(Fourier Transform Types)

\(cuFFT\)库实现了三种不同类型的Fourier变换接口分为:\(C2C\)(复数变换到复数),\(C2R\)(复数到实数),\(R2C\)(实数到复数)。本质上,这三种转换都可以被看做是复数域到复数域的变换,之所以这样划分,其最主要的考量是性能因素。例如,在一般的数字信号处理中,输入数据是一些离散的实数域上的采样点,这时候对它们做Fourier变换实际上就是\(R2C\),根据埃尔米特对称性(Hermitian symmetry),变换\(X_k=X_{N-k}^{*}\),\(*\)代表共轭复数。\(cuFFT\)的傅里叶变换则利用了这些冗余,将计算量降到最低。

​ 变换执行函数的单精度和双精度版本分别定义如下:

API描述
\(cufftExecC2C()/cufftExecZ2Z()\)单精度/双精度浮点数复数域到复数域的傅里叶变换
\(cufftExecR2C()/cufftExecD2Z()\)单精度/双精度浮点数实数域到复数域的傅里叶变换(正变换)
\(cufftExecC2R()/cufftExecZ2D()\)单精度/双精度浮点数复数域到实数域的傅里叶变换(逆变换)

4. 数据布局(Data Layout)

\(CUFFT\)库保含若干种数据类型,对于复数有\(cufftComplex/cufftDoubleComplex\)两种数据类型,对于实数则分别有\(cufftReal/cufftDouble\)两种数据类型 。

​ 根据转换结果的存储位置不同,\(FFT\)变换可分为就地变换(\(in-place\))和外部变换(\(out-place\)),前者直接在输入数据进行变换,而后者则会将变换后结果存入新的存储器地址。

​ 就地转换(\(in-place\)) 支持数据的两种布局:\(native\)\(padded\),前者用于获取最佳性能,而后者则用于与FFTW兼容。

​ 在\(padded\)布局中输出信号的开始地址与输入信号一样,换句话说,实数域到复数域变换的输入数据和复数域到实数域的输出数据必须被填充。在native布局中则没有填入要求。

​ 输入数据和输出数据的尺寸总结如下:

FFT typeinput data sizeoutput data size
\(C2C\)\(X \space cufftComplex\)\(X \space cufftComplex\)
\(C2R\)\([\frac{X}{2}]+1 \space cufftComplex\)\(X\)\(cufftReal\)
\(R2C^{*}\)\(X\)\(cufftReal\)\([\frac{X}{2}]+1 \space cufftComplex\)

5. 单个一维信号的FFT变换代码实现(One Dimension SIgnal FFT Transfrom)

在本次测试代码中:首先生成一维的随机信号,利用cufft 先进行正变换,然后逆变换,并判定逆变换后结果与原输入信号判断是否相等。

/* by 01022.hk - online tools website : 01022.hk/zh/utf8.html */ #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 = 0; 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 = 0; 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 - 1) / 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, 0, 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) * 1000 / CLOCKS_PER_SEC; cout << "时间为 " << duration << " ms" << endl; cufftDestroy(plan); // 销毁句柄 cudaFree(data_dev); // 释放空间 cout << IsEqual(data_Host, resultIFFT, NX) << endl; return 0; }

其中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\);

输出结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1124420.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

旋转曲面接缝处问题

想要实现曲面纹理贴图功能&#xff0c;但是发现曲面表面一旦偏移&#xff0c;接缝处就会出现明显缝隙一开始想通过动态偏移的方式&#xff0c;根据纹理偏移的高度相应的缩小旋转半径。细想发现由于纹理不同&#xff0c;实际偏移的高度也不同。这会导致旋转曲面接不上后面了解到…

Langchain 太重?试试 Google ADK!搭建 Agent 新思路,上下文管理效率翻倍,教程来了!

Agent 的状态数据分两种&#xff1a;会话内的临时上下文和跨会话的长期知识。 前者是“用户刚才说了什么”、“工具返回了什么结果”&#xff0c;会话结束就该清空。后者是“用户三个月前的购买记录”、“历史工单的解决方案”&#xff0c;需要持久化并在未来对话中智能召回。…

Dakota: Design Analysis Kit for Optimization and Terascale Applications

文章目录一、Dakota 核心功能介绍1. **优化&#xff08;Optimization&#xff09;**2. **不确定性量化&#xff08;UQ&#xff09;**3. **参数研究&#xff08;Parameter Studies&#xff09;**4. **模型校准与验证&#xff08;Calibration & Validation&#xff09;**二、…

省电费之外,这些隐藏价值让企业稳赚不赔!

工厂主们常为日益攀升的电费成本而辗转难眠&#xff0c;特别是随着生产规模扩大&#xff0c;高峰期电价飙升的压力如影随形。储能系统&#xff0c;这个被称作 "工业充电宝" 的创新方案&#xff0c;已悄然成为企业降本增效的关键助力。它不仅能为工厂省下大笔电费&…

互联网大厂Java求职面试实录:从Spring Boot到微服务架构的技术深潜

互联网大厂Java求职面试实录&#xff1a;从Spring Boot到微服务架构的技术深潜 本文通过一个互联网大厂Java求职者谢飞机与面试官的三轮面试问答&#xff0c;深入探讨Java核心技术栈及相关业务场景&#xff0c;帮助读者系统了解Java面试中常见的技术点。面试覆盖Spring Boot、微…

别再说RAG过时了!Context Engineering系列一:掌握这10个上下文处理技巧,效果翻倍!

RAG效果不及预期&#xff0c;试试这10个上下文处理优化技巧 **对大部分开发者来说&#xff0c;搭一个RAG或者agent不难&#xff0c;怎么把它优化成生产可用的状态最难。 在这个过程中&#xff0c;检索效率、准确性、成本、响应速度&#xff0c;都是重点关注问题。 那么&…

LangChain 杀疯了!DeepAgents 横空出世,长任务稳健不崩,高可控简直无敌!

任务规划文件系统访问子agent委托 现如今&#xff0c;Agent 所需要执行的任务长度每几个月翻一番&#xff0c;长周期任务通常涉及数十次工具调用&#xff0c;这会带来成本和可靠性方面的问题。 那么&#xff0c;要如何解决&#xff1f; deepagents 是 LangChain 推出的开源框…

大模型多Agent实战教程(非常详细):Agno与LangGraph全方位对比,从原理到生产部署全解析!

今天还是聊聊生产级agent怎么搭这回事。 前面几期内容&#xff0c;我们聊了agent 常见的坑有哪些&#xff0c;memory怎么管理&#xff0c;还有一些rerank细节&#xff0c;今天从部署层面看看怎么选一个不错的agent框架。 现如今&#xff0c;针对复杂场景&#xff0c;多agent架…

高级DoS攻击技术深度解析:Slowloris、SYN Flood与Hping3实战

前言 在上一篇文章中&#xff0c;我们介绍了DoS/DDoS攻击的基础理论以及SSL洪水、慢速POST等攻击技术。本文将继续深入探讨更多高级攻击手段&#xff0c;包括经典的Slowloris攻击、SYN Flood洪水攻击、DNS放大攻击&#xff0c;以及强大的Hping3工具的多种应用场景。这些技术是…

(85页PPT)以人为本创新驱动构建未来校园智慧后勤云平台(附下载方式)

篇幅所限&#xff0c;本文只提供部分资料内容&#xff0c;完整资料请看下面链接 &#xff08;85页PPT&#xff09;以人为本创新驱动构建未来校园智慧后勤云平台.pptx_PPT格式医疗信息化方案资源-CSDN下载 资料解读&#xff1a;以人为本创新驱动构建未来校园智慧后勤云平台 详…

嵌入式现代C++教程: 构造函数优化:初始化列表 vs 成员赋值

构造函数优化&#xff1a;初始化列表 vs 成员赋值 在嵌入式 C 项目中&#xff0c;我们很容易把精力放在“看得见”的地方&#xff1a;中断、DMA、时序、缓存命中率、Flash/RAM 占用……而对于构造函数这种“看起来只执行一次”的代码&#xff0c;往往下意识地放松了警惕。 但实…

别再给OpenAI送钱了!大模型自主化部署全方案,本地部署流程详解,省钱又安全!

“ 模型本地部署是运维人员的基本技能&#xff0c;也是开发人员的基本技能。” 在大模型应用中&#xff0c;数据安全问题是很多企业关注的重点&#xff0c;特别是政务&#xff0c;金融&#xff0c;医疗等领域&#xff0c;对数据安全性有着更高的要求。 因此&#xff0c;这时使…

加密数据传输技术

威胁场景 假设攻击者控制的Kali系统被入侵&#xff0c;需要通过加密方式传输敏感数据以避免被防火墙拦截。 Base64文本传输 场景1&#xff1a;Linux到Windows 目标机器&#xff08;Linux&#xff09;&#xff1a; cat /etc/passwd | base64 | nc -nv [黑客IP] 3333 -q 1攻击者&…

链表专题(九):应用篇的无冕之王——「LRU 缓存机制」

场景想象&#xff1a; 你有一个书架&#xff08;缓存&#xff09;&#xff0c;容量有限&#xff08;比如只能放 3 本书&#xff09;。 规则是 “最近最少使用 (Least Recently Used)” 淘汰&#xff1a; 读取&#xff1a;如果你读了一本书&#xff0c;它就变得“新鲜”了&…

嵌入式现代C++:移动语义不是玄学,是资源转移的工程实践

嵌入式现代C&#xff1a;移动语义不是玄学&#xff0c;是资源转移的工程实践 假设你在写一个USB数据传输层&#xff0c;需要把一个4KB的DMA缓冲区从接收队列传递到处理线程。你可能会这样写&#xff1a; class DMABuffer {std::array<uint8_t, 4096> data;size_t length;…

大模型Agent实战教程(非常详细):深入理解ReAct架构,彻底搞懂稳定性难题!

“ 大模型的能力有限&#xff0c;因此在智能体处理复杂任务时&#xff0c;我们需要通过提示词告诉模型复杂任务的处理方法。” 最近在研究模型部署和Langchain新版本框架时&#xff0c;突然想到一个问题&#xff0c;就是ReAct Agent智能体问题。 ReAct Agent智能体的运行原理…

重塑安全认知:图解物理与环境安全如何托起整个信息安全“金字塔”

信息安全&#xff1a;物理与环境安全技术. 传统上的物理安全也称为 实体安全 &#xff0c;是指包括 环境、设备和记录介质在内的所有支持网络信息系统运行的硬件的总体安全&#xff0c;是网络信息系统安全、可靠、不间断运行的基本保证&#xff0c;并且确保在信息进行加工处理…

Context Pruning全攻略:RAG效果提升的关键,带你从零掌握高质量上下文剪枝技术!

Context Pruning如何结合rerank&#xff0c;优化RAG上下文&#xff1f; 现如今&#xff0c;LLM的上下文窗口长度正在经历爆发式增长。 翻开LLM Leaderboard&#xff0c;可以发现顶级模型的上下文长度已经陆续突破了1M tokens&#xff0c;并且这个数字还在不断刷新。 但问题也…

如何避免性能测试的常见陷阱

性能测试的核心价值与挑战 性能测试是软件质量保障的关键环节&#xff0c;旨在评估系统在高负载、高并发下的响应能力、稳定性和可扩展性。对于测试从业者而言&#xff0c;它能暴露潜在瓶颈&#xff08;如数据库延迟或代码低效&#xff09;&#xff0c;预防线上故障。然而&…

深度测评10个一键生成论文工具,专科生毕业论文轻松搞定!

深度测评10个一键生成论文工具&#xff0c;专科生毕业论文轻松搞定&#xff01; AI 工具的崛起&#xff0c;让论文写作不再难 随着人工智能技术的不断进步&#xff0c;越来越多的 AI 工具开始进入学术写作领域&#xff0c;为学生和研究人员提供了强大的辅助支持。尤其是在降低 …