在cuDNN中简化Tensor Ops

在Tesla V100 GPU中引入神经网络模型以来,神经网络模型已迅速利用NVIDIA Tensor Cores进行深度学习。例如,基于Tensor Core的解决方案宣布了ResNet50训练的性能记录。

NVIDIA的cuDNN库 使CUDA程序员能够优化循环神经网络卷积神经网络,以实现GPU加速。概述了cuDNN用户使用Tensor Core 进行卷积的简便方法,并附有说明和示例代码。该文章为cuDNN应用提供了一些简单的规则:FP16数据规则,张量维数规则,ALGO_1的使用等。

cuDNN版本解除了大多数限制。cuDNN 7.2版本取消了FP16数据约束,而cuDNN 7.3删除了张量尺寸约束(对于打包的NCHW张量数据),直接进行改进。

将FP32数据用于Tensor Ops

关于在CUDA中使用Tensor Core的帖子讨论了将FP16输入用于张量操作,如图1所示。虽然张量操作仍然使用FP16数据,但卷积cuDNN API允许用户选择将FP32输入数据转换为FP16。如果需要,卷积的输出数据也将转换为FP32。

图1. FP32数据现在可以用作输入

CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION枚举值,在cuDNN 7.2,使cuDNN应用程序员选择转换FP32数据运算使用。该枚举值与枚举值一样传递给cudnnSetConvolutionMathType()调用CUDNN_TENSOR_OP_MATH。此代码段显示了如何执行此操作:

//设置数学类型以允许cuDNN使用Tensor Core:
checkCudnnErr(cudnnSetConvolutionMathType(cudnnConvDesc,CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION));

将在后面的部分中看到使用代码片段的上下文。

FP32数据也用于RNN

现在还为RNN启用了类似的FP32数据转换。只需将CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION枚举值传递给cudnnSetRNNMatrixMathType()调用,即可将FP32数据转换为在RNN中使用。如下使用:

//设置数学类型以允许cuDNN使用Tensor Core:
checkCudnnErr(cudnnSetRNNMatrixMathType(cudnnRnnDesc,CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION));

消除了NCHW张量尺寸约束

早期版本的cuDNN要求所有张量的通道维数必须为8的倍数。cuDNN可以根据需要自动填充张量。

CUDNN_TENSOR_OP_MATHCUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION情况下,对于填充的NCHW数据,此填充都是自动的。发生填充时,性能损失可忽略不计。

//设置NCHW张量尺寸,不必设置为8的倍数(此处仅显示输入张量):
int dimA [] = {1,7,32,32};
int strideA [] = {7168,1024,32,1};

下一节中的示例代码演示了如何使用。

样例代码

将张量运算用于FP32数据和任何通道尺寸的逻辑类似于为cuDNN的早期版本编写时使用的逻辑。只有维度和数据类型发生了变化(以及使用CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION)

//创建一个cuDNN句柄:
checkCudnnErr(cudnnCreate(&handle_));
 
//创建张量描述符:
checkCudnnErr(cudnnCreateTensorDescriptor(&cudnnIdesc));
checkCudnnErr(cudnnCreateFilterDescriptor(&cudnnFdesc));
checkCudnnErr(cudnnCreateTensorDescriptor(&cudnnOdesc));
checkCudnnErr(cudnnCreateConvolutionDescriptor(&cudnnConvDesc));
 
//设置NCHW张量尺寸,不必设置为8的倍数(此处仅显示输入张量):
int dimA [] = {1,7,32,32};
int strideA [] = {7168,1024,32,1};
 
checkCudnnErr(cudnnSetTensorNdDescriptor(cudnnIdesc,CUDNN_DATA_FLOAT,
convDim + 2,dimA,strideA));
 
//分配和初始化张量(同样,仅显示输入张量): 
checkCudaErr(cudaMalloc((void **)&(devPtrI),(insize)* sizeof(devPtrI [0]))));;
hostI =(T_ELEM *)calloc(insize,sizeof(hostI [0]));
 
initImage(hostI,insize);
 
checkCudaErr(cudaMemcpy(devPtrI,hostI,sizeof(hostI [0])* insize,cudaMemcpyHostToDevice));
 
//设置计算数据类型(以下为CUDNN_DATA_FLOAT):
checkCudnnErr(cudnnSetConvolutionNdDescriptor(cudnnConvDesc,convDim,padA,convstrideA,dilationA,CUDNN_CONVOLUTION,CUDNN_DATA_FLOAT));;
 
//设置数学类型以允许cuDNN使用Tensor Core:
checkCudnnErr(cudnnSetConvolutionMathType(cudnnConvDesc,CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION));
 
//选择支持的算法:
cudnnConvolutionFwdAlgo_t算法= CUDNN_CONVOLUTION_FWD_ALGO_IMPLICIT_PRECOMP_GEMM;
 
//分配的工作空间:
checkCudnnErr(cudnnGetConvolutionForwardWorkspaceSize(handle_,cudnnIdesc,
cudnnFdesc,cudnnConvDesc,
cudnnOdesc,algo,&workSpaceSize));
 
如果(workSpaceSize> 0){
   cudaMalloc(&workSpace,workSpaceSize);
}
 
//调用卷积: 
checkCudnnErr(cudnnConvolutionForward(handle_,(void *)(&alpha),cudnnIdesc,devPtrI,
cudnnFdesc,devPtrF,cudnnConvDesc,algo,
workSpace,workSpaceSize,(void *)(&beta),
cudnnOdesc,devPtrO));

FP32性能

图2显示了将Tensor Core用于FP32张量数据时卷积的比较性能。该图表将V100张量运算与V100 FMA运算进行了比较,因此,其增益并不像以前的将V100性能与P100 FMA进行比较的图表那样明显。但是,与使用FMA ops相比,与FP32输入一起使用的Tensor ops仍然代表了可观的收益。

图2.具有Tensor Core的Tesla V100(Volta)与Tesla V100(Volta)的卷积性能比较。比较是在每个神经网络的卷积层运行时间的几何方法之间进行的。两种情况都使用FP32输入/输出数据和FP32计算。一种使用Tensor Core,另一种使用FP32融合乘加(FMA)。

剩余约束

尽管解除了在cuDNN中使用张量运算的主要限制,但仍然存在一些次要限制。一个限制是使用ALGO_1(IMPLICIT_PRECOMP_GEMM用于转发)。cuDNN中还没有其他卷积算法使用张量运算。

另一个较小的限制是卷积滤波器的大小,特别是空间尺寸(r和s)。但是,用于卷积的FFT算法非常适合于滤波器尺寸较大的用例。只需在超出张量运算滤波器限制以达到最佳性能之前就将卷积切换为使用FFT算法即可。

在cuDNN中简化Tensor Ops的更多相关文章

  1. 在PHP应用中简化OAuth2.0身份验证集成:OAuth 2.0 Client

    在PHP应用中简化OAuth2.0身份验证集成:OAuth 2.0 Client   阅读目录 验证代码流程 Refreshing a Token Built-In Providers 这个包能够让你 ...

  2. TensorFlow中的 tensor 张量到底是什么意思?

    详见[Reference]: TensorFlow中的“Tensor”到底是什么? 以下摘录一些要点: 这个图好生动呀!~ 标量和向量都是张量(tensor).

  3. tensorflow中张量(tensor)的属性——维数(阶)、形状和数据类型

    tensorflow的命名来源于本身的运行原理,tensor(张量)意味着N维数组,flow(流)意味着基于数据流图的计算,所以tensorflow字面理解为张量从流图的一端流动到另一端的计算过程. ...

  4. EL和 JSTL? 在JSP中简化 java代码的写法!

    一.servlet部分 package com.aaa.servlet; import com.aaa.dao.IStudentDAO; import com.aaa.dao.Impl.Student ...

  5. pytorch 中改变tensor维度的几种操作

    具体示例如下,注意观察维度的变化 #coding=utf-8 import torch """改变tensor的形状的四种不同变化形式""" ...

  6. Python中简化的验证码功能实现

    #!/usr/bin/env python # _*_ encoding:utf-8 _*_ # author:snate import random def generate_auth_code() ...

  7. Pytorch中ndarray tensor list互转

    1.ndarray->tensor : b=torch.from_numpy(a) 2.tensor->ndarray: b=a.numpy() ''' 但这么写会报错-- Runtime ...

  8. python中简化的验证码功能

    验证码一般用来验证登陆.交易等行为,减少对端为机器操作的概率,python中可以使用random模块,char()内置函数来实现一个简单的验证码功能. import random def veri_c ...

  9. VIM中简化删除,光标移动和查找操作

    # 一.命令行模式下简化删除 1. 向后删除单个字符:[x] 2. 向前删除单个字符:[X] 3. 删除从光标开始到单词结尾:[dw] 删除从光标后的2个单词:[d2w] 4. 删除整个单词:[daw ...

随机推荐

  1. 【责任链模式】责任链模式结合Spring实战Demo

    备注: 责任链与策略模式有很多相似之处,如都是行为型设计模式,都能够处理代码中的if-else逻辑 主要区别在于: 策略模式 封装了算法,通过上下文对象去接受客户端的数据,根据数据类型执行不同的算法 ...

  2. Java中实现某方法和重写某方法的区别

    实现(implements) 实现一个方法,在实现某个接口,或者是继承某个抽象类,在接口和在抽象类中定义的方法,本身是没有实现的,也就是没有方法体,你在当前类中就需要去实现这个方法. 重写(overl ...

  3. Insert Pictures In Hexo Blog

    After build my blog following the online course step by step , I began to try to write my own blog️ ...

  4. Docker为PHP安装gd扩展

    安装扩展库的通常命令 docker-php-ext-install 扩展库名 安装gd库需要特殊照顾,步骤如下 //进入PHP容器 //更新软件源 apt update //安装各种库 apt ins ...

  5. Window内核学习之保护模式基础

    段寄存器 段寄存器有6个分别是 cs,ss,ds,es,fs,gs.这些段寄存器包含16位的可见部分和80位的隐藏部分,共90位. 16位的可见部分就是我们知道的cs等段寄存器的值,我们可以在od中查 ...

  6. C++ primer plus读书笔记——第15章 友元、异常和其他

    第15章 友元.异常和其他 1. 友元类的所有方法都可以访问原有类的私有成员和保护成员.另外,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元.哪些函数.成员函数.或类为友元是由类定义的, ...

  7. C++ primer plus读书笔记——第7章 函数——C++的编程模块

    第7章 函数--C++的编程模块 1. 函数的返回类型不能是数组,但可以是其他任何一种类型,甚至可以是结构和对象.有趣的是,C++函数不能直接返回数组,但可以将数组作为结构或对象的组成部分来返回. 2 ...

  8. RTTI之typeid运算符

    1 #include <iostream> 2 #include <cstdlib> 3 #include <ctime> 4 #include <typei ...

  9. 事后分析$\alpha$

    项目 内容 课程:北航-2020-春-软件工程 博客园班级博客 要求 事后分析 我们在这个课程的目标是 提升团队管理及合作能力,开发一项满意的工程项目 这个作业在哪个具体方面帮助我们实现目标 组织组员 ...

  10. java并发编程:深入了解synchronized

    简介 synchronized是Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码.同时它还保证了共享变量的内存可见性. ...