4

これは長い投稿です。質問の前に多くの背景があります。簡単なバージョンは、リンクされたリストの要素で OpenMP を使用しようとしたことです。他の場所で規定されている方法で OpenMP タスクを使用しましたが、大幅な速度低下につながりました。ただし、物事を別の方法で分割すると、大幅なスピードアップを得ることができますが、最初の方法で作業する方法があるかどうか疑問に思っています。これは、よりクリーン/シンプルであり、(私が思うに) スレッド間で動的に作業のバランスを取るためです。

Fortran 型 (C 構造体) のかなり長いリンク リスト (数百万の要素になる可能性があります) があり、- 数回- リストを繰り返し処理し、各要素を操作する必要があります。したがって、サブルーチンを引数 (srt) として取り、リストの各要素に対してそれを操作するサブルーチン (eachPhonon) があります。

subroutine eachPhonon(srt)
  external :: srt
  type(phonon), pointer :: tptr

  tptr => head

  do while(associated(tptr))
    call srt(tptr)
    tptr => tptr%next
  enddo
endsubroutine

srt の各呼び出しは他とは独立して実行できるため、これは並列処理の高速化に適しているようです。Fortran do (C for) ループがあれば、これは openmp を使用すると非常に簡単になります。ただし、スタックオーバーフローintelの両方で、リンクされたリストを使用してそれを行う方法を見てきました。基本的に、それは srt への各呼び出しを独自のタスクにします - 次のようなものです:

subroutine eachPhonon(srt)
  external :: srt
  type(phonon), pointer :: tptr

  tptr => head

  !$OMP PARALLEL
  !$OMP SINGLE    
    do while(associated(tptr))
      !$OMP TASK FIRSTPRIVATE(tptr)
        call srt(tptr)
      !$OMP END TASK
      tptr => tptr%next
    enddo
  !$OMP END SINGLE
  !$OMP END PARALLEL
endsubroutine

これは機能しているように見えますが、スレッドを 1 つだけ使用する場合よりも大幅に遅くなります。

たとえば、4 つのスレッドがある場合、1 つのスレッドが要素 1,5,9... で動作し、別のスレッドが要素 2,6,10... などで動作するように書き直しました。

subroutine everyNth(srt, tp, n)
  external :: srt

  type(phonon), pointer :: tp
  integer :: n, j

  do while(associated(tp))
    call srt(tp)

    do j=1,n
      if(associated(tp)) tp => tp%next
    enddo
  enddo
endsubroutine

subroutine eachPhononParallel(srt)
  use omp_lib
  external :: srt

  type(phonon), pointer :: tp
  integer :: j, nthreads

  !$OMP PARALLEL
  !$OMP SINGLE
    nthreads = OMP_GET_NUM_THREADS()
    tp => head
    do j=1,nthreads
      !$OMP TASK FIRSTPRIVATE(tp)
        call everyNth(srt, tp, nthreads)
      !$OMP END TASK
      tp => tp%next
    enddo
  !$OMP END SINGLE
  !$OMP END PARALLEL
endsubroutine

これにより、大幅な高速化が可能になります。

最初の方法を効率的にする方法はありますか?

私は並列処理に慣れていませんが、最初のメソッドは要素ごとにタスクを作成しようとするため、オーバーヘッドが大きすぎると読んでいます。2 番目の方法では、スレッドごとに 1 つのタスクのみを作成し、そのオーバーヘッドを回避します。欠点は、openmp なしではコンパイルできないクリーンなコードではないことです。また、スレッド間で作業のバランスを動的にとることができません。すべての作業は最初に静的に割り当てられます。

4

2 に答える 2

2

スローダウンは、srt()実行にかかる時間が短すぎることを意味し、そのため、オーバーヘッドが可能な並列スピードアップを圧倒します。Massimiliano のソリューションに加えて、リンクされたリストをポインターの配列に変換PARALLEL DOし、結果の構造で使用することもできます。

type phononptr
  type(phonon), pointer :: p
endtype phononptr

...

subroutine eachPhonon(srt)
  external :: srt
  type(phonon), pointer :: tptr
  type(phononptr), dimension(:), allocatable :: ptrs
  integer :: i

  allocate(ptrs(numphonons))

  tptr => head
  i = 1

  do while(associated(tptr))
    ptrs(i)%p => tptr
    i = i + 1
    tptr => tptr%next
  enddo

  !$OMP PARALLEL DO SCHEDULE(STATIC)
  do i = 1, numphonons
    call srt(ptrs(i)%p)
  enddo
  !$OMP END PARALLEL DO

endsubroutine

numphononsリスト項目の数を別の変数 (この場合) に明示的に保持しない場合、リストを 2 回トラバースする必要があります。phononptrFortran にはポインターの配列を宣言する簡単な方法がないため、型が必要です。

同じことはchunksize、マッシミリアーノの解を に設定することによっても実現できnumphonons / omp_get_num_threads()ます。

于 2013-06-11T19:05:55.503 に答える