用Fortran&计算PI CUDA

时间:2014-04-28 00:58:21

标签: cuda fortran pgi

我正在尝试在PGI的fortran编译器中创建一个简单的程序。这个简单的程序将使用图形卡使用“飞镖板”算法计算pi。在与这个程序进行了很长一段时间的斗争之后,我终于让它在大多数情况下都表现得很好。但是,我目前仍然坚持正确地传回结果。我必须说,这是一个相当棘手的调试程序,因为我不能再将任何打印语句推入子程序。该程序当前返回全零。我不确定发生了什么,但我有两个想法。我不知道如何解决这两个问题:

  1. CUDA内核没有以某种方式运行?
  2. 我没有正确转换价值? pi_parts = pi_parts_d
  3. 嗯,这是我当前节目的状态。最后带有_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神可以怜悯我的灵魂。

1 个答案:

答案 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)

在这些情况下,当我们启动比我们的数据集大小更大的网格(就总线程而言)时(5​​12个线程但本例中只有400个数据元素),通常会进行线程检查靠近我们内核的开头(在这种情况下,在id初始化之后),以防止越界访问,如下所示:

if (id .lt. N) then

(以及内核代码最末端的相应endif)这样,只允许与实际有效数据对应的线程执行任何工作。

通过上述更改,您的代码应该基本上是可用的,并且您应该能够将内核代码恢复到正确的语句并开始估计PI。

请注意,您可以检查CUDA API以获取错误返回代码,还可以使用cuda-memcheck运行代码,以了解内核是否正在进行越界访问。然而,其中的任何一个都会对这个特殊的seg故障有所帮助。