3

我正在尝试将此 FORTRAN 程序(钟摆运动)转换为 CUDA FORTRAN,但我只能使用 1 个块和两个线程。有什么办法可以使用超过 2 个线程....

MODULE CB
    REAL :: Q,B,W
END MODULE CB

PROGRAM PENDULUM
    USE CB
    IMPLICIT NONE
    INTEGER, PARAMETER :: N=10,L=100,M=1
    INTEGER :: I,count_rate,count_max,count(2)
    REAL :: PI,H,T,Y1,Y2,G1,G1F,G2,G2F
    REAL :: DK11,DK21,DK12,DK22,DK13,DK23,DK14,DK24

    REAL, DIMENSION (2,N) :: Y

    PI = 4.0*ATAN(1.0)
    H  = 3.0*PI/L
    Q  = 0.5
    B  = 0.9
    W  = 2.0/3.0
    Y(1,1) = 0.0
    Y(2,1) = 2.0

    DO I = 1, N-1
        T  = H*I
        Y1 = Y(1,I)
        Y2 = Y(2,I)
        DK11 = H*G1F(Y1,Y2,T)
        DK21 = H*G2F(Y1,Y2,T)
        DK12 = H*G1F((Y1+DK11/2.0),(Y2+DK21/2.0),(T+H/2.0))
        DK22 = H*G2F((Y1+DK11/2.0),(Y2+DK21/2.0),(T+H/2.0))
        DK13 = H*G1F((Y1+DK12/2.0),(Y2+DK22/2.0),(T+H/2.0))
        DK23 = H*G2F((Y1+DK12/2.0),(Y2+DK22/2.0),(T+H/2.0))
        DK14 = H*G1F((Y1+DK13),(Y2+DK23),(T+H))
        DK24 = H*G2F((Y1+DK13),(Y2+DK23),(T+H))
        Y(1,I+1) = Y(1,I)+(DK11+2.0*(DK12+DK13)+DK14)/6.0
        Y(2,I+1) = Y(2,I)+(DK21+2.0*(DK22+DK23)+DK24)/6.0

        ! Bring theta back to the region [-pi,pi]
        Y(1,I+1) = Y(1,I+1)-2.0*PI*NINT(Y(1,I+1)/(2.0*PI))

    END DO

    call system_clock ( count(2), count_rate, count_max )

    WRITE (6,"(2F16.8)") (Y(1,I),Y(2,I),I=1,N,M)

END PROGRAM PENDULUM

FUNCTION G1F (Y1,Y2,T) RESULT (G1)
    USE CB
    IMPLICIT NONE
    REAL :: Y1,Y2,T,G1
    G1 = Y2
END FUNCTION G1F

FUNCTION G2F (Y1,Y2,T) RESULT (G2)
    USE CB
    IMPLICIT NONE
    REAL :: Y1,Y2,T,G2
    G2 = -Q*Y2-SIN(Y1)+B*COS(W*T)
END FUNCTION G2F

程序的 CUDA FORTRAN 版本


MODULE KERNEL

    CONTAINS  
    attributes(global) subroutine mykernel(Y_d,N,L,M)

    INTEGER,value:: N,L,M
    INTEGER ::tid
    REAL:: Y_d(:,:)
    REAL :: PI,H,T,G1,G1F,G2,G2F
    REAL,shared :: DK11,DK21,DK12,DK22,DK13,DK23,DK14,DK24,Y1,Y2

    PI = 4.0*ATAN(1.0)
    H  = 3.0*PI/L
    Y_d(1,1) = 0.0
    Y_d(2,1) = 2.0
    tid=threadidx%x

    DO I = 1, N-1
        T  = H*I
        Y1 = Y_d(1,I)
        Y2 = Y_d(2,I)

        if(tid==1)then
            DK11 = H*G1F(Y1,Y2,T)
        else
            DK21 = H*G2F(Y1,Y2,T)
        endif

        call syncthreads ()

        if(tid==1)then
            DK12 = H*G1F((Y1+DK11/2.0),(Y2+DK21/2.0),(T+H/2.0))
        else
            DK22 = H*G2F((Y1+DK11/2.0),(Y2+DK21/2.0),(T+H/2.0))
        endif

        call syncthreads ()

        if(tid==1)then
            DK13 = H*G1F((Y1+DK12/2.0),(Y2+DK22/2.0),(T+H/2.0))
        else
            DK23 = H*G2F((Y1+DK12/2.0),(Y2+DK22/2.0),(T+H/2.0))
        endif

        call syncthreads ()

        if(tid==1)then
            DK14 = H*G1F((Y1+DK13),(Y2+DK23),(T+H))
        else
            DK24 = H*G2F((Y1+DK13),(Y2+DK23),(T+H))
        endif

        call syncthreads ()

        if(tid==1)then
            Y_d(1,I+1) = Y1+(DK11+2.0*(DK12+DK13)+DK14)/6.0
        else
            Y_d(2,I+1) = Y2+(DK21+2.0*(DK22+DK23)+DK24)/6.0
        endif

        Y_d(1,I+1) = Y_d(1,I+1)-2.0*PI*NINT(Y_d(1,I+1)/(2.0*PI))

        call syncthreads ()

    END DO

end subroutine mykernel

attributes(device) FUNCTION G1F (Y1,Y2,T) RESULT (G1)
    IMPLICIT NONE
    REAL :: Y1,Y2,T,G1
    G1 = Y2
END FUNCTION G1F

attributes(device) FUNCTION G2F (Y1,Y2,T) RESULT (G2)
    IMPLICIT NONE
    REAL :: Y1,Y2,T,G2
    G2 = -0.5*Y2-SIN(Y1)+0.9*COS((2.0/3.0)*T)
END FUNCTION G2F

END MODULE KERNEL

PROGRAM PENDULUM

    use cudafor
    use KERNEL

    IMPLICIT NONE
    INTEGER, PARAMETER :: N=100000,L=1000,M=1
    INTEGER :: I,d,count_max,count_rate

    REAL,device :: Y_d(2,N)
    REAL, DIMENSION (2,N) :: Y
    INTEGER :: count(2)

    call mykernel<<<1,2>>>(Y_d,N,L,M)

    Y=Y_d

    WRITE (6,"(2F16.8)") (Y(1,I),Y(2,I),I=1,N,M)

END PROGRAM PENDULUM
4

1 回答 1

2

通过对原始串行代码进行数据依赖性分析,您可以看到只有两个独立的执行线程是可能的。最容易将其视为“外部”和“内部”部分。

“外部”部分是对 on 的Y(1:2,i+1)依赖Y(1:2,i)。在每个时间步,您需要使用 的值Y(1:2,i)来计算Y(1:2,i+1),因此不可能并行执行多个时间步的计算,这仅仅是因为串行依赖结构——您需要知道时间发生了什么i来计算什么发生在 time i+1,您需要知道 time 发生了什么i+1来计算time 发生了什么i+2,等等。您希望做的最好的事情就是并行计算Y(1,i+1)Y(2,i+1)这正是您所做的。

“内部”部分基于 Runge-Kutta 方案中的中间值、代码中的 、 等值之间的依赖DK11关系DK12。计算时Y(1:2,i+1),每一个都DK[n,m]依赖于Y(1:2,i)m > 1,每一个都DK[n,m]依赖于DK[1,m-1]DK[2,m-1]。如果您绘制这些依赖关系的图表(我的 ASCII 艺术技能还不够好!),您会发现在计算的每个步骤中只有两个可能的子计算可以并行执行。

所有这一切的结果是,对于这个计算,你不能比两个并行线程做得更好。正如上面的一位评论者所说,如果您要模拟具有多个独立自由度的粒子系统或其他机械系统,您当然可以做得更好,然后您可以并行集成。

于 2015-04-19T18:41:40.280 回答