CPU, GPU, CUDA, CUDNN总结

07 July 2018

CPU:

Central Processing Unit,中央处理器,是一块超大规模的集成电路,是一台计算机的运算核心和控制核心。

显卡:

Graphics card,全称显示接口卡,又称显示适配器,是电脑进行数模信号转换的设备,承担输出显示图形的任务。

GPU:

Graphics Processing Unit,图形处理器。显卡的处理器称为图形处理器(GPU)

CUDA:

Compute Unified Device Architecture,NVIDIA创造的一个并行计算平台和编程模型,它利用图形处理器(GPU)的编程能力,实现计算性能的显著提高。CUDA™是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。CUDA工具包为C和C++开发人员构建GPU加速应用程序提供全面的开发环境。CUDA工具包中包括一个针对NVIDIA GPU和数学库的编译器,以及用于调试和优化程序性能的各种工具。CUDA中包括很多GPU加速库和开发工具

GPU加速库:快速傅里叶变换,基本线性代数子例程,随机数生成,图像与视频处理单元,CUDA数学库等

开发工具: NVIDIA CUDA C/C++编译器,Nsight集成开发环境,视觉分析器等

CUDA程序(.cu)长这样:

#include "cuda_runtime.h"  
#include "device_launch_parameters.h"  
  
#include <stdio.h>  
  
cudaError_t addWithCuda(int *c, const int *a, const int *b, size_t size);  
  
__global__ void addKernel(int *c, const int *a, const int *b)  
{  
    int i = threadIdx.x;  
    c[i] = a[i] + b[i];  
}  
  
int main()  
{  
    
    // cudaThreadExit must be called before exiting in order for profiling and  
    // tracing tools such as Nsight and Visual Profiler to show complete traces.  
    cudaStatus = cudaThreadExit();  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaThreadExit failed!");  
        return 1;  
    }  
  
    return 0;  
}  
  
// Helper function for using CUDA to add vectors in parallel.  
cudaError_t addWithCuda(int *c, const int *a, const int *b, size_t size)  
{  
    int *dev_a = 0;  
    int *dev_b = 0;  
    int *dev_c = 0;  
    cudaError_t cudaStatus;  
  
    // Choose which GPU to run on, change this on a multi-GPU system.  
    cudaStatus = cudaSetDevice(0);  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaSetDevice failed!  Do you have a CUDA-capable GPU installed?");  
        goto Error;  
    }  
  
    // Allocate GPU buffers for three vectors (two input, one output)    .  
    cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaMalloc failed!");  
        goto Error;  
    }  
  
    cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaMalloc failed!");  
        goto Error;  
    }  
  
    cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaMalloc failed!");  
        goto Error;  
    }  
  
    // Copy input vectors from host memory to GPU buffers.  
    cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaMemcpy failed!");  
        goto Error;  
    }  
  
    cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaMemcpy failed!");  
        goto Error;  
    }  
  
    // Launch a kernel on the GPU with one thread for each element.  
    addKernel<<<1, size>>>(dev_c, dev_a, dev_b);  
  
    // cudaThreadSynchronize waits for the kernel to finish, and returns  
    // any errors encountered during the launch.  
    cudaStatus = cudaThreadSynchronize();  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaThreadSynchronize returned error code %d after launching addKernel!\n", cudaStatus);  
        goto Error;  
    }  
  
    // Copy output vector from GPU buffer to host memory.  
    cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);  
    if (cudaStatus != cudaSuccess) {  
        fprintf(stderr, "cudaMemcpy failed!");  
        goto Error;  
    }  
  
Error:  
    cudaFree(dev_c);  
    cudaFree(dev_a);  
    cudaFree(dev_b);  
      
    return cudaStatus;  
}  

可以看到cuda程序和c程序并无区别,只是多了一些’cuda’开头的一些库函数,和一些特殊声明的函数。

cuda程序用nvcc进行编译,用gcc编译不能通过。

CUDNN:

CUDA Deep Neural Network library,是用于深度神经网络的GPU加速库。cuDNN为标准例程提供了高度优化的例程,包括前向卷积,后向卷积,池化,规范化和激活层等。全球深度学习研究人员和框架开发人员依靠cuDNN实现高性能GPU加速。它允许他们专注于训练神经网络和开发软件应用程序,而不是花时间在底层的GPU性能调优上。使用cuDNN加速的深度学习框架包括:Caffe2,MATLAB,Microsoft认知工具包,Tensorflow,Theano和PyTorch。

显卡的历史

Nvidia是现在最大的独立显卡芯片生产销售商,包括Geforce系列,专业工作站的Quadro系列,超级计算的Tesla系列。AMD是世界上第二大的独立显卡芯片生产销售商。

它是为了视频游戏而产生的,在三维游戏中常常出现的一类操作是对海量数据进行相同的操作,如:对每一个顶点进行同样的坐标变换,对每一个顶点按照同样的光照模型计算颜色值。GPU的众核架构非常适合把同样的指令流并行发送到众核上,采用不同的输入数据执行。

在2003-2004年左右,图形学之外的领域专家开始注意到GPU与众不同的计算能力,开始尝试把GPU用于通用计算(即GPGPU)。之后NVIDIA发布了CUDA,AMD和Apple等公司也发布了OpenCL,GPU开始在通用计算领域得到广泛应用,包括:数值分析,海量数据处理(排序,Map-Reduce等),金融分析等等。

CPU和GPU

CPU和GPU之所以大不相同,是由于其设计目标的不同,CPU需要很强的通用性来处理不各种不同的数据类型,同时又需要逻辑判断,从而导致大量的分支跳转和中断的处理。这些使得CPU的内部结构异常复杂。而GPU面对的则是类型高度统一的、相互无依赖的大规模数据和不需要被打断的纯净计算环境。 于是CPU和GPU就呈现出非常不同的结构(示意图):

其中绿色的是计算单元,橙红色的是存储单元,橙黄色的是控制单元。

GPU采用了数量众多的计算单元和超长的流水线,但只有非常简单的控制逻辑并省去了Cache。而CPU不仅被Cache占据了大量空间,而且还有复杂的控制逻辑和诸多优化电路,相比之下计算能力只是CPU很小的一部分。

CPU虽然有多核,但总数没有超过两位数,每个核都有足够大的缓存和足够多的数字和逻辑运算单元,并辅助有很多加速分支判断甚至更复杂的逻辑判断的硬件;GPU的核数远超CPU,被称为众核,每个核拥有的缓存大小相对较小,数字逻辑运算单元也少而简单。