CUDA Fortran的优化准则

CUDA Fortran的优化准则

前言

本文内容出自《GPU并行算法--N-S方程高性能计算》(白智勇,李志辉 著),摘自chapter 6,有少许精简,但不影响整体逻辑。

CUDA Fortran作为标准Fortran的一种扩展,CUDA Fortran程序的编写应该遵循Fortran程序性能优化的一般准则。常见的有以下几种:

优化准则一:按存储顺序优化数组的访问

无论代码中定义 的数组是多少维的,Fortran数组在内存中都是按列优先的顺序一维排列的。现代计算机采用内存页面预读取技术,为了提升缓存命中率,应该尽可能访问与上一指令数据相邻的内存空间,因此,在设计多重循环结构访问多维数组时,应尽可能将代表行变量的循环放在循环的最内层。例如:

应该写成:

do j=1,3
	do i=1,3
	call mysub(a(i,j),otherparlist)
	end do
end do

而不是:

do i=1,3
	do j=1,3
	call mysub(a(i,j),otherparlist)
	end do
end do

优化准则二:充分利用数组的整体运算

数组整体运算是Fortran在数值计算领域最重要的优势之一,把数组当做指针处理的程序设计语言,如C,C++等不能使用数组的整体运算,编译器针对数组整体运算进行了大量优化,因而使用数组整体运算可以获得极大的性能提升。例如:

do j=1,M
	do i=1,N
	a(i,j)=2.0*b(i,j)+c(i,j)
	end do
end do

将上面这段代码改写为:

a(:,:)=2.0*b(:,:)+c(:,:)

不仅性能可以大幅提高,可读性也更好

但需要注意的是数组整体运算时系统实际需要使用临时内存空间,故设备端代码使用数组整体运算时数组不能太大(具体的大小和CUDA Fortran编译器有关)

优化准则三:使用临时变量

当数组元素被多次访问时,用临时变量替换数组元素可以提升性能。原因在于,无论是CPU还是GPU代码,数组采用的是全局内存,临时变量则有机会可以使用高速缓存。

优化准则四:尽量使用内置子程序

Fortran语言之所以在科学计算领域始终处于霸主地位,历史积累下来的大量优秀代码是重要因素,这些优秀代码很大一部分已经被内置到编译器中成为内置子程序并进行了针对性优化,程序员想要达到同等功能并写出比它们性能更优的代码是很难的,绝大多数时候也没有必要,因此在动手编写Fortran代码之前应检查编译器提供的用户手册,尽量使用内置子程序而避免重复劳动或降低代码性能。

如:矩阵乘法直接调用内置矩阵乘法函数matmul既简洁,性能也通常远比自己写出的数组乘法代码性能更优:

c=matmul(a,b)

优化准则五:尽量避免速度较慢的运算操作

在早期的计算机硬件中,乘法/除法计算还是远高于加法/减法,现代计算机中通过提供乘法器硬件已经基本解决了这个问题,但仍然存在大量速度较慢的运算,比如乘方,开方,指数,求幂等。在CPU代码中的耗时已经远高于乘法、除法等基本运算,而在GPU代码中这种差距则更加明显,此时,能用其他运算代替的尽量用其他运算代替,不能用其他运算代替的,也应该用算法改进以减少使用的次数,例如a2,可以写成a×a,计算结果不变,但执行速度更快。

优化准则六:谨慎使用逻辑判断和分支结构

GPU代码中需要谨慎使用逻辑判断和分支结构,原因主要有两点:

  1. 一方面,在CUDA中,GPU在执行如下分支程序结构时,会把所有分支都执行一边,虽然最终结果仍然正确,但会导致代码执行的性能降低。

    if(logical_expr1) then
    	....
    else
    	....
    endif
    
  2. 另一方面,GPU的浮点运算单元众多而逻辑运算单元相对较少,逻辑表达式对代码整体的性能影响较大,应尽可能在代码设计时避免或减少。

posted @ 2022-12-10 21:46  yukina~  阅读(353)  评论(0编辑  收藏  举报