2012-11-26 99 views
2

當我嘗試使用OpenMP對Fortran90中的程序進行並行化時,出現分段錯誤錯誤。OpenMP專用陣列 - 分段錯誤:11

!$OMP PARALLEL DO NUM_THREADS(4) & 
    !$OMP PRIVATE(numstrain, i) 
    do irep = 1, nrep 
     do i=1, 10 
      PRINT *, numstrain(i) 
     end do 
    end do 
    !$OMP END PARALLEL DO 

我發現如果我註釋掉「PRINT *,numstrain(i)」或刪除openmp標誌,它將無誤地工作。我認爲這是因爲當我並行訪問numstrain(i)時會發生內存訪問衝突。我已經將i和numstrain聲明爲私有變量。有人可以給我一些想法,爲什麼是這種情況?非常感謝。 :)

UPDATE:

我修改之前的版本,這個版本可以打印出正確的結果。

integer, allocatable :: numstrain(:) 
integer :: allocate_status 
integer :: n 
!$OMP PARALLEL DO NUM_THREADS(4) & 
!$OMP PRIVATE(numstrain, i) 
n = 1000000 
do irep = 1, nrep 
    allocate (numstrain(n), stat = allocate_status) 
    do i=1, 10 
     PRINT *, numstrain(i) 
    end do 
    deallocate (numstrain, stat = allocate_status) 
end do 
!$OMP END PARALLEL DO 

但是如果我移動numstrain訪問由該子程序(下面所附的代碼)被稱爲另一個子程序,1.它總是在一個線程處理。 2.在某個點(i = 4或5),它返回分段錯誤:11。當我有不同的NUM_THREADS時,返回Segmentation Fault:11時的變量i不同。

integer, allocatable :: numstrain(:) 
integer :: allocate_status 
integer :: n 
!$OMP PARALLEL DO NUM_THREADS(4) & 
!$OMP PRIVATE(numstrain, i) 
n = 1000000 
do irep = 1, nrep 
    allocate (numstrain(n), stat = allocate_status) 
    call anotherSubroutine(numstrain) 
    deallocate (numstrain, stat = allocate_status) 
end do 
!$OMP END PARALLEL DO 

subroutine anotherSubroutine(numstrain) 
    integer, allocatable :: numstrain(:) 
    do i=1, 10 
     PRINT *, numstrain(i) 
    end do 
end subroutine anotherSubroutine 

我也試圖在這兩個子程序的幫助和主子程序分配/解除分配,並且只分配在幫助子程序分配/釋放。沒有什麼改變。

回答

3

最典型的原因是堆棧上沒有足夠的空間來容納numstrain的私人副本。計算和比較以下兩個值:

  • 陣列的大小以字節
  • 堆棧大小限制

有兩種的堆棧大小限制。主線程的堆棧大小由Unix系統上的進程限制(使用ulimit -s來檢查和修改此限制)或在Windows上的鏈接時固定(可執行文件的重新編譯或二進制編輯是必要的,以便更改限制)。其他OpenMP線程的堆棧大小由像標準OMP_STACKSIZE或實現特定的GOMP_STACKSIZE(GNU/GCC OpenMP)和KMP_STACKSIZE(英特爾OpenMP)的環境變量控制。

請注意,大多數Fortran OpenMP實現總是將私有數組放在堆棧上,無論您是否啓用在堆上分配大型陣列的編譯器選項(使用GNU的gfortran和英特爾的ifort進行測試)。

如果您註釋掉PRINT語句,則可以有效地刪除對numstrain的引用,並且編譯器可以自由地對其進行優化,例如,它可能不會製作numstrain的私人副本,因此不會超出堆棧限制。


當您提供的附加信息可以得出結論後,堆棧大小不是罪魁禍首。當privateALLOCATABLE陣列處理,你應該知道:

  • 未分配的數組的專用副本保留未分配的;
  • 已分配數組的私人副本被分配了相同的邊界。

如果不使用numstrain外並行區域,是細做您已在第一種情況下做了什麼,但有一些修改:

integer, allocatable :: numstrain(:) 
integer :: allocate_status 
integer, parameter :: n = 1000000 
interface 
    subroutine anotherSubroutine(numstrain) 
     integer, allocatable :: numstrain(:) 
    end subroutine anotherSubroutine 
end interface 

!$OMP PARALLEL NUM_THREADS(4) PRIVATE(numstrain, allocate_status) 
allocate (numstrain(n), stat = allocate_status) 
!$OMP DO 
do irep = 1, nrep 
    call anotherSubroutine(numstrain) 
end do 
!$OMP END DO 
deallocate (numstrain) 
!$OMP END PARALLEL 

如果還使用numstrain並行區域之外,則分配和釋放出去:

allocate (numstrain(n), stat = allocate_status) 
!$OMP PARALLEL DO NUM_THREADS(4) PRIVATE(numstrain) 
do irep = 1, nrep 
    call anotherSubroutine(numstrain) 
end do 
!$OMP END PARALLEL DO 
deallocate (numstrain) 

你也應該知道,當你調用一個子程序,它接受一個ALLOCATABLE數組作爲參數,則甲肝e爲該例程提供一個明確的接口。你可以寫一個INTERFACE塊,或者你可以把被調用的例程放在一個模塊中,然後USE該模塊 - 兩種情況都會提供顯式接口。如果您沒有提供顯式接口,編譯器將無法正確傳遞數組,並且子例程將無法訪問其內容。

+0

非常感謝您的回答。我想知道在使用它之前分配數組是否會將數組放入堆棧。我發現如果我在do循環之前分配(numstrain(n),stat = allocate_status),並且在do循環之後釋放它,它就可以正常工作!但是,如果除了將代碼移動到由此子例程調用的另一個幫助子例程之外,我還會執行相同的操作,則會再次發生分段錯誤。 – Ling0k

+0

您能否編輯您的問題並在那裏添加附加信息?另外放置'numstrain'的定義,因爲給定了額外的細節,它可能不會成爲堆棧大小問題。 –

+0

當然。抱歉讓人困惑。我更新了我的問題。 – Ling0k