Düzensiz OpenMP paralelliği

Yaygın bir tuzak, paralel bir bölgenin tüm iş parçacıklarının görevleri başlatması (oluşturması) gerektiğine inanmaktır, ancak iş parçacığı sayısı çarpı işlenecek öğe sayısı kadar görev oluşturmak istemiyorsanız, bu genellikle böyle değildir. Bu nedenle, OpenMP görev kodlarında şuna benzer bir şey bulacaksınız:

#pragma omp parallel
#pragma omp single
...
   #pragma omp task
   { code for a given task; }
...

OpenMP görevlerini kullanarak bir c++ liste kabının paralel işlenmesi

#include <omp.h>
#include <unistd.h>
#include <iostream>
#include <list>

static void processElement (unsigned n)
{
    // Tell who am I. The #pragma omp critical ensures that
    // only one thread sends data to std::cout
    #pragma omp critical
    std::cout <<
      "Thread " << omp_get_thread_num() << " processing element " << n
      << std::endl;

    // Simulate some work
    usleep (n*1000);
}

int main (void)
{
    std::list<unsigned> lst;

    // Fill the list
    for (unsigned u = 0; u < 16; ++u)
            lst.push_back (1+u);

    // Now process each element of the list in parallel

    #pragma omp parallel  // Create a parallel region
    #pragma omp single    // Only one thread will instantiate tasks
    {
            for (auto element : lst)
            {
                    #pragma omp task firstprivate (element)
                    processElement (element);
            }

            // Wait for all tasks to be finished
            #pragma omp taskwait
    }

    return 0;
}

Bu örnek, bir STL listesinin (kodda “lst” olarak adlandırılır) OpenMP görev yapıları aracılığıyla ("#pragma omp görevi" yönergesini kullanarak) paralel olarak işlenmesini simüle eder. Örnek, “lst” içindeki her öğe için bir OpenMP görevi oluşturur/başlatır ve OpenMP dizileri, çalışmaya hazır oldukları anda görevleri yürütür.

$ OMP_NUM_THREADS=4 ./a.out
Thread 0 processing element 16
Thread 3 processing element 3
Thread 2 processing element 1
Thread 1 processing element 2
Thread 2 processing element 4
Thread 1 processing element 5
Thread 3 processing element 6
Thread 2 processing element 7
Thread 1 processing element 8
Thread 3 processing element 9
Thread 2 processing element 10
Thread 1 processing element 11
Thread 0 processing element 15
Thread 3 processing element 12
Thread 2 processing element 13
Thread 1 processing element 14

OpenMP görevlerini kullanarak pi için özyinelemeli hesaplama

Aşağıdaki kod, özyinelemeli bir yaklaşım kullanarak PI değerini hesaplar. Hangi özyineleme derinliğinde görev oluşturmayı durduracağını belirlemek için “MAX_PARALLEL_RECURSIVE_LEVEL” değerini değiştirin. Özyinelemeli uygulamalardan paralellik oluşturmaya yönelik bu yaklaşımla: ne kadar çok görev oluşturursanız, o kadar çok paralel görev oluşturulur, ancak aynı zamanda görev başına daha az iş yapılır. Bu nedenle, daha fazla görev oluşturmanın hangi düzeyde performans açısından fayda sağlamadığını anlamak için uygulama ile deneme yapmak uygundur.

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

double pi_r (double h, unsigned depth, unsigned maxdepth, unsigned long long begin, unsigned long long niters)
{
    if (depth < maxdepth)
    {
        double area1, area2;

        // Process first half
        #pragma omp task shared(area1)
        area1 = pi_r (h, depth+1, maxdepth, begin, niters/2-1);

        // Process second half
        #pragma omp task shared(area2)
        area2 = pi_r (h, depth+1, maxdepth, begin+niters/2, niters/2);

        #pragma omp taskwait

        return area1+area2;
    }
    else
    {

        unsigned long long i;
        double area = 0.0;

        for (i = begin; i <= begin+niters; i++)
        {
            double x = h * (i - 0.5);
            area += (4.0 / (1.0 + x*x));
        }

        return area;
    }
}

double pi (unsigned long long niters)
{
    double res;
    double h = 1.0 / (double) niters;

    #pragma omp parallel shared(res)
    {
#define MAX_PARALLEL_RECURSIVE_LEVEL 4

        #pragma omp single
        res = pi_r (h, 0, MAX_PARALLEL_RECURSIVE_LEVEL, 1, niters);
    }
    return res * h;
}


int main (int argc, char *argv[])
{
#define NITERS (100*1000*1000ULL)

    printf ("PI (w/%d iters) is %lf\n", NITERS, pi(NITERS));

    return 0;
}