我正在尝试在PGI的fortran编译器中创建一个简单的程序。这个简单的程序将使用图形卡使用“飞镖板”算法计算pi。在与这个程序进行了很长一段时间的斗争之后,我终于让它在大多数情况下都表现得很好。但是,我目前仍然坚持正确地传回结果。我必须说,这是一个相当棘手的调试程序,因为我不能再将任何打印语句推入子程序。该程序当前返回全零。我不确定发生了什么,但我有两个想法。我不知道如何解决这两个问题:
pi_parts = pi_parts_d
嗯,这是我当前节目的状态。最后带有_d
的变量代表CUDA准备的设备内存,其中所有其他变量(CUDA内核除外)是典型的Fortran CPU准备变量。现在有一些印刷语句我已经注释掉了,我已经从CPU Fortran土地上试过了。这些命令用于检查我是否真的正确生成随机数。至于CUDA方法,我目前已对计算进行了评论,并将z
替换为静态等于1
只是为了看到发生的事情。
module calcPi
contains
attributes(global) subroutine pi_darts(x, y, results, N)
use cudafor
implicit none
integer :: id
integer, value :: N
real, dimension(N) :: x, y, results
real :: z
id = (blockIdx%x-1)*blockDim%x + threadIdx%x
if (id .lt. N) then
! SQRT NOT NEEDED, SQRT(1) === 1
! Anything above and below 1 would stay the same even with the applied
! sqrt function. Therefore using the sqrt function wastes GPU time.
z = 1.0
!z = x(id)*x(id)+y(id)*y(id)
!if (z .lt. 1.0) then
! z = 1.0
!else
! z = 0.0
!endif
results(id) = z
endif
end subroutine pi_darts
end module calcPi
program final_project
use calcPi
use cudafor
implicit none
integer, parameter :: N = 400
integer :: i
real, dimension(N) :: x, y, pi_parts
real, dimension(N), device :: x_d, y_d, pi_parts_d
type(dim3) :: grid, tBlock
! Initialize the random number generaters seed
call random_seed()
! Make sure we initialize the parts with 0
pi_parts = 0
! Prepare the random numbers (These cannot be generated from inside the
! cuda kernel)
call random_number(x)
call random_number(y)
!write(*,*) x, y
! Convert the random numbers into graphics card memory land!
x_d = x
y_d = y
pi_parts_d = pi_parts
! For the cuda kernel
tBlock = dim3(256,1,1)
grid = dim3((N/tBlock%x)+1,1,1)
! Start the cuda kernel
call pi_darts<<<grid, tblock>>>(x_d, y_d, pi_parts_d, N)
! Transform the results into CPU Memory
pi_parts = pi_parts_d
write(*,*) pi_parts
write(*,*) 'PI: ', 4.0*sum(pi_parts)/N
end program final_project
编辑代码:
更改了各行以反映上述提及的修复:Robert Crovella
。当前状态:cuda-memcheck
在我的计算机上显示错误:Program hit error 8 on CUDA API call to cudaLaunch
。
如果我有任何方法可以用来测试这个程序,请告诉我。我正在投掷飞镖,看看他们在哪里登陆我目前的CUDA调试风格。不是最理想的,但在我找到另一种方式之前必须这样做。
在这黑暗的时刻,Fortran神可以怜悯我的灵魂。
答案 0 :(得分:1)
当我编译并运行你的程序时,我得到一个段错误。这是由于您传递给内核的最后一个参数(N_d
):
call pi_darts<<<grid, tblock>>>(x_d, y_d, pi_parts_d, N_d)
由于N
是标量,内核期望直接使用它,而不是指针。因此,当您将指针传递给设备数据(N_d
)时,设置内核的过程会在尝试访问值N
时生成seg错误(在主机代码中!)直接传递为:
call pi_darts<<<grid, tblock>>>(x_d, y_d, pi_parts_d, N)
当我对您发布的代码进行更改时,我会获得实际的打印输出(而不是seg错误),这是一个1和0的数组(256个,后跟144个零,总计为N
= 400个值),后跟计算的PI值(在这种情况下恰好是2.56(4 * 256/400),因为你已经使内核基本上是一个虚拟内核)。
这行代码也可能没有你想做的事情:
grid = dim3(N/tBlock%x,1,1)
N
= 400且tBlock%x
= 256(来自之前的代码行),计算结果为1(即grid
结束于(1,1,1)
相当于一个线程块)。但是你真的想要启动2个线程块,以覆盖整个数据集范围(N
= 400个元素)。有很多方法可以解决这个问题,但为了简单起见,我们只需要在计算中加1:
grid = dim3((N/tBlock%x)+1,1,1)
在这些情况下,当我们启动比我们的数据集大小更大的网格(就总线程而言)时(512个线程但本例中只有400个数据元素),通常会进行线程检查靠近我们内核的开头(在这种情况下,在id
初始化之后),以防止越界访问,如下所示:
if (id .lt. N) then
(以及内核代码最末端的相应endif
)这样,只允许与实际有效数据对应的线程执行任何工作。
通过上述更改,您的代码应该基本上是可用的,并且您应该能够将内核代码恢复到正确的语句并开始估计PI。
请注意,您可以检查CUDA API以获取错误返回代码,还可以使用cuda-memcheck
运行代码,以了解内核是否正在进行越界访问。然而,其中的任何一个都会对这个特殊的seg故障有所帮助。