0

我的代码因访问全局内存中的 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

此致,

4

2 回答 2

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

请注意,唯一的变化是myFourdArray,不需要更改d_value数组中的数据顺序。

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

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

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

在 C 中,该索引是多下标数组的最后一个。

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

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

于 2013-09-23T13:44:07.440 回答
1

您可以反转索引,即让第一个维度更改最快。Fortran 是专栏专业的!

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

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

根据我的经验,指针在应用于大型数组时在加速方面并没有太大变化。您可以尝试剥离挖掘以优化缓存访问方面的循环,但我不知道使用 PGI 编译器启用此功能的编译选项。

啊,好的,这是一个简单的指令

!$acc do vector
do k=...
enddo
于 2013-09-23T12:37:53.437 回答