【问题标题】:Cuda Fortran 4D arrayCuda Fortran 4D 数组
【发布时间】:2013-09-28 07:30:41
【问题描述】:

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

最好的问候,

【问题讨论】:

  • 当你说'PGI编译器'时,你的意思是你使用OpenACC?您可能想为您的问题添加标签OpenACC
  • Eric,不是 OpenACC 而是 mpif90

标签: arrays cuda fortran pgi


【解决方案1】:

我相信@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 线程索引 下标(例如 ifirst(对于 Fortran,或 last 对于 C)来促进。

【讨论】:

  • 罗伯特谢谢你。我开始看到相当大的加速。我会继续努力。多亏有你,看到这一进展真是太好了。
【解决方案2】:

您可以反转索引,即让第一个维度更改最快。 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

【讨论】:

  • 谢谢。我会尽力让你知道。
  • @Alexander Vogt PGI 有多个 CUDA 产品。 PGI 加速器(用于 C 和 Fortran)接受指令语法。 PGI CUDA Fortran 是一种不同的产品,它在其他本机 fortran 中接受 CUDA 语法,没有加速器指令。发布的问题中包含 CUDA 语法元素(例如 blockIdx%x),因此它必须是 CUDA Fortran(不是加速器 Fortran,即不使用指令)。
  • 哦,我以为那些是一些自定义结构...感谢您的澄清!
猜你喜欢
  • 2021-03-21
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-24
  • 1970-01-01
相关资源
最近更新 更多