将Tensor核心引入标准Fortran
将Tensor核心引入标准Fortran
调优的数学库是从HPC系统提取最终性能的一种简单而可靠的方法。但是,对于寿命长的应用程序或需要在各种平台上运行的应用程序,为每个供应商或库版本调整库调用可能是维护的噩梦。
可以自动生成对调优数学库的调用的编译器为提供了两全其美的优势:易于移植和终极性能。在本文中,将展示如何在GPU上无缝加速许多标准Fortran数组内在函数和语言构造。nvfortran编译器通过将Fortran语句映射到NVIDIA cuTENSOR库中的可用功能来自动启用此加速,该库是NVIDIA cuTENSOR的先河,提供GPU加速的张量线性代数库,提供张量收缩,缩小和逐元素运算。
轻松过渡到NVIDIA GPU
以下是标准Fortran数组内在函数如何映射到GPU加速的数学库。在最简单的级别上,只需要两个Fortran语句即可利用cuTENSOR库提供的出色性能:
使用cutensorex
...
c = matmul(a,b)
使用cutensorex预定义模块的第一条语句以重载的Fortran内在过程,数组表达式和重载的赋值形式包含cuTENSOR库的接口。编写接口以仅映射位于GPU设备内存中的阵列。在本文的稍后部分,将从OpenACC和CUDA Fortran程序员的角度讨论这意味着什么。定义了这些接口后,包含matmul()内部调用的第二条语句将自动映射到cuTENSOR函数调用。
这些接口通过识别并匹配可以映射到单个cuTENSOR内核调用的几种常用模式来实现延迟执行。在所有情况下,都将调用多个cuTENSOR函数来设置cuTENSOR所需的句柄,描述符数据结构和工作缓冲区。
但是,只有一个内核被启动到GPU。出于性能考虑,将整个语句(包括分配给左侧数组)映射起来很重要。不希望编译器为右侧操作的输入或结果(中间或最终)创建Fortran中常见的临时数组。
支持的标准Fortran操作
cuTENSOR库包含常规的排列和收缩操作。置换的结果可以可选地通过元素函数来操作,并且可选地缩放。
nvfortran编译器可以识别和映射与通用数组语法结合使用的各种Fortran转换内在函数和基本内在函数,并将其映射到cuTENSOR功能。一些更直接的翻译包括:
d =转置(a)
d =函数(transpose(a))
d = alpha * func(转置(a)
d =重塑(a,shape = [...])
d =重塑(a,shape = [...],order = [...])
d =函数(reshape(a,...))
d = alpha * func(reshape(a,...))
d =点差(a,dim = k,ncopies = n)
d =函数(spread(a,dim = k,ncopies = n))
d = alpha * func(spread(a,dim = k,ncopies = n))
matmul()也可以在cuTENSOR中排列to的输入,并且可以缩放和累加结果。这导致几种可能的组合,例如以下语句:
c = matmul(a,b)
c = c + matmul(a,b)
c = c-matmul(a,b)
c = c + alpha * matmul(a,b)
d = alpha * matmul(a,b)+ beta * c
c = matmul(转置(a),b)
c = matmul(reshape(a,shape = [...],order = [...]),b)
c = matmul(a,转置(b))
c = matmul(a,reshape(b,shape = [...],order = [...]))
使用标准Fortran中的NVIDIA Tensor Core
当使用cutensorex模块中包含的用于随机数生成的功能时,利用cuTENSOR和NVIDIA Tensor Core就像下面的代码示例一样容易:
程序主体
使用cutensorex
整数,参数:: ni = 5120,nj = 5120,nk = 5120,ntimes = 10
实数(8),可分配,尺寸(:,:) :: a,b,d
分配(a(ni,nk),b(nk,nj),d(ni,nj))
呼叫random_number(a)
呼叫random_number(b)
d = 0.0d0
打印*,“裁切器”
呼叫cpu_time(t1)
做nt = 1,ntimes
d = d + matmul(a,b)
做完
呼叫cpu_time(t2)
拖鞋= 2.0 * ni * nj * nk
翻牌=翻牌* n次
打印*,“ times”,t2,t1,t2-t1
打印*,“ GFlops”,flops /(t2-t1)/1.e9
结束程序
该matmul()固有调用映射到cuTENSOR调用,无缝地使用张量核尽可能。将在本文后面显示一些性能结果。
使用nvfortran编译程序
可能会问, cutensorex接口仅将GPU设备阵列上的操作映射到cuTENSOR调用时,该程序如何使用cuTENSOR 。答案在于程序的编译方式:
%nvfortran -acc -gpu =托管-cuda -cudalib main.f90
在这里,将程序编译为OpenACC程序,并利用OpenACC托管内存模式,在该模式下,所有可分配阵列都分配在CUDA统一内存中。由于增加了-cuda,使CUDA Fortran扩展为好,数组实质上CUDA Fortran语言-管理型阵列。CUDA Fortran通用接口匹配的一条规则是,当主机和设备接口同时存在时,优选使用设备接口作为托管实际参数。
当声明,分配和使用位于同一程序单元中时,nvfortran编译器提供一些快捷方式。通常,最好使用OpenACC指令来指示编译器传递设备地址,如以下代码示例所示:
!$ acc host_data use_device(a,b,d)
做nt = 1,ntimes
d = d + matmul(a,b)
做完
!$ acc结束host_data
在这种情况下,-cuda不需要编译器选项。
使用CUDA Fortran中的cuTENSOR
对于CUDA Fortran用户而言,cutensorex模块和Fortran转换内在函数成为获取高性能和完全可移植代码的快速途径。使用!@cuf前哨添加由nvfortran CUDA Fortran编译器解释和编译的代码行,或由标准Fortran编译器作为注释忽略的代码行:
程序主体
!@cuf使用cutensorex
!@cuf使用cudafor
整数,参数:: ni = 5120,nj = 5120,nk = 5120,ntimes = 10
实数(8),可分配,尺寸(:,:) :: a,b,d
!@cuf属性(设备):: a,b,d
分配(a(ni,nk),b(nk,nj),d(ni,nj))
呼叫random_number(a)
呼叫random_number(b)
d = 0.0d0
打印*,“裁切器”
呼叫cpu_time(t1)
做nt = 1,ntimes
d = d + matmul(a,b)
做完
呼叫cpu_time(t2)
拖鞋= 2.0 * ni * nj * nk
翻牌=翻牌* n次
打印*,“ times”,t2,t1,t2-t1
打印*,“ GFlops”,flops /(t2-t1)/1.e9
结束程序
在第6行,使用设备属性声明了数组,将其放置在GPU设备内存中。但是,也可以使用托管属性来声明它。可以编译该程序并将其与以下命令链接:
%nvfortran -Mcudalib main.cuf
实测(8)数据的性能
下面是从前面示例中使用的real(8)(双精度)数据开始的性能观察。可以通过以下几种方法来衡量矩阵乘法性能:
- 单线程CPU实现
- 多线程或多核CPU实现
- 天真编码矩阵乘法使用指令分载
- 该
matmul()映射到cuTENSOR内在
为了获得最佳的线程CPU性能,请使用基本的线性代数子程序(BLAS)库例程DGEMM。以下命令与先前操作的等效DGEMM调用:
呼叫dgemm('n','n',ni,nj,nk,1.0d0,a,ni,b,nk,1.0d0,d,ni)
为了了解经过调整的库可以通过天真的实现提供什么,使用以下OpenACC循环结构在GPU上运行。循环结构不使用特殊的拼贴或硬件指令。
$ acc内核
做j = 1,nj
是否等于1,ni
做k = 1,nk
d(i,j)= d(i,j)+ a(i,k)* b(k,j)
做完
做完
做完
$ acc结束内核
表1显示了real(8)在基于双插槽AMD EPYC 7742 Rome CPU的服务器,单个NVIDIA V100和单个NVIDIA A100 GPU的一个NUMA节点上获得的性能。

表1.双插槽AMD EPYC 7742 Rome基于CPU的服务器,单个V100和单个A100 GPU在一个NUMA节点上的real(8)性能。
不仅可以使用matmul()固有函数在V100和A100 GPU上获得自动GPU加速,而且在A100上matmul()到cuTENSOR调用的映射可以使自动使用FP64 Tensor Core。
在real(4)和real(2)数据上测得的性能
可以使用real(4)(单精度)数据并调用SGEMM而不是DGEMM来执行同一组运行。此外,CUDA 11.0 cuTENSOR Fortran包装器可以利用A100 TF32数据类型和Tensor Core。表2显示了这些运行的性能。

表2.双插槽AMD EPYC 7742 Rome基于CPU的服务器,单个V100和单个A100 GPU的一个NUMA节点上的real(4)性能。
为什么停在那里?nvfortran编译器使用real(2)数据类型支持16位浮点格式(FP16)。可以在较早的测试中更改数组的类型,并以半精度运行计时。
Tensor Core操作是在V100上引入的,用于半精度数据,然后在A100 GPU上进行了扩展,以支持TF32和完整的双精度DP64 Tensor Core。尽管nvfortran支持real(2)V100和A100上的Tensor Core,但它不支持real(2)CPU上的完整和优化,标准BLAS库也不支持。在这种情况下,只有比较GPU加速版本的性能才有意义(表3)。

表3.单个V100和单个A100 GPU的real(2)性能。
尽管A100的性能令人印象深刻且代码可完全移植,但它远低于TF32和FP16的峰值。有固定的开销:在每个调用中,创建并销毁cuTENSOR张量描述符并创建收缩计划。还必须查询和管理收缩中使用的工作空间要求,这最终可能会调用cudaMalloc和cudaFree。如果FP64的开销为5 – 10%,则对于这种大小的问题,TF32的开销将接近25%,FP16的开销将接近35%。
对于需要最终性能的开发人员,nvfortran确实支持HPC SDK中也提供的Fortran cutensor模块中与C cuTENSOR API的Fortran接口。可以自己管理张量描述符,计划和工作区。
结论
在本文中,展示了一些简单的程序以及可以在GPU上自动加速的Fortran内部调用和代码模式的类型。甚至可以通过cuTENSOR自动利用Tensor Core。使用几乎完全是Fortran标准的程序并完全可移植到其编译器和系统的程序,可以在NVIDIA GPU上实现矩阵乘法,矩阵转置,元素数组内在函数以及数组语法的多种组合的近峰性能。
无法预测使用这些新功能可能会做什么。期待着的反馈和结果。NVIDIA继续添加更多功能,使可以使用标准的Fortran结构以最高的性能对NVIDIA GPU进行编程。
将Tensor核心引入标准Fortran的更多相关文章
- A100 Tensor核心可加速HPC
A100 Tensor核心可加速HPC HPC应用程序的性能需求正在迅速增长.众多科学研究领域的许多应用程序都依赖于双精度(FP64)计算. 为了满足HPC计算快速增长的计算需求,A100 GPU支持 ...
- python 生成list的所有的子集 (不使用递归且不引入标准库)
不使用递归且不引入标准库,单纯用两个for循环即可得出一个list的所有子集 L = [1, 2, 3, 4] List = [[]] for i in range(len(L)): ...
- css的核心内容 标准流、盒子模型、浮动、定位等分析
1.块级元素:如:<div></div>2.行内元素:如:<span></span>从效果中看块级元素与行内元素的区别: 通过CSS的设置把行内元素转换 ...
- c++标准库多线程入门
从c++ 11开始,语言核心和标准库开始引入了对多线程的原生支持.如下所示: int doSth(char c) { default_random_engine dre(c); uniform_int ...
- 什么是 C 和 C ++ 标准库?
简要介绍编写C/C ++应用程序的领域,标准库的作用以及它是如何在各种操作系统中实现的. 我已经接触C++一段时间了,一开始就让我感到疑惑的是其内部结构:我所使用的内核函数和类从何而来? 谁发明了它们 ...
- [转]什么是 C 和 C ++ 标准库?
转载地址:https://www.cnblogs.com/findumars/p/9000371.html 简要介绍编写C/C ++应用程序的领域,标准库的作用以及它是如何在各种操作系统中实现的.我已 ...
- CUDA 9中张量核(Tensor Cores)编程
CUDA 9中张量核(Tensor Cores)编程 Programming Tensor Cores in CUDA 9 一.概述 新的Volta GPU架构的一个重要特点是它的Tensor核,使T ...
- 用NVIDIA Tensor Cores和TensorFlow 2加速医学图像分割
用NVIDIA Tensor Cores和TensorFlow 2加速医学图像分割 Accelerating Medical Image Segmentation with NVIDIA Tensor ...
- UNIX环境高级编程笔记之标准I/O库
一.总结 文件I/O一章讲了不带缓冲的I/O,本章讲的是带缓冲的I/O.不带缓冲针对的是内核的系统调用,而带缓冲针对的是用户空间的标准库函数,是基于带缓冲的I/O实现的.不带缓冲的I/O通过文件描述符 ...
随机推荐
- 编译libdvm.so: makefile,mm
操作系统:Ubuntu14.4 android版本:4.4 设备:nexus 5 android系统的编译使用make来操作,那make呢是执行对应的makefile即android的编译系统看mak ...
- 安装全局消息钩子实现dll窗体程序注入
说明{ 通过设置全局消息钩子来实现dll注入,然后窗体有相关消息请求的时候就会自动加载注入dll, 然后在入口处做处理就可以了.注入方式简单很多,比代码注入和lsp等注入都简单,就不解释了. ...
- (Py练习)判断能被几个9整除
# 输入一个奇数,判断最少几个9除以该数的结果为整数 # 999999 / 13 = 76923 if __name__ == '__main__': zi = int(input("输入一 ...
- Linux的基础操作
1.概念 Linux是基于Unix的开源免费的操作系统,由于系统的稳定性和安全性几乎成为程序代码运行的最佳系统环境. 2.Linux的分类 1.按市场需求分为: 图形化界面版.服务器版 2.按原生程度 ...
- 【技术博客】基于vue的前端快速开发(工具篇)
一.Vue教程 vue.js是一套构建用户界面的渐进式框架.vue采用自底向上增量开发的设计.vue的核心库只关心视图层,非常容易学习,非常容易与其它库和已有项目整合.vue完全有能力驱动采用单文件组 ...
- API网关才是大势所趋?SpringCloud Gateway保姆级入门教程
什么是微服务网关 SpringCloud Gateway是Spring全家桶中一个比较新的项目,Spring社区是这么介绍它的: 该项目借助Spring WebFlux的能力,打造了一个API网关.旨 ...
- 成功的多项目管理都有哪些"制胜之道"?
实施多项目管理,一个重要原因就是提高项目的效率和管理水平.除了满足时间.成本.业绩和客户需求之外,项目管理办公室(PMO)经理的预期产出还包括有效利用组织资源.下面是影响多项目管理成功的几个关键因素, ...
- where优先级
select name from emply where id >5; 先找表from emply 再找条件 where id >5 最后打印 你想打印的字段 可以把select看成打印 ...
- 16.分类和static
1.案例驱动模式 1.1案例驱动模式概述 (理解) 通过我们已掌握的知识点,先实现一个案例,然后找出这个案例中,存在的一些问题,在通过新知识点解决问题 1.2案例驱动模式的好处 (理解) 解决重复代码 ...
- wmctrl像xmonad那样方便地用快捷键来控制任务窗口的显示
窗口左右互搏之wmctrl篇 分类: LINUX 2012-10-24 16:34:41 一直有个念头,就是能够像xmonad那样方便地用快捷键来控制任务窗口的显示,今天弄wmctrl,刚好有时间 ...