代码之家  ›  专栏  ›  技术社区  ›  MechanicalEagle36

Fortran的Openmp数组缩减

  •  1
  • MechanicalEagle36  · 技术社区  · 10 年前

    我正在尝试并行化我写的代码。我在阵列上执行归约时遇到问题。对于较小的数组来说,这一切似乎都很好,但是当数组大小超过某一点时,我要么得到堆栈溢出错误,要么崩溃。

    我已经尝试在编译时使用/F来增加堆栈大小,我在windows上使用ifort,我还尝试传递set KMP_STACKSIZE=xxx特定于intel的堆栈大小递减。这有时会有所帮助,并允许代码在循环中进一步前进,但最终并不能解决问题,即使堆栈大小为1Gb或更大。

    下面是我的代码的一个独立的小示例。它以串行方式工作,并且只有一个线程。或者有很多线程,但只有一个小“N”。大N(例如,示例中为250000)会导致问题。

    我不认为这些数组如此庞大以至于会导致重大问题,我认为增加堆栈空间会有所帮助——还有其他选择吗,或者我在编码中遗漏了一些重要的东西?

    program testreduction
        use omp_lib
        implicit none
        integer :: i, j, nthreads, Nsize
        integer iseed /3/
        REAL, allocatable :: A(:,:), B(:), C(:), posi(:,:)
        REAL :: dx, dy, r, Axi, Ayi, m, F
        !Set size of matrix, and main loop
        Nsize = 250000
        m = 1.0
        F = 1.0
        !Allocate posi array
        allocate(posi(2,Nsize))
        !Fill with random numbers
        do i=1,Nsize
            do j=1,2
                posi(j,i) = (ran(iseed))
            end do
        end do
        !Allocate other arrays
        allocate(A(2,Nsize), C(Nsize), B(Nsize))
    
        print*, sizeof(A)+sizeof(B)+sizeof(C)
        !$OMP parallel
        nthreads = omp_get_num_threads()
        !$OMP end parallel
    
        print*, "Number of threads ", nthreads
        !Go through each array and do some work, calculating a reduction on A, B and C.
        !$OMP parallel do schedule(static) private(i, j, dx, dy, r, Axi, Ayi) reduction(+:C, B, A)
        do i=1,Nsize
            do j=1,Nsize
                !print*, i
                dx = posi(1,i) - posi(1,j)
                dy = posi(2,i) - posi(2,j)
                r = sqrt(dx**2+dy**2)
                Axi = -m*(F)*(dx/(r))
                Ayi = -m*(F)*(dy/(r))
                A(1,i) = A(1,i) + Axi
                A(2,i) = A(2,i) + Ayi
                B(i) = B(i) + (Axi+Ayi)
                C(i) = C(i) + dx/(r) + dy/(r)
            end do    
        END DO
        !$OMP END parallel do
    
    end program
    

    更新

    这是我所说的更好的例子。。

    program testreduction2
        use omp_lib
        implicit none
        integer :: i, j, nthreads, Nsize, q, k, nsize2
        REAL, allocatable :: A(:,:), B(:), C(:)
        integer, ALLOCATABLE :: PAIRI(:), PAIRJ(:)
    
        Nsize = 25
        Nsize2 = 19
        q=0
    
        allocate(A(2,Nsize), C(Nsize), B(Nsize))
        ALLOCATE(PAIRI(nsize*nsize2), PAIRJ(nsize*nsize2))
    
        do i=1,nsize
            do j =1,nsize2
                q=q+1
                PAIRI(q) = i
                PAIRJ(q) = j
            end do
        end do
    
        A = 0
        B = 0
        C = 0
    
        !$OMP parallel do schedule(static) private(i, j, k)
        do k=1,q
            i=PAIRI(k)
            j=PAIRJ(k)
            A(1,i) = A(1,i) + 1
            A(2,i) = A(2,i) + 1
            B(i) = B(i) + 1
            C(i) = C(i) + 1
        END DO
        !$OMP END parallel do
    
        PRINT*, A
        PRINT*, B
        PRINT*, C       
    END PROGRAM
    
    1 回复  |  直到 10 年前
        1
  •  2
  •   Vladimir F Героям слава    3 年前

    问题是您正在减少非常大的阵列。注意,其他语言(C、C++)在OpenMP 4.5之前无法减少数组。

    但我看不出你的情况有任何减少的原因,你只更新每个元素一次。

    试试看

    !$OMP parallel do schedule(static) private(i, dx, dy, r, Axi, Ayi)
    do i=1,Nsize
      do j=1,Nsize
        ...
        A(1,i) = A(1,i) + Axi
        A(2,i) = A(2,i) + Ayi
        B(i) = B(i) + (Axi+Ayi)
        C(i) = C(i) + dx/(r) + dy/(r)
      end do
    end do
    !$OMP END parallel do
    

    重点是螺纹不相互连接。每个线程使用不同的 i 因此 A , B C .

    即使你提出了一个看起来很有必要的案例,你也可以重写它来避免它。你甚至可以自己分配一些缓冲区并模拟减少。或者使用原子更新。