OpenMP'de döngü paralelliği

Parametreler

Madde parametre
‘özel’ Özel değişkenlerin virgülle ayrılmış listesi
‘ilközel’ ‘özel’ gibi, ancak döngüye girmeden önce değişkenin değerine başlatıldı
‘sonözel’ ‘private’ gibi, ancak değişken, çıkışta döngünün son yinelemesine karşılık gelen değeri alacaktır
“azaltma” indirgeme operatörü : karşılık gelen indirgeme değişkenlerinin virgülle ayrılmış listesi
“program” ‘statik’, ‘dinamik’, ‘kılavuzlu’, ‘otomatik’ veya ‘çalışma zamanı’, 3 önceki için bir komadan sonra isteğe bağlı bir yığın boyutuyla
‘çöküş’ Daraltmak ve paralelleştirmek için mükemmel şekilde iç içe geçmiş döngü sayısı
‘sipariş’ Döngünün bazı bölümlerinin sıralı tutulması gerektiğini söyler (bu parçalar döngü gövdesi içinde bazı ‘sıralı’ cümlelerle özel olarak tanımlanacaktır)
‘hayır’ Döngü yapısının sonunda varsayılan olarak var olan örtük engeli kaldırın

‘schedule’ deyiminin anlamı şu şekildedir:

  • ‘statik[,yığın]’: Döngü yinelemelerini statik olarak dağıtın (dağıtımın döngüye girmeden önce yapıldığı anlamına gelir) döngü yinelemelerini ‘parça’ boyutunda toplu olarak sıralı bir şekilde dağıtın. “yığın” belirtilmemişse, parçalar mümkün olduğunca eşit olur ve her bir iş parçacığı en fazla bir tane alır.
  • ‘dinamik[,yığın]’: Döngü yinelemelerini, hiçbir yığın kalmayana kadar ilk gelen ilk hizmet ilkesiyle “parça” boyutundaki gruplara göre iş parçacıkları arasında dağıtın. Belirtilmezse, “yığın” 1 olarak ayarlanır
  • “yönlendirilmiş[, yığın]”: “Dinamik” gibi ancak boyutları küçüldükçe 1’e kadar küçülen partilerle
  • auto: Neyin en uygun olduğuna derleyicinin ve/veya çalışma zamanı kitaplığının karar vermesine izin verin
  • ‘runtime’: ‘OMP_SCHEDULE’ ortam değişkeni aracılığıyla çalışma zamanında kararı erteleyin. Çalışma zamanında ortam değişkeni tanımlanmadıysa, varsayılan zamanlama kullanılacaktır.

“schedule” için varsayılan değer uygulama tanımlaması şeklindedir. Birçok ortamda ‘statik’tir, ancak aynı zamanda ‘dinamik’ veya pekâlâ ‘otomatik’ olabilir. Bu nedenle, uygulamanızın açıkça ayarlamadan dolaylı olarak ona güvenmemesine dikkat edin.

Yukarıdaki örneklerde, “paralel için” veya “paralel do” birleşik biçimini kullandık. Bununla birlikte, döngü yapısı, bir ‘#pragma omp for […]’ veya ‘!$omp do […]’ bağımsız yönergesi biçiminde ‘paralel’ yönergeyle kaynaştırılmadan kullanılabilir. “paralel” bölge.

Yalnızca Fortran sürümü için, paralelleştirilmiş döngünün/döngülerin döngü dizin değişkeni/değişkenleri varsayılan olarak her zaman ‘özel’dir. Bu nedenle, bunları açıkça “özel” olarak bildirmeye gerek yoktur (ancak bunu yapmak bir hata değildir).
C ve C++ sürümü için döngü dizinleri diğer değişkenler gibidir. Bu nedenle, kapsamları paralelleştirilmiş döngü(ler)in dışına çıkıyorsa (yani, ‘for ( int i = …)’ gibi değil, daha çok ‘int i; … for ( i = … ) o zaman **özel ilan edilmeleri **vardır.

Tipik C örneği

#include <stdio.h>
#include <math.h>
#include <omp.h>

#define N 1000000

int main() {
    double sum = 0;

    double tbegin = omp_get_wtime();
    #pragma omp parallel for reduction( +: sum )
    for ( int i = 0; i < N; i++ ) {
        sum += cos( i );
    }
    double wtime = omp_get_wtime() - tbegin;

    printf( "Computing %d cosines and summing them with %d threads took %fs\n",
            N, omp_get_max_threads(), wtime );

    return sum;
}

Bu örnekte, sadece 1 milyon kosinüs hesaplıyoruz ve değerlerini paralel olarak topluyoruz. Paralelleştirmenin performans üzerinde herhangi bir etkisi olup olmadığını görmek için yürütmeyi de zamanlarız. Son olarak, zamanı ölçtüğümüz için, derleyicinin yaptığımız işi optimize etmeyeceğinden emin olmalıyız, bu yüzden sonucu geri döndürerek kullanıyormuş gibi yaparız.

Fortran’da aynı örnek

program typical_loop
    use omp_lib
    implicit none
    integer, parameter :: N = 1000000, kd = kind( 1.d0 )
    real( kind = kd ) :: sum, tbegin, wtime
    integer :: i

    sum = 0

    tbegin = omp_get_wtime()
    !$omp parallel do reduction( +: sum )
    do i = 1, N
        sum = sum + cos( 1.d0 * i )
    end do
    !$omp end parallel do
    wtime = omp_get_wtime() - tbegin

    print "( 'Computing ', i7, ' cosines and summing them with ', i2, &
        & ' threads took ', f6.4,'s' )", N, omp_get_max_threads(), wtime

    if ( sum > N ) then
        print *, "we only pretend using sum"
    end if
end program typical_loop

Burada yine 1 milyon kosinüs hesaplayıp biriktiriyoruz. Döngüyü zamanlıyoruz ve istenmeyen derleyici optimizasyonunu önlemek için sonucu kullanıyormuş gibi yapıyoruz.

Örnekleri derleme ve çalıştırma

GCC sürüm 4.4 kullanan 8 çekirdekli bir Linux makinesinde C kodları aşağıdaki şekilde derlenebilir ve çalıştırılabilir:

$ gcc -std=c99 -O3 -fopenmp loop.c -o loopc -lm
$ OMP_NUM_THREADS=1 ./loopc
Computing 1000000 cosines and summing them with 1 threads took 0.095832s
$ OMP_NUM_THREADS=2 ./loopc
Computing 1000000 cosines and summing them with 2 threads took 0.047637s
$ OMP_NUM_THREADS=4 ./loopc
Computing 1000000 cosines and summing them with 4 threads took 0.024498s
$ OMP_NUM_THREADS=8 ./loopc
Computing 1000000 cosines and summing them with 8 threads took 0.011785s

Fortran versiyonu için şunları verir:

$ gfortran -O3 -fopenmp loop.f90 -o loopf
$ OMP_NUM_THREADS=1 ./loopf
Computing 1000000 cosines and summing them with  1 threads took 0.0915s
$ OMP_NUM_THREADS=2 ./loopf
Computing 1000000 cosines and summing them with  2 threads took 0.0472s
$ OMP_NUM_THREADS=4 ./loopf
Computing 1000000 cosines and summing them with  4 threads took 0.0236s
$ OMP_NUM_THREADS=8 ./loopf
Computing 1000000 cosines and summing them with  8 threads took 0.0118s

Yapı için OpenMP paralel kullanarak iki vektörün eklenmesi

void parallelAddition (unsigned N, const double *A, const double *B, double *C)
{
    unsigned i;

    #pragma omp parallel for shared (A,B,C,N) private(i) schedule(static)
    for (i = 0; i < N; ++i)
    {
        C[i] = A[i] + B[i];
    }
}

Bu örnek, bir iş parçacığı ekibi (örneğin, ‘OMP_NUM_THREADS’ ortam değişkeni tarafından belirtilen) oluşturarak ve her iş parçacığına bir iş yığını atayarak iki vektörü (‘C’ye ‘A’ ve ‘B’ ekler) “schedule(static)” ifadesi aracılığıyla statik olarak atanır).

‘private(i)’ opsiyonu ile ilgili açıklamalar bölümüne bakınız.