教程:把TensorCores带到标准Fortran

描述

调优数学库是从HPC系统中提取最终性能的一种简单而可靠的方法。 然而,对于长期存在的应用程序或需要在各种平台上运行的应用程序来说,为每个供应商或库版本调整库调用可能是一个维护噩梦。

一个编译器可以自动生成对调优的数学库的调用,这给您提供了两个世界中最好的:易于移植和最终性能。 在这篇文章中,我展示了如何无缝地加速GPU上的许多标准Fortran数组intrinsic和语言构造。 nvfortran编译器通过将Fortran语句映射到NVIDIA cu TEN SOR库中可用的函数来自动实现这种加速,这是一种第一种GPU加速的张量线性代数库,提供张量收缩、约简和元素操作。

一个简单的上车到NVIDIA GPU

下面是标准Fortran数组内在函数如何映射到GPU加速的数学库。 在最简单的层次上,只需要两个Fortran语句就可以利用cut TEN SOR库提供的出色性能:

 

use  卡顿索克斯
...
c = matmul(a,b)

 

使用的第一个语句 卡顿索克斯 预定义模块以重载Fortran内部过程、数组表达式和重载赋值的形式包含cuTENSOR库的接口。这些接口仅用于映射位于GPU设备内存中的阵列。在本文后面,我将从OpenACC和CUDA Fortran程序员的角度讨论这意味着什么。定义了这些接口后,第二条语句包含 马修() 内在调用自动映射到cuTEN SOR函数调用。

接口通过识别和匹配几种常用模式来实现延迟执行,这些模式可以映射到单个cu TEN SOR内核调用。 在所有情况下,调用多个cu TEN SOR函数来设置cu TEN SOR所需的句柄、描述符数据结构和工作缓冲区。

然而,只有一个内核被启动到GPU上。 由于性能原因,必须映射整个语句,包括左侧数组的赋值。 您不希望编译器为右侧操作的输入或结果(中间或最终)创建临时数组,这在Fortran中很常见。

支持标准Fortran操作

cut TEN SOR库包含一般的置换和收缩操作。 置换的结果可以选择由元素函数操作,也可以选择缩放。

nvfortran编译器可以识别和映射各种Fortran转换intrinsic和元素intrinsic函数,这些函数与通用数组语法相结合,用于cut TEN SOR功能。 一些比较直接的翻译包括以下内容:

 

d = transpose(a)
d = func(transpose(a))
d = alpha * func(transpose(a)

d = reshape(a,shape=[...])
d = reshape(a,shape=[...],order=[...])
d = func(reshape(a,...))
d = alpha * func(reshape(a,...))

d = spread(a,dim=k,ncopies=n)
d = func(spread(a,dim=k,ncopies=n))
d = alpha * func(spread(a,dim=k,ncopies=n))

 

的投入 马修() 也可以在CuTEN SOR中置换,结果可以缩放和累积。 这导致了几种可能的组合,例如以下陈述:

 

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(transpose(a),b)
c = matmul(reshape(a,shape=[...],order=[...]),b)
c = matmul(a,transpose(b))
c = matmul(a,reshape(b,shape=[...],order=[...]))

 

使用来自标准Fortran的NVIDIA TensorCores

利用cuTEN SOR和NVIDIA TensorCores可以像下面的代码示例一样容易,当您使用包含在其中的随机数生成特性时 卡顿索克斯 模块:

 

program main
 use  卡顿索克斯
 integer, parameter :: ni=5120, nj=5120, nk=5120, ntimes=10
  真实的(8), allocatable, dimension(:,:) :: a, b, d
 allocate(a(ni,nk),b(nk,nj),d(ni,nj))
 call random_number(a)
 call random_number(b)
 d = 0.0d0

 print *,"cutensor"
 call cpu_time(t1)
 do nt = 1, ntimes
 d = d + matmul(a,b)
 end do
 call cpu_time(t2)

 flops = 2.0*ni*nj*nk
 flops = flops*ntimes
 print *,"times",t2,t1,t2-t1
 print *,"GFlops",flops/(t2-t1)/1.e9
 end program

 

The 马修() 内在调用映射到cuTENSOR调用,在可能的情况下无缝地使用Tensor Cores。我将在本文后面展示一些性能结果。

用nvfortran编译程序

你可能会问这个程序是如何使用cuTEN SOR的,当我早些时候说的 cutensorex 接口只将GPU设备阵列上的操作映射到CuTEN SOR调用。 答案在于程序是如何编译的:

 

% nvfortran -acc -gpu=managed -cuda -cudalib main.f90

 

在这里,我将程序编译为Open ACC程序,并利用OpenACC管理内存模式,其中所有可分配数组都在CUDA统一内存中分配。 加上了 -cuda 这也支持CUDAFortran扩展,数组本质上是CUDAFortran– 托管数组。 CUDA Fortran通用接口匹配的一个规则是,当主机和设备接口都存在时,对于托管的实际参数更喜欢设备接口。

当声明、分配和使用在同一个程序单元中时,nvfortran编译器提供了一些快捷方式。 一般来说,最好使用OpenACC指令来指示编译器传递设备地址,如下面的代码示例:

 

!$acc host_data use_device(a, b, d)
 do nt = 1, ntimes
 d = d + matmul(a,b)
 end do
!$acc end host_data

 

在这种情况下 -cuda 不需要编译器选项。

使用CUDAFortran的CuTEN SOR

对于CUDAFortran用户,the cutensorex 模块和Fortran转换本质成为高性能和完全可移植代码的快速路径。 使用这个 !@cuf 哨兵添加由nvfortranCUDAFortran编译器解释和编译的代码行,或被标准Fortran编译器忽略为注释:

 

 program main
!@cuf use cutensorex
!@cuf use cudafor
 integer, parameter :: ni=5120, nj=5120, nk=5120, ntimes=10
 real(8), allocatable, dimension(:,:) :: a, b, d
!@cuf attributes(device) :: a, b, d
 allocate(a(ni,nk),b(nk,nj),d(ni,nj))
 call random_number(a)
 call random_number(b)
 d = 0.0d0

 print *,"cutensor"
 call cpu_time(t1)
 do nt = 1, ntimes
 d = d + matmul(a,b)
 end do
 call cpu_time(t2)

 flops = 2.0*ni*nj*nk
 flops = flops*ntimes
 print *,"times",t2,t1,t2-t1
 print *,"GFlops",flops/(t2-t1)/1.e9
 end program

 

在第6行,我用设备属性声明了数组,它将它们放在GPU设备内存中。 但是,它们也可以用托管属性来声明。 本程序可编译并链接如下命令:

 

% nvfortran -Mcudalib main.cuf

 

在真实(8)数据上测量的性能

下面是性能,从前面示例中使用的真实(8)(双精度)数据开始。 你可以用几种方式来衡量矩阵乘性能:

单线程CPU实现

多线程或多核CPU实现

朴素编码矩阵乘用指令卸载

The 马修() 内在映射到CuTEN SOR

To get the best threaded-CPU performance, use the basic linear algebra subprogram (BLAS) library routine DGEMM. The equivalent DGEMM call to the earlier operation is the following command:

 

call dgemm('n','n',ni,nj,nk,1.0d0,a,ni,b,nk,1.0d0,d,ni)

 

为了了解调优库在天真的实现中可以提供什么,请使用下面的Open ACC循环结构在GPU上运行。 回路结构采用无特殊平铺或硬件指令。

 

!$acc kernels
 do j = 1, nj
 do i = 1, ni
 do k = 1, nk
 d(i,j) = d(i,j) + a(i,k) * b(k,j)
 end do
 end do
 end do
!$acc end kernels

 

 

实施/处理器 TFLOP
NVFORTRAN单CPU核上的Matmul 0.010
在64个CPU核心上的MKLDGEMM 1.674
天真开放ACC在V100 0.235
天真开放ACC在A100 0.447
NVFORTRAN Matmul on V100 6.866
A100上的NVFORTRAN Matmul 一十七点六六

 

您不仅得到自动GPU加速在V100和A100GPU使用 马修() 内在的,但在A100上的映射 马修() 对于cuTensor调用,您可以自动使用FP64TensorCores。

在真实(4)和真实(2)数据上测量的性能

您可以使用相同的运行集执行 真实的(4) (单一精度)数据和调用SGEMM而不是DGEMM。 此外,CUDA11.0cut Tensor Fortran包装器可以利用A100TF32数据类型和TensorCores。 表2显示了这些运行的性能。

实施/处理器 TFLOP
NVFORTRAN单CPU核上的Matmul 0.025
在64个CPU核心上的MKLSGEMM 3.017
天真开放ACC在V100 0.460
天真开放ACC在A100 0.946
NVFORTRAN Matmul on V100 一十点七零六
A100上的NVFORTRAN Matmul 一十四点六二一
NVFORTRAN Matmul on A100 using TF32 六十点三五八


为什么停在那里? nvfortran编译器支持16位浮点格式(FP16 真实的(2) 数据类型。 您可以在前面的测试中更改数组的类型,并在半精度上运行时间。

在V100上引入了半精度数据的TensorCore操作,然后在A100GPU上扩展,以支持TF32和全双精度DP64TensorCores。 而nvfortran支持 真实的(2) 而TensorCores在V100和A100上,它不支持完整和优化 真实的(2) 在CPU上,标准的BLAS库也没有。 在这种情况下,比较GPU加速版本的性能是有意义的(表3)。

实施/处理器 TFLOP
天真开放ACC在V100 0.490
天真开放ACC在A100 2.058
NVFORTRAN Matmul on V100 六十八点二四二
A100上的NVFORTRAN Matmul 九十二点八一


虽然A100的性能令人印象深刻,代码是完全可移植的,但对于TF32和FP16来说,它明显低于峰值。 有固定的开销:在每次调用时,创建和销毁cutTEN SOR张量描述符并创建收缩计划。 您还必须查询和管理收缩中使用的工作区需求,这最终可能会调用 古达·马洛克 and 无库达 。 如果开销是5– 对于FP64,这变得更接近25%的TF32和大约35%的FP16,对于这个大小的问题。

对于需要最终性能的开发人员,nvfortran确实直接支持Fortran接口到FortranCutensor模块中的CcuTEN SORAPI,也是在HPCSDK中提供的。 您可以自己管理张量描述符、计划和工作区。

结局推论

在这篇文章中,我展示了一些简单的程序和Fortran intrinsic调用的类型以及可以在GPU上自动加速的代码模式。 他们甚至可以通过cuTEN SOR自动利用TensorCores。 使用几乎完全标准的Fortran和完全可移植到其他编译器和系统的程序,您可以在NVIDIA GPU上实现矩阵乘法、矩阵转置、元素数组本质和数组语法的多个组合上的近峰性能。

不可能预测你可以用这些新特性做些什么或实现什么。 我期待着看到你的反馈和结果。 NVIDIA继续添加更多的特性,允许您使用标准Fortran结构以最大性能编程NVIDIA GPU。

关于作者

关于布伦特·莱克
Brent Leback管理NVIDIA HPC编译器客户支持和高级服务,并与HPC社区一起移植和优化GPU计算应用程序。 他是CUDAFortran编程语言的共同创造者,并继续积极参与新的CUDAFortran功能的设计。 他是开放ACC GPU黑客马拉松的常客,也是CUDA Fortran的专家。


审核编辑 黄昊宇

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 相关推荐

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分