[CUDA] CUDA to DL

又是一枚祖国的骚年,阅览做做笔记:http://www.cnblogs.com/neopenx/p/4643705.html

这里只是一些基础知识。帮助理解DL tool的实现。

“这也是深度学习带来的一个全新领域,它要求研究者不仅要理论强,建模强,程序设计能力也要过硬,不能纸上谈兵。”

  • CUDA的广泛应用造就了GPU计算专用Tesla GPU的崛起。
  • 随着显卡的发展,GPU越来越强大,而且GPU为显示图像做了优化。在计算上已经超越了通用的CPU。如此强大的芯片如果只是作为显卡就太浪费了,因此NVidia推出CUDA,让显卡可以用于图像计算以外的目的
  • 目前只有G80、G92、G94、G96、GT200、GF100、GF110、GK100、GK104、GK107平台(即GeForce 8~Gecorce GTX780Ti)的NVidia显卡才能使用CUDA,工具集的核心是一个C语言编译器。G80中拥有128个单独的ALU(Arithmetic Logic Unit,算术逻辑单元),因此非常适合并行计算,而且数值计算的速度远远优于CPU。

Tesla GPU

Ref: http://bbs.gpuworld.cn/forum.php?mod=viewthread&tid=199
A. GeForce系列GPU追求“速度”,并不会对“数据正确性”进行“再确认”,因为“显示”就算有1%~2%的错误,也无伤大雅,反正刷屏速度60Hz,肉眼也分辨不出,错就错了也无所谓
B. 但对于“运算”需求,是容不得丝毫错误的,必须达到99.999999999....%正确率的,就有非常高的要求,这也是Tesla GPU必须采用ECC显存,以确保运算正确率的原因,导致价格比GeForce高出很多。

如果,你对“数据正确性”要求不高,那 GeForce 卡绝对会让你很开心。但如果你的计算结果容不下一点点错误,那还是得咬着牙选择 Tesla 专业卡,否则你还要花更多成本去面对难以预期的风险。

最后的caffe性能对比,自觉脑补。

OpenCL

OpenCL(全称Open Computing Language,开放运算语言)是第一个面向异构系统通用目的并行编程的开放式、免费标准,也是一个统一的编程环境,便于软件开发人员为高性能计算服务器、桌面计算系统、手持设备编写高效轻便的代码,而且广泛适用于多核心处理器(CPU)、图形处理器(GPU)、Cell类型架构以及数字信号处理器(DSP)等其他并行处理器,在游戏、娱乐、科研、医疗等各种领域都有广阔的发展前景。


CUDA到底是个什么?

为了泛型编程(C、C++、Fortran多语言)、以及榨取更多的计算力,NVIDIA对OpenCL进行的改装,贴合自己的GPU硬件架构,量身定做出CUDA。

较游戏程序员不同,CUDA程序员主要工作,就是把握硬件架构,在算法理论时间复杂度下,将算法串行执行体系,改组为并行执行体系。(能并行的并行化)

NVIDIA为它在不同成长阶段卖出的产品,规定了计算能力体系:

  1. 计算能力1.0是跑CUDA的最低条件,这一时期的代表作是8800GT家族。
  2. Fermi架构的计算能力是2.0,
  3. Kepler是3.0,
  4. Maxwell是4.0。

GPU会按照负载均衡的原则,将任务平均分至各个SM阵列 <---- Stream Multiprocessors(流多处理器),民间多译为SM计算阵列

对于每个SM阵列,就调度它手下那一伙CUDA核心干活。流处理器(SP)改名为"CUDA核心"。

2.

由于每个SM阵列的CUDA核心有限,NVIDIA规定,Fermi架构,每个SM最多并行执行1024个线程。

当然,实际任务中,每个SM会分到几百万个线程,这时候,就只能小部分并行,然后再串行了。

  • Fermi 1.0架构,官方设计是16组SM,512SP,然而旗舰GTX480最后只弄出了15组,480SP,顺次阉割出GTX470、GTX460。
  • Fermi 2.0架构,旗舰GTX580,总算达到设计图要求,达到了16组,512SP,顺次阉割出了GTX570,GTX560。

3.

Kepler架构最大变化在于, 对每个SM阵列,将SP数量扩大到6倍,达到192SP。谓之曰SMX阵列

每个SMX阵列,包含192个CUDA核心,单次并行吞吐量是2048个线程。

  • Kepler 1.0架构,官方设计是15组SM,2880SP,然而旗舰GTX580最后只弄出了8组,1536SP,顺次阉割出GTX570、GTX560。
  • Kepler 2.0架构,旗舰GTX680,总算达到设计图要求,达到了15组,2880SP,顺次阉割出了GTX670,GTX660。

特别版,GTX Titan Z,直接把两块GK110并在一起,合出了30组,5760SP,同时支持双精度浮点计算。

其阉割掉双精度之后,就是GTX690。

  

值得一提的是,GTX游戏卡直接把双精度阉割掉了,因为只有Tesla做科学计算的时候,才会用双精度浮点运算。

4.

Maxwell架构是老黄的无奈之举。因为台积电把20nm工艺让给了ARM系(Apple和高通)。

还是基于28nm的Maxwell,继续在SM上大刀阔斧闹改革,将192SP降低为128SP,谓之曰SMM阵列

Maxwell 最新架构,官方设计是16组SM,2048SP,为旗舰GTX980,顺次阉割出GTX970,GTX960。

特别版,GTX TitanX,24组SM,3072SP,较之TitanZ,阉掉了双精度浮点数支持。

TitanX是老黄在GTC 2015向DL界主推的一块民用卡,因为DL无需高精度浮点,用Tesla太奢侈。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
    int deviceCount;
    cudaGetDeviceCount(&deviceCount);
    int dev;
    for (dev = 0; dev < deviceCount; dev++)
    {
        cudaDeviceProp deviceProp;
        cudaGetDeviceProperties(&deviceProp, dev);
        if (dev == 0)
        {
            if (/*deviceProp.major==9999 && */deviceProp.minor = 9999&&deviceProp.major==9999)
                printf("\n");

        }
        printf("\nDevice%d:\"%s\"\n", dev, deviceProp.name);
        printf("Total amount of global memory                   %u bytes\n", deviceProp.totalGlobalMem);
        printf("Number of mltiprocessors                        %d\n", deviceProp.multiProcessorCount);
        printf("Total amount of constant memory:                %u bytes\n", deviceProp.totalConstMem);
        printf("Total amount of shared memory per block         %u bytes\n", deviceProp.sharedMemPerBlock);
        printf("Total number of registers available per block:  %d\n", deviceProp.regsPerBlock);
        printf("Warp size                                       %d\n", deviceProp.warpSize);
        printf("Maximum number of threada per block:            %d\n", deviceProp.maxThreadsPerBlock);
        printf("Maximum sizes of each dimension of a block:     %d x %d x %d\n", deviceProp.maxThreadsDim[0],
            deviceProp.maxThreadsDim[1],
            deviceProp.maxThreadsDim[2]);
        printf("Maximum size of each dimension of a grid:       %d x %d x %d\n", deviceProp.maxGridSize[0], deviceProp.maxGridSize[1], deviceProp.maxGridSize[2]);
        printf("Maximum memory pitch :                          %u bytes\n", deviceProp.memPitch);
        printf("Texture alignmemt                               %u bytes\n", deviceProp.texturePitchAlignment);
        printf("Clock rate                                      %.2f GHz\n", deviceProp.clockRate*1e-6f);
    }
    printf("\nTest PASSED\n");
    getchar();
}

Comment:

deviceProp.name为GPU名字,如果没有GPU则会输出 Device Emulation 
deviceProp.totalGlobalMem返回的是全局储存器的大小,对大数据或一些大模型计算时显存大小必须大于数据大小,如图返回的是2GB的存储大小, 
deviceProp.multiProcessorCount返回的是设备中流多处理器(SM)的个数,流处理器(SP)的个数SM数×每个SM包含的SP数,其中帕斯卡为每个SM,64个SP,麦克斯韦为128个,开普勒为192个,费米为32个, 
deviceProp.totalConstMem返回的是常数储存器的大小,如同为64kB 
deviceProp.sharedMemPerBlock返回共享储存器的大小,共享存储器速度比全局储存器快, 
deviceProp.regsPerBlock返回寄存器的数目; 
deviceProp.warpSize返回线程束中线程多少; 
deviceProp.maxThreadsPerBlock返回一个block中最多可以有的线程数; 
deviceProp.maxThreadsDim[]返回block内3维度中各维度的最大值 
deviceProp.maxGridSize[]返回Grid内三维度中各维度的最大值; 
deviceProp.memPitch返回对显存访问时对齐时的pitch的最大值; 
deviceProp.texturePitchAlignment返回对纹理单元访问时对其参数的最大值; 
deviceProp.clockRate返回显存的频率;

附另一个可能是不错的链接,关于cuda programming:http://blog.csdn.net/augusdi/article/details/12833235

只看一些基础概念:

2.1 线程网格(Grid)、线程块(Block)、线程(Thread)、线程束(Warp)

2.1.1 内核函数

内核函数是并行计算中最基本的单元函数,其特点是:

统一的处理逻辑代码,分布并行掌控不同区域的数据,以此达到多区域数据联动并行执行。

NVIDIA为了CPU在逻辑上能调度GPU计算的函数,规定了统一的格式。

以__global__限定符为始,声明:__global__ void helloworld()。

__global__意思为,GPU执行,CPU调用

调用时,需要分配  <<<线程块,块内线程数>>>。

如执行helloword,使用1个线程块,块内使用256个线程,则

helloworld<<<1,256>>>

2.1.2 线程网格(Grid)

线程网格在编程时并不存在,它只是抽象上的并行网格体系。

不同种类的内核函数,每种内核函数调度数个的线程块,这数个线程块逻辑上被判为一个Grid。

2.1.3 线程块(Block)

线程块是一个3D结构,强调3D坐标系时,需要以dim3类型声明三维大小。

dim3是个结构体, 成员x、y、z,代表方向轴尺度。

如helloworld<<<dim3(1,1,1), 256>>>。

当然,大部分操作基本使用的是1D坐标系,线程块默认全部扩展到X轴上。

一般写成helloworld<<<1,256>>>。

通常在内核函数内,需要获取线程块编号,以便对数据集的不同区域处理,四大重要属性:

?dim3 gridDim(不是指有多少Grid,而是指一个Grid有多少Block)

?dim3 blockDim(不是指有多少Block,而是指一个Block有多少Thread)

?dim3 blockIdx

?dim3 threadIdx

对于1D坐标系,有int tid= (blockDim.x*blockIdx.x) + threadIdx.x;

tid指明当前线程的编号,是内核函数里最基本的控制变量。

int step=(blockDim.x * gridDim.x);

由于CUDA限制每个Block的线程数(2.0以上通常使用1024,以下通常使用512)

所以在常规元素分解模型中,通常把每个Block的线程数设置为常量(固定不动)

这时,有两个策略:

① 其一,不固定Block:

这种方法最为常用,由于CUDA对每个任务而言,对Block数量的限制很松,

如图:

这时候,可以采取为每个线程分配一个元素的方法,用

BLOCKS=(N+THREADS−1)/THREADSBLOCKS=(N+THREADS−1)/THREADS

算出一个动态的Block数量的需求,这时候,for(i=tid;i<N;i+=step)等效于for(i=tid;i<N;i+=1)

因为这个循环根本不会执行第二次。

① 其二,固定Block数量:

这时,这时候,为了跑完全部的N个元素,有些Thread会启动人工循环。

i+=step会将元素坐标继续跳转,因为N必然大于step,你不能用+1来取剩余的元素吧?

这两种方法本质上是等效的,由于在物理执行时,同时并行线程最多大概是3072,

几百万、甚至几千万的Block会被CUDA扔到等待队列里,由CUDA自己安排自动循环,

有时甚至比你的人工循环更高效,所以,通常用①的方法,为了保持写法一致,step也会作为默认跳转量。

2.1.4 线程(Thread)

CUDA逻辑体系里最基本的执行单位,等效于CPU的线程。

内核函数一旦被指明了线程块大小,线程大小后,每个线程就分配到了一个内核函数的副本。

区别这些的线程的唯一方法就是线程编号tid,通过tid,让不同线程窥视数据集的不同部分。

用相同的逻辑代码,执行数据空间的不同子集。

2.1.5 线程束(Warp)

线程束对用户透明,它是NVIDIA强行规定的。目前显卡都固定为32。

逻辑上,线程束将32个线程编为一组。

一般微机系统,如8086,它的访存模式是串行的。每一个总线周期,吞一个字节进来。

NVIDIA的GPU在一个总线周期内,能够最大吞32*4=128字节。

前提是当个线程束内的线程,逐序访问显存,这特别需要设计数据存储形式。

使用线程束的目的是掩盖单个总线周期过长的问题,通常要跑500~600个T周期。



一般来说,一个CUDA程序必然少不了以下三步:

?cudaMalloc:创建新的动态显存堆

?cudaMemcpy:将主机(Host)内存复制到设备(Device)显存

?显存处理完之后,cudaMemcpy:设备(Device)显存复制回主机(Host)内存,释放显存cudaFree

其中第三步最容易遗忘。要知道,CPU最后是无法使用显存中的数据的。

一个例子:

/* GPU版HelloWorld,主要目的是演示CUDA基本程序框架:
 *? 将HelloWorld复制进显存
 *? 让GPU完成strcpy函数
 *? 将显存中的HelloWorld转回内存,并且打印
 */

/****kernel.cu****/
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
__global__ void cudaStrcpy(char *des, char *src)  /*内核函数*/
{
    while ((*src) != ‘\0‘) *des++ = *src++;
    *des = ‘\0‘;
}

/****gpu_helloworld.cu****/
#include "device.cu"
#include "kernel.cu"
#include "cstring"
void helloworld(char *str1, char *str2)
{
    InitCUDA();
    char *dev_str1=0, *dev_str2=0;
    int size = strlen(str1) + 1;
    cudaMalloc((void**)&dev_str1, size);   /*cuda系函数必须放在cu文件里*/
    cudaMalloc((void**)&dev_str2, size);
    cudaMemcpy(dev_str1, str1, size,cudaMemcpyHostToDevice);
    cudaStrcpy<<<1,1>>>(dev_str2, dev_str1); /*单线程块、单线程*/
    cudaMemcpy(str2, dev_str1, size, cudaMemcpyDeviceToHost);
}

/****main.cpp****/
#include "cstdio"
#include "cstring"
extern void helloworld(char *str1, char *str2);
int main()
{
    char src[] = "HelloWorld with CUDA";
    char *des = new char[strlen(src)+1];
    helloworld(src, des);
    printf("%s\n", des);
}

另一个例子:

/* 向量加法是CUDA 7.0在VS中提供的样例模板,演示了并行算法的经典trick:循环消除。
 *利用单个线程块中,多个线程并发执行,来消除循环。
 *时间复杂度估计,不能简单从O(n)迁移到O(1),因为GPU同时并行量存在限制。
 *即便是Kepler架构中拥有192SP的SM阵列,理论同时并行量也不过是2048。
 */

/****kernel.cu****/
__global__ void kernel_plus(int *a, int *b, int *c)
{
    int x = threadIdx.x;
    c[x] = a[x] + b[x];
}

/****gpu_vectoradd.cu****/
void vectorAdd(int *a, int *b, int *c,int size)
{
    if (!InitCUDA()) return;
    int *dev_a = 0, *dev_b = 0, *dev_c = 0;
    cudaMalloc((void**)&dev_a, size*sizeof(int));
    cudaMalloc((void**)&dev_b, size*sizeof(int));
    cudaMalloc((void**)&dev_c, size*sizeof(int));
    cudaMemcpy(dev_a, a, size*sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(dev_b, b, size*sizeof(int), cudaMemcpyHostToDevice);
    kernel_plus << <1, size >> >(dev_a, dev_b, dev_c);
    cudaMemcpy(c, dev_c, size*sizeof(int), cudaMemcpyDeviceToHost);
}

/****main.cpp****/
extern void vectorAdd(int *a, int *b, int *c, int size);
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 }, b[5] = { 10, 20, 30, 40, 50 }, c[5] = { 0 };
    vectorAdd(a, b, c,5);
    printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",
        c[0], c[1], c[2], c[3], c[4]);
}


Ref: http://blog.csdn.net/qiexingqieying/article/details/51734347

首先对这些框架进行总览。


库名称


开发语言


速度


灵活性


文档


适合模型


平台


上手难易


Caffe


c++/cuda



一般


全面


CNN


所有系统


中等


TensorFlow


c++/cuda/Python


中等



中等


CNN/RNN


Linux, OSX



MXNet


c++/cuda




全面


CNN


所有系统


中等


Torch


c/lua/cuda




全面


CNN/RNN


Linux, OSX


中等


Theano


python/c++/cuda


中等



中等


CNN/RNN


Linux, OSX


接下来将对这些框架进行分别介绍。

Caffe

第一个主流的工业级深度学习工具。

它开始于2013年底,由UC Berkely的Yangqing Jia老师编写和维护的具有出色的卷积神经网络实现。在计算机视觉领域Caffe依然是最流行的工具包。

它有很多扩展,但是由于一些遗留的架构问题,不够灵活且对递归网络和语言建模的支持很差。

TensorFlow

Google开源的其第二代深度学习技术——被使用在Google搜索、图像识别以及邮箱的深度学习框架。

是一个理想的RNN(递归神经网络)API和实现,TensorFlow使用了向量运算的符号图方法,使得新网络的指定变得相当容易,支持快速开发。

缺点是速度慢,内存占用较大。(比如相对于Torch)

MXNet

是李沐和陈天奇等各路英雄豪杰打造的开源深度学习框架,是分布式机器学习通用工具包DMLC 的重要组成部分。

它注重灵活性和效率,文档也非常的详细,同时强调提高内存使用的效率,甚至能在智能手机上运行诸如图像识别等任务。

Torch

Facebook力推的深度学习框架,主要开发语言是C和Lua。

有较好的灵活性和速度。

它实现并且优化了基本的计算单元,使用者可以很简单地在此基础上实现自己的算法,不用浪费精力在计算优化上面。核心的计算单元使用C或者cuda做了很好的优化。在此基础之上,使用lua构建了常见的模型。

缺点是接口为lua语言,需要一点时间来学习。

Theano

2008年诞生于蒙特利尔理工学院,主要开发语言是Python。

Theano派生出了大量深度学习Python软件包,最著名的包括BlocksKeras

Theano的最大特点是非常的灵活,适合做学术研究的实验,且对递归网络和语言建模有较好的支持,缺点是速度较慢。

时间: 2024-07-30 13:48:18

[CUDA] CUDA to DL的相关文章

CUDA ---- CUDA库简介

CUDA Libraries简介 上图是CUDA 库的位置,本文简要介绍cuSPARSE.cuBLAS.cuFFT和cuRAND,之后会介绍OpenACC. cuSPARSE线性代数库,主要针对稀疏矩阵之类的. cuBLAS是CUDA标准的线代库,不过没有专门针对稀疏矩阵的操作. cuFFT傅里叶变换 cuRAND随机数 CUDA库和CPU编程所用到的库没有什么区别,都是一系列接口的集合,主要好处是,只需要编写host代码,调用相应API即可,可以节约很多开发时间.而且我们完全可以信任这些库能够

[转]CUDA在Windows下的软件开发环境搭建

引自:http://www.makaidong.com/yaoyuanzhi/archive/2010/11/13/1876215.html 本文我们以visual studio 2005 为例演示cuda的安装以及软件开发环境搭建,以及cuda与mfc联调的实现. 1.cuda安装包 cuda是免费使用的,各种操作系统下的cuda安装包均可以在http://www.nvidia.cn/object/cuda_get_cn.html上免费下载.cuda提供3个安装包,分别是: driver, t

详解CUDA编程

CUDA 是 NVIDIA 的 GPGPU 模型,它使用 C 语言为基础,可以直接以大多数人熟悉的 C 语言,写出在显示芯片上执行的程序,而不需要去学习特定的显示芯片的指令或是特殊的结构." 编者注:NVIDIA的GeFoce 8800GTX发布后,它的通用计算架构CUDA经过一年多的推广后,现在已经在有相当多的论文发表,在商业应用软件等方面也初步出现了视频编解码.金融.地质勘探.科学计算等领域的产品,是时候让我们对其作更深一步的了解.为了让大家更容易了解CUDA,我们征得Hotball的本人同

CUDA, 软件抽象的幻影背后

本文原载于我们的博客planckscale.info,转载于此. 版权声明:原创作品,欢迎转载,但转载请以超链接形式注明文章来源(planckscale.info).作者信息和本声明,否则将追究法律责任. 今天最酷炫的事情应该就是来自老黄的这条消息:1TFLOPS,P < 15W, ARM Cortex A57 * 4 + ARM Cortex A53 * 4 +  Maxwell 256 CUDA Cores,  Tegra X1. 图1.  Tegra X1 本想挖掘一下写篇博,但目前报道满

CUDA编程

目录: 1.什么是CUDA 2.为什么要用到CUDA 3.CUDA环境搭建 4.第一个CUDA程序 5. CUDA编程 5.1. 基本概念 5.2. 线程层次结构 5.3. 存储器层次结构 5.4. 运行时API 5.4.1. 初始化 5.4.2. 设备管理 5.4.3. 存储器管理 5.4.3.1. 共享存储器 5.4.3.2. 常量存储器 5.4.3.3. 线性存储器 5.4.3.4. CUDA数组 5.4.4. 流管理 5.4.5. 事件管理 5.4.6. 纹理参考管理 5.4.6.1.

CUDA学习日志:windows开发环境配置

接触CUDA的时间并不长,最开始是在cuda-convnet的代码中接触CUDA代码,当时确实看的比较痛苦.最近得空,在图书馆借了本<GPU高性能编程 CUDA实战>来看看. Jeremy Lin 什么是CUDA CUDA(Compute Unified Device Architecture)是一种专门为提高并行程序开发效率而设计的计算架构.在构建高性能应用程序时,CUDA架构能充分发挥GPU的强大计算能力.更多的介绍,可以参考NVIDIA的ABOUT PAGE. CUDA开发环境配置 在开

64位双系统Ubuntu 14.04 LTS + Caffe + CUDA 7.5 + Opencv 3.0 安装配置实战

一切的一切,开端便是这caffe,作为博客的第一篇文章,自然要讲讲一个哲学问题"我是从哪来的" 一.windows情况下安装双系统64位Ubuntu 本段落根据http://www.linuxidc.com/Linux/2014-04/100369p2.htm而成. 下面开始: 1)首先还是分区,在计算机上右键--管理--磁盘管理 装Ubuntu分配的硬盘大小最好是(20G以上)不要太小,配好整个环境就要消耗10G左右,再加上数据集和各种库,空间太小非常尴尬.这里请注意,Ubuntu和

CUDA(六). 从并行排序方法理解并行化思维——冒泡、归并、双调排序的GPU实现

在第五讲中我们学习了GPU三个重要的基础并行算法: Reduce, Scan 和 Histogram,分析了 其作用与串并行实现方法. 在第六讲中,本文以冒泡排序 Bubble Sort.归并排序 Merge Sort 和排序网络中的双调排序 Bitonic Sort 为例, 讲解如何从数据结构课上学的串行并行排序方法转换到并行排序,并附GPU实现代码. 在并行方法中,我们将考虑到并行方法需要注意的特点进行设计,希望大家在读完本文后对GPU上并行算法的设计有一些粗浅的认识.需注意的特点如下: 1

windows10下theano启用gpu:CUDA + Anaconda

最近在学习深度学习的相关内容,照着http://deeplearning.net/tutorial/中每个章节的例子一个个对着代码做.并且将部分代码重构了,代码地址在https://github.com/wangzhics/deeplearning里面了. deeplearning里面的代码是基于http://deeplearning.net/software/theano/tutorial/index.html#tutorial的,theano中我们可以定义变量,使用其内置的公式并组合,而不用