Szukaj…


Wprowadzenie

Ten temat obejmuje podstawy współbieżności w C ++ przy użyciu OpenMP. OpenMP jest udokumentowany bardziej szczegółowo w znaczniku OpenMP .

Równoległość lub współbieżność oznacza wykonanie kodu w tym samym czasie.

Uwagi

OpenMP nie wymaga żadnych specjalnych nagłówków ani bibliotek, ponieważ jest wbudowaną funkcją kompilatora. Jeśli jednak korzystasz z funkcji API OpenMP, takich jak omp_get_thread_num() , musisz dołączyć omp.h i jego bibliotekę.

Instrukcje pragma OpenMP są ignorowane, gdy opcja OpenMP nie jest włączona podczas kompilacji. Możesz skorzystać z opcji kompilatora w instrukcji kompilatora.

  • GCC używa -fopenmp
  • Clang używa -fopenmp
  • MSVC używa /openmp

OpenMP: sekcje równoległe

Ten przykład ilustruje podstawy wykonywania sekcji kodu równolegle.

Ponieważ OpenMP jest wbudowaną funkcją kompilatora, działa na wszystkich obsługiwanych kompilatorach bez dołączania bibliotek. Możesz dołączyć omp.h jeśli chcesz skorzystać z dowolnej funkcji API openMP.

Przykładowy kod

std::cout << "begin ";
//    This pragma statement hints the compiler that the
//    contents within the { } are to be executed in as
//    parallel sections using openMP, the compiler will
//    generate this chunk of code for parallel execution
#pragma omp parallel sections
{
    //    This pragma statement hints the compiler that
    //    this is a section that can be executed in parallel
    //    with other section, a single section will be executed
    //    by a single thread.
    //    Note that it is "section" as opposed to "sections" above
    #pragma omp section
    {
        std::cout << "hello " << std::endl;
        /** Do something **/
    }
    #pragma omp section
    {
        std::cout << "world " << std::endl;
        /** Do something **/
    }
}
//    This line will not be executed until all the
//    sections defined above terminates
std::cout << "end" << std::endl;

Wyjścia

Ten przykład daje 2 możliwe wyniki i jest zależny od systemu operacyjnego i sprzętu. Dane wyjściowe ilustrują również problem warunków wyścigu , który wystąpiłby w wyniku takiej implementacji.

WYJŚCIE A WYJŚCIE B
zacznij cześć koniec świata zacznij świat witaj koniec

OpenMP: sekcje równoległe

Ten przykład pokazuje, jak równolegle wykonywać części kodu

std::cout << "begin ";
//    Start of parallel sections
#pragma omp parallel sections
{
    //    Execute these sections in parallel
    #pragma omp section
    {
        ... do something ...
        std::cout << "hello ";
    }
    #pragma omp section
    {
        ... do something ...
        std::cout << "world ";
    }
    #pragma omp section
    {
        ... do something ...
        std::cout << "forever ";
    }
}
//    end of parallel sections
std::cout << "end";

Wynik

  • zacznij cześć świat na zawsze koniec
  • zacznij świat witaj na zawsze koniec
  • zacznij cześć na zawsze koniec świata
  • zacznij na zawsze cześć koniec świata

Ponieważ zlecenie wykonania nie jest gwarantowane, możesz obserwować dowolne z powyższych wyników.

OpenMP: Parallel For Loop

Ten przykład pokazuje, jak podzielić pętlę na równe części i wykonać je równolegle.

//    Splits element vector into element.size() / Thread Qty
//    and allocate that range for each thread.
#pragma omp parallel for
for    (size_t i = 0; i < element.size(); ++i)
    element[i] = ...

//    Example Allocation (100 element per thread)
//    Thread 1 : 0 ~ 99
//    Thread 2 : 100 ~ 199
//    Thread 2 : 200 ~ 299
//    ...

//    Continue process
//    Only when all threads completed their allocated
//    loop job
...

* Zachowaj szczególną ostrożność, aby nie modyfikować wielkości wektora używanego równolegle dla pętli, ponieważ indeksy przydzielonych zakresów nie aktualizują się automatycznie .

OpenMP: Parallel Gathering / Reduction

Ten przykład ilustruje koncepcję przeprowadzania redukcji lub gromadzenia przy użyciu std::vector i OpenMP.

Załóżmy, że mamy scenariusz, w którym chcemy, aby wiele wątków pomogło nam wygenerować wiele rzeczy, int jest tutaj stosowany dla uproszczenia i może być zastąpiony innymi typami danych.

Jest to szczególnie przydatne, gdy trzeba scalić wyniki z urządzeń podrzędnych, aby uniknąć błędów segmentacji lub naruszeń dostępu do pamięci i nie chce się używać bibliotek ani niestandardowych bibliotek kontenerów synchronizacji.

//    The Master vector
//    We want a vector of results gathered from slave threads
std::vector<int> Master;    

//    Hint the compiler to parallelize this { } of code
//    with all available threads (usually the same as logical processor qty)
#pragma omp parallel
{
    //    In this area, you can write any code you want for each
    //    slave thread, in this case a vector to hold each of their results
    //    We don't have to worry about how many threads were spawn or if we need
    //    to repeat this declaration or not.
    std::vector<int> Slave;

    //    Tell the compiler to use all threads allocated for this parallel region
    //    to perform this loop in parts. Actual load appx = 1000000 / Thread Qty
    //    The nowait keyword tells the compiler that the slave threads don't
    //    have to wait for all other slaves to finish this for loop job
    #pragma omp for nowait
    for (size_t i = 0; i < 1000000; ++i
    {
        /* Do something */
        ....
        Slave.push_back(...);
    }

    //    Slaves that finished their part of the job
    //    will perform this thread by thread one at a time
    //    critical section ensures that only 0 or 1 thread performs
    //    the { } at any time
    #pragma omp critical
    {
        //    Merge slave into master
        //    use move iterators instead, avoid copy unless
        //    you want to use it for something else after this section
        Master.insert(Master.end(), 
                      std::make_move_iterator(Slave.begin()), 
                      std::make_move_iterator(Slave.end()));
    }
}

//    Have fun with Master vector
...


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow