Cuda Fortran 4D阵列

时间:2013-09-23 11:56:27

标签: arrays cuda fortran pgi

我的代码因全局内存中的4D阵列访问速度而变慢。

我正在使用PGI编译器2010.

我正在访问的4D阵列只能从设备读取,并且在运行时已知大小。

我想分配给纹理内存,发现我的PGI版本不支持纹理。由于仅在运行时知道大小,因此也不可能使用常量内存。

在编译时只知道一个维度,如MyFourD(100, x,y,z),其中x,y,z是用户输入。

我的第一个想法是关于指针,但不熟悉指针fortran。

如果您有经验如何应对这种情况,我将非常感谢您的帮助。因为这只会使我的代码比预期慢5倍

以下是我正在尝试做的示例代码

int i,j,k

i = (blockIdx%x-1) * blockDim%x + threadIdx%x-1
j = (blockIdx%y-1) * blockDim%y + threadIdx%y-1

    do k = 0, 100 
        regvalue1 = somevalue1
        regvalue2 = somevalue2 
        regvalue3 =  somevalue3 

        d_value(i,j,k)=d_value(i,j,k)
     &     +myFourdArray(10,i,j,k)*regvalue1      
     &     +myFourdArray(32,i,j,k)*regvalue2      
     &     +myFourdArray(45,i,j,k)*regvalue3                    
    end do

致以最诚挚的问候,

2 个答案:

答案 0 :(得分:2)

我相信@Alexander Vogt的答案是正确的 - 我会考虑重新订购阵列存储。但我会这样试试:

int i,j,k

i = (blockIdx%x-1) * blockDim%x + threadIdx%x-1
j = (blockIdx%y-1) * blockDim%y + threadIdx%y-1

    do k = 0, 100 
        regvalue1 = somevalue1
        regvalue2 = somevalue2 
        regvalue3 =  somevalue3 

        d_value(i,j,k)=d_value(i,j,k)
     &     +myFourdArray(i,j,k,10)*regvalue1      
     &     +myFourdArray(i,j,k,32)*regvalue2      
     &     +myFourdArray(i,j,k,45)*regvalue3                    
    end do

请注意,唯一的更改是myFourdArrayd_value数组中的数据排序无需更改。

这种变化的关键在于我们允许相邻线程访问myFourdArray中的相邻元素,因此我们允许合并访问。您的原始配方强制相邻线程访问由第一个维度的长度分隔的元素,因此不允许有用的合并。

无论是在CUDA C还是CUDA Fortran中,线程首先分组为X,然后是Y,然后是Z维。所以快速变化的线程下标首先是X.因此,在矩阵访问中,我们希望这个快速变化的下标显示在迅速变化的索引中。

在Fortran中,这个索引是多个下标数组的第一个

在C中,这个索引是多个下标数组的 last

您的原始代码遵循d_value的约定,方法是将X线程索引(i)放在第一个数组下标位置。但它通过在第一个数组下标位置放置一个常量来打破myFourdArray的这个约定。因此,您对myFourdArray的访问速度明显变慢。

当代码中存在循环时,我们也不希望首先放置循环变量(对于Fortran,或最后为C)(即k,在这种情况下,正如Alexander Vogt所做的那样)因为这样做也会打破合并。对于循环的每次迭代,我们有多个线程以锁步方式执行,并且这些线程应该都访问相邻元素。通过 X线程索引下标(例如i首先(对于Fortran,或最后为C),可以实现这一点。

答案 1 :(得分:1)

您可以反转索引,即让第一个维度更改为最快。 Fortran是column major

do k = 0, 100 
    regvalue1 = somevalue1
    regvalue2 = somevalue2 
    regvalue3 =  somevalue3 

    d_value(k,i,j)=d_value(k,i,j) +         &
      myFourdArray(k,i,j,10)*regvalue1 +    &
      myFourdArray(k,i,j,32)*regvalue2 +    &
      myFourdArray(k,i,j,45)*regvalue3                   
end do

如果最后一个(在原始情况下为第二个)维度始终是固定的(并且不是太大),请考虑使用单个数组。

根据我的经验,当应用于大型数组时,指针在加速方面没有太大变化。你可以尝试strip-mining来优化你的循环的缓存访问,但我不知道使用PGI编译器启用它的编译选项。

啊,好吧,这是一个simple directive

!$acc do vector
do k=...
enddo