三分钟搞懂CUDA和GPU编程 (三分钟搞懂比较优势原理)
1. 为什么使用 GPU 进行计算?
现代 GPU 是高度并行的处理器,设计用于同时处理大量数据。它们在能够分解为更小并行任务上表现出色,非常适合以下任务:
- 科学模拟
- 数据处理
- 机器学习
2. CUDA GPU 编程的关键概念
2.1 线程和块
CUDA 将计算分为并行运行的线程。线程组织成块,块组成网格。这种分层结构有助于管理并行性。
2.2 核函数
核函数是在 GPU 上运行并由各个线程执行的函数,是 CUDA 中并行计算的核心。
2.3 共享内存
共享内存是一个快速且低延迟的内存空间,块内的线程可以使用它来交换数据和协作。
2.4 全局内存
全局内存是所有线程都可以访问的主要内存空间,比共享内存慢,但容量更大。
2.5 网格和块维度
开发人员可以指定网格和块的维度来分割计算。优化这些维度对性能很重要。
3. CUDA GPU 编程的基本步骤
3.1 内存管理
使用
cudaMalloc
和
cudaMemcpy
等函数在 CPU 和 GPU 内存之间分配和传输数据。
3.2 核函数定义
编写将由每个线程执行的核函数。该函数应表达开发人员想执行的并行计算。
3.3 启动核函数
使用 符号指定网格和块的维度来在 GPU 上启动核函数。
3.4 同步
使用
cudaDeviceSynchronize
等同步函数确保所有 GPU 线程在继续之前完成工作。
4. CUDA GPU 编程的优势
- 提高计算速度,因为 CUDA 允许开发人员将计算任务并行化。
- GPU 在涉及大量计算的任务上表现优异,如图像处理、模拟、深度学习训练等。
- CUDA 提供专门针对各种任务进行优化的库,这样利用 GPU 进行加速更加简单。
5. 挑战和注意事项
- 在 CPU 和 GPU 内存之间传输数据可能会引入开销。应该尽量减少数据传输的次数和量,以提高程序的性能和效率。
- 块内的线程应遵循相似的执行路径以最大化效率。分歧行为可能导致性能下降。
6. 实际应用
- 科学领域:用于模拟、数值计算和建模
- 图像处理:加速图像滤波、视频编解码和计算机视觉算法
- 机器学习:许多深度学习框架利用 GPU 进行训练和推断
7. 总结
通过使用 CUDA 进行 GPU 编程,开发者可以利用 GPU 巨大的并行处理能力处理各种任务。通过理解 CUDA 的关键概念并遵循最佳实践,开发人员可以获得显著的性能提升,并加速从科学研究到机器学习等领域的计算密集型应用程序。
显卡中CUDA是什么及其应用介绍
CUDA(Compute Unified Device Architecture),显卡厂商NVidia推出的运算平台。 CUDA™是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。 它包含了CUDA指令集架构(ISA)以及GPU内部的并行计算引擎。
计算行业正在从只使用CPU的“中央处理”向CPU与GPU并用的“协同处理”发展。为打造这一全新的计算典范,NVIDIA®(英伟达™)发明了CUDA(Compute Unified Device Architecturem,统一计算设备架构)这一编程模型,是想在应用程序中充分利用CPU和GPU各自的优点。现在,该架构现已应用于GeForce®(精视™)、ION™(翼扬™)、Quadro以及Tesla GPU(图形处理器)上,对应用程序开发人员来说,这是一个巨大的市场。
在消费级市场上,几乎每一款重要的消费级视频应用程序都已经使用CUDA加速或很快将会利用CUDA来加速,其中不乏Elemental Technologies公司、MotionDSP公司以及LoiLo公司的产品。在科研界,CUDA一直受到热捧。例如,CUDA现已能够对AMBER进行加速。AMBER是一款分子动力学模拟程序,全世界在学术界与制药企业中有超过60,000名研究人员使用该程序来加速新药的探索工作。在金融市场,Numerix以及CompatibL针对一款全新的对手风险应用程序发布了CUDA支持并取得了18倍速度提升。Numerix为近400家金融机构所广泛使用。
CUDA的广泛应用造就了GPU计算专用Tesla GPU的崛起。全球财富五百强企业现在已经安装了700多个GPU集群,这些企业涉及各个领域,例如能源领域的斯伦贝谢与雪佛龙以及银行业的法国巴黎银行。随着微软Windows 7与苹果Snow Leopard操作系统的问世,GPU计算必将成为主流。在这些全新的操作系统中,GPU将不仅仅是图形处理器,它还将成为所有应用程序均可使用的通用并行处理器。
CUDA的应用
计算行业正在从只使用CPU的“中央处理”向CPU与GPU并用的“协同处理”发展。为打造这一全新的计算典范,NVIDIA(英伟达)发明了CUDA(Compute Unified Device Architecturem,统一计算设备架构)这一编程模型,是想在应用程序中充分利用CPU和GPU各自的优点。现在,该架构现已应用于GeForce(精视)、ION(翼扬)、Quadro以及Tesla GPU(图形处理器)上,对应用程序开发人员来说,这是一个巨大的市场。
CUDA产生的原因
随着显卡的发展,GPU越来越强大,而且GPU为显示图像做了优化。在计算上已经超越了通用的CPU。如此强大的芯片如果只是作为显卡就太浪费了,因此NVidia推出CUDA,让显卡可以用于图像计算以外的目的。
CUDA体系结构的组成
开发库:开发库是基于CUDA技术所提供的应用开发库。
运行期环境:运行期环境提供了应用开发接口和运行期组件,包括基本数据类型的定义和各类计算、类型转换、内存管理、设备访问和执行调度等函数。
驱动:CUDA-enable的GPU的设备抽象层,提供硬件设备的抽象访问接口。也就是需要安装有nVIDIA硬件的电脑上安装相应的驱动来实现CUDA通用运算。
如何学习cuda c?
1、CUDAC编写WindowsConsoleApplication
下面我们从一个简单的例子开始学习CUDAC。
打开VS,新建一个CUDAWinApp项目,项目名称为Vector,解决方案名称为CUDADemo。依次点击“确定”,“下一步”,选择Emptyproject。点击“Finished”。这样一个CUDA的项目就建成了。
右键点击Vector项目,依次选择“添加”、“新建项”、“代码”、“CUDA”。在名称中输入要添加的文件名。如。然后点击添加。
下面在文件里实现两个向量相加的程序。
//添加系统库
//添加CUDA支持
__global__voidVecAdd(float*A,float*B,float*C);
__host__voidrunVecAdd(intargc,char**argv);
intmain(intargc,char**argv)
runVecAdd(argc,argv);
CUT_EXIT(argc,argv);
__host__voidrunVecAdd(intargc,char**argv)
{//初始化host端内存数据
constunsignedintN=8;//向量维数
constunsignedintmemSize=sizeof(float)*N;//需要空间的字节数
float*h_A=(float*)malloc(memSize);
float*h_B=(float*)malloc(memSize);
float*h_C=(float*)malloc(memSize);
for(unsignedinti=0;i<N;i++)
{h_A[i]=i;h_B[i]=i;}
//设备端显存空间
float*d_A,*d_B,*d_C;
//初始化Device
CUT_DEVICE_INIT(argc,argv);
CUDA_SAFE_CALL(cudaMalloc((void**)&d_A,memSize));
CUDA_SAFE_CALL(cudaMalloc((void**)&d_B,memSize));
CUDA_SAFE_CALL(cudaMalloc((void**)&d_C,memSize));
CUDA_SAFE_CALL(cudaMemcpy(d_A,h_A,memSize,cudaMemcpyHostToDevice));
CUDA_SAFE_CALL(cudaMemcpy(d_B,h_B,memSize,cudaMemcpyHostToDevice));
VecAdd<<<1,N,memSize>>>(d_A,d_B,d_C);
CUT_CHECK_ERROR(Kernelexecutionfailed);
CUDA_SAFE_CALL(cudaMemcpy(h_C,d_C,memSize,cudaMemcpyDeviceToHost));
for(unsignedinti=0;i<N;i++)
{printf(%.0f,h_C[i]);}
free(h_A);free(h_B);free(h_C);
CUDA_SAFE_CALL(cudaFree(d_A));
CUDA_SAFE_CALL(cudaFree(d_B));
CUDA_SAFE_CALL(cudaFree(d_C));
__global__voidVecAdd(float*A,float*B,float*C)
//分配sharedmemory
extern__shared__floats_A[];
extern__shared__floats_B[];
extern__shared__floats_C[];
//从globalmemory拷贝到sharedmemory
constunsignedinti=threadIdx.x;
s_A[i]=A[i];
s_B[i]=B[i];
//计算
s_C[i]=s_A[i]+s_B[i];
//拷贝到globalmemory
C[i]=s_C[i];
由于这里不是讲CUDA编程的,关于它的编程模型已经超出了我要介绍的范围,您可以阅读《GPU高性能运算之CUDA》来获得CUDA编程模型的知识。
编译Vector项目,执行此项目后会得到图1如下输出:
图1Vector项目执行结果
2、CUDAC编写DLL模块
更多情况下的您的软件可能只是使用CUDA来实现一段程序的加速,这种情况下我们可以使用CUDAC编写DLL来提供接口。下面我们就将例1编译成DLL。
在刚才的CUDADemo解决方案目录下添加一个新的CUDA项目(当然您也可以重新建立一个解决方案)。项目名为VecAdd_dynamic。ApplicationType选为DLL,AdditionalOptions选择EmptyProject。
第一步,添加头文件,文件名最好与工程名同名,这样便于您的维护工作。这里我向项目中添加了VecAdd_dynamic.h,在此头文件中添加如下代码
#ifndef_VECADD_DYNAMIC_H_
#define_VECADD_DYNAMIC_H_
//并行计算N维向量的加法
__declspec(dllexport)voidVecAdd(float*h_A,float*h_B,float*h_C,intN);
第二步,添加cpp文件,文件名为VecAdd_,在此文件中添加如下代码
#includeVecAdd_dynamic.h
#ifdef_MANAGED
#pragmamanaged(push,off)
BOOLAPIENTRYDllMain(HMODULEhModule,DWORDul_reason_for_call,LPVOIDlpReserved)
returnTRUE;
#ifdef_MANAGED
#pragmamanaged(pop)
第三步,添加def文件,此文件的功能就是确保其它厂商的编译器能够调用此DLL里的函数。这一点非常关键,因为您的程序可能用到多个厂家的编译器。文件名为VecAdd_。向该文件中添加:
第四步,添加cu文件,文件名为VecAdd_。注意此文件最好直接添加到项目目录下,不要添加到源文件选项卡或其它已有的选项卡下。
在cu文件里添加如下代码,实现要导出的函数。
#if__DEVICE_EMULATION__
boolInitCUDA(void)
{returntrue;}
boolInitCUDA(void)
intcount=0;
cudaGetDeviceCount(&count);
if(count==0)
fprintf(stderr,Thereisnodevice./n);
returnfalse;
for(i=0;i<count;i++)
cudaDevicePropprop;
if(cudaGetDeviceProperties(&prop,i)==cudaSuccess)
if(i==count)
fprintf(stderr,ThereisnodevicesupportingCUDA./n);
returnfalse;
cudaSetDevice(i);
printf(CUDAinitialized./n);
returntrue;
__global__voidD_VecAdd(float*g_A,float*g_B,float*g_C,intN)
unsignedinti=threadIdx.x;
{g_C[i]=g_A[i]+g_B[i];}
voidVecAdd(float*h_A,float*h_B,float*h_C,intN)
if(!InitCUDA())
float*g_A,*g_B,*g_C;
unsignedintsize=N*sizeof(float);
CUDA_SAFE_CALL(cudaMalloc((void**)&g_A,size));
CUDA_SAFE_CALL(cudaMalloc((void**)&g_B,size));
CUDA_SAFE_CALL(cudaMalloc((void**)&g_C,size));
CUDA_SAFE_CALL(cudaMemcpy(g_A,h_A,size,cudaMemcpyHostToDevice));
CUDA_SAFE_CALL(cudaMemcpy(g_B,h_B,size,cudaMemcpyHostToDevice));
D_VecAdd<<<1,N>>>(g_A,g_B,g_C,N);
CUDA_SAFE_CALL(cudaMemcpy(h_C,g_C,size,cudaMemcpyDeviceToHost));
cudaFree(g_A);cudaFree(g_B);cudaFree(g_C);
第五步,如果您已经正确完成了以上四步,那么剩下的就只有编译,只要您用过VS,这一步就不需要我介绍了吧。成功之后,在您的解决方案文件目录下的Debug文件夹下会有一个VecAdd_文件。
3、在中使用CUDAC编写的DLL
下面介绍在托管程序中如何使用VecAdd_。
第一步,在上面的解决方案CUDADemo下添加一个C++/CLR的Windows窗体应用程序,工程名为NETDemo(当然您也可以重新建一个解决方案,工程名也是随意的)。
第二步,在窗体上添加一个按钮,名字随意,我将它的现实文本改为“调用CUDA_DLL”,给这个按钮添加click事件。我们的代码将在这个事件里添加调用VecAdd()的程序。在窗体上添加一个文本框用来显示调用输出的结果。
第三步,代码实现。为工程NETDemo添加一个头文件,我将它命名为Win32.h,这个文件中主要是实现VecAdd()函数的导入。在此文件中添加如下代码
#pragmaonce
namespaceWin32
usingnamespaceSystem::Runtime::InteropServices;
[DllImport(VecAdd_,EntryPoint=VecAdd,CharSet=CharSet::Auto)]
externCvoidVecAdd(float*h_A,float*h_B,float*h_C,intN);
在Form1.h中,#pragmaonce之后namespaceNETDemo之前添加以下代码。
#includeWin32.h
在button1_Click()中添加如下代码
float*h_A=(float*)malloc(N*sizeof(float));
float*h_B=(float*)malloc(N*sizeof(float));
float*h_C=(float*)malloc(N*sizeof(float));
for(inti=0;i<N;i++)
{h_A[i]=i;h_B[i]=i;}
Win32::VecAdd(h_A,h_B,h_C,N);
String^reslut;
for(inti=0;i<N;i++)
{reslut+=Convert::ToString(h_C[i])+,;}
this->textBox1->Text=Convert::ToString(reslut);
free(h_A);free(h_B);free(h_C);
第四步、执行NETDemo项目。点击“调用CUDA_DLL”,您会看到图3所示的结果
图3NETDemo运行结果
到现在为止您已经完全可以正确使用CUDA了。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。