我的代码因全局内存中的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
致以最诚挚的问候,
答案 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
请注意,唯一的更改是myFourdArray
,d_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