C++ OpenMP Parallel For Loop – Alternativen zu std::vector [closed]

Lesezeit: 5 Minuten

C OpenMP Parallel For Loop Alternativen zu stdvector closed
Armin Meisterhirn

Basierend auf diesem Thread, OpenMP und STL-Vektor, welche Datenstrukturen gute Alternativen für eine sind geteilt std::vector in einer parallelen For-Schleife? Der Hauptaspekt ist die Geschwindigkeit, und der Vektor muss möglicherweise während der Schleife in der Größe geändert werden.

  • Zeigen Sie uns etwas Code, beschreiben Sie Ihre spezifische Situation … was wird im Vektor gespeichert? Was wird Ihre Schleife damit machen? Es ist sehr wahrscheinlich, dass es absolut sicher zu verwenden ist std::vector ohnehin.

    – Liho

    7. September 2013 um 3:15

  • Wie im verlinkten Thread erwähnt, müssen Sie sich nur darum kümmern, std::vector nicht zu verwenden, wenn die Größe Ihres Vektors in Ihrer Schleife geändert und möglicherweise neu zugewiesen wird. Wenn Sie nur Objekte ändern, können Sie es problemlos verwenden. Können Sie Ihre Anforderungen näher erläutern und warum Vector nicht Ihren Anforderungen entspricht?

    – SinisterMJ

    7. September 2013 um 3:29 Uhr

  • Ich denke, es ist nur ein Problem, wenn die std::vector wird geteilt. Wenn es privat ist, dann denke ich nicht, dass es ein Problem gibt, es zu benutzen push_back oder resize.

    – Z-Boson

    7. September 2013 um 7:23 Uhr

C OpenMP Parallel For Loop Alternativen zu stdvector closed
Z-Boson

Ich denke, Sie können verwenden std::vector mit OpenMP die meiste Zeit und haben immer noch eine gute Leistung. Der folgende Code füllt beispielsweise aus std::vectors parallel und kombiniert sie am Ende. Solange Ihre Hauptschleifen-/Füllfunktion der Engpass ist, sollte dies im Allgemeinen gut funktionieren und Thread-sicher sein.

std::vector<int> vec;
#pragma omp parallel
{
    std::vector<int> vec_private;
    #pragma omp for nowait //fill vec_private in parallel
    for(int i=0; i<100; i++) {
        vec_private.push_back(i);
    }
    #pragma omp critical
    vec.insert(vec.end(), vec_private.begin(), vec_private.end());
}

Bearbeiten:

OpenMP 4.0 ermöglicht benutzerdefinierte Reduzierungen mit #pragma omp declare reduction. Der obige Code kann mit to vereinfacht werden

#pragma omp declare reduction (merge : std::vector<int> : omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))

std::vector<int> vec;
#pragma omp parallel for reduction(merge: vec)
for(int i=0; i<100; i++) vec.push_back(i);

Bearbeiten: Was ich bisher gezeigt habe, füllt den Vektor nicht in Ordnung. Wenn die Reihenfolge wichtig ist, kann dies so erfolgen

std::vector<int> vec;
#pragma omp parallel
{
    std::vector<int> vec_private;
    #pragma omp for nowait schedule(static)
    for(int i=0; i<N; i++) { 
        vec_private.push_back(i);
    }
    #pragma omp for schedule(static) ordered
    for(int i=0; i<omp_get_num_threads(); i++) {
        #pragma omp ordered
        vec.insert(vec.end(), vec_private.begin(), vec_private.end());
    }
}

Dadurch wird vermieden, dass für jeden Thread ein std::vector gespeichert und dann außerhalb der parallelen Region seriell zusammengeführt wird. Ich habe hier von diesem “Trick” erfahren. Ich bin mir nicht sicher, wie ich das für benutzerdefinierte Reduzierungen machen soll (oder ob es überhaupt möglich ist).. Dies ist mit benutzerdefinierten Reduzierungen nicht möglich.

Ich habe gerade festgestellt, dass der kritische Abschnitt nicht notwendig ist, was ich aus dieser Frage herausgefunden habe. Diese Methode erhält auch die richtige Reihenfolge

std::vector<int> vec;
size_t *prefix;
#pragma omp parallel
{
    int ithread  = omp_get_thread_num();
    int nthreads = omp_get_num_threads();
    #pragma omp single
    {
        prefix = new size_t[nthreads+1];
        prefix[0] = 0;
    }
    std::vector<int> vec_private;
    #pragma omp for schedule(static) nowait
    for(int i=0; i<100; i++) {
        vec_private.push_back(i);
    }
    prefix[ithread+1] = vec_private.size();
    #pragma omp barrier
    #pragma omp single 
    {
        for(int i=1; i<(nthreads+1); i++) prefix[i] += prefix[i-1];
        vec.resize(vec.size() + prefix[nthreads]);
    }
    std::copy(vec_private.begin(), vec_private.end(), vec.begin() + prefix[ithread]);
}
delete[] prefix;

  • Zu der Frage im allerletzten Satz: „Wie oft die Kombinierer ausgeführt wird, und die Reihenfolge dieser Ausführungen, für alle reduction Klausel ist nicht spezifiziert”, daher nicht möglich.

    – Christo Iljew

    26. März 2015 um 12:20 Uhr

  • Danke, das hat mir wirklich geholfen!

    Benutzer9128740

    19. September 2018 um 21:28 Uhr

  • Nur eine Frage: Wenn wir eine verschachtelte for-Schleife hätten, würde der obige Code immer noch gelten, indem er schreibt: std::vector<int> vec; #pragma omp parallel { #pragma omp for collapse(2) nowait schedule(static) for(int i=0; i<N; i++) { for(int j=0; j< M; j++){ } } #pragma omp for collapse(2) schedule(static) ordered for(int i=0; i<omp_get_num_threads(); i++) { #pragma omp ordered do some stuff } }

    – Joachim

    9. Februar 2020 um 15:25 Uhr


  • Sorry für die hässliche Einrückung

    – Joachim

    9. Februar 2020 um 15:27 Uhr

  • @ Joachim, ich weiß es nicht. Normalerweise verwende ich keine verschachtelte Parallelität. Ich habe auch keine Zeit, mich darum zu kümmern. Vielleicht eine Frage stellen. Hier gibt es viele Experten, die Ihnen helfen können.

    – Z-Boson

    10. Februar 2020 um 8:36 Uhr

1646645415 139 C OpenMP Parallel For Loop Alternativen zu stdvector closed
Vivian Miranda

Die von Ihnen verlinkte Frage bezog sich auf die Tatsache, dass “dieser STL-Vektorcontainer in Situationen, in denen mehrere Threads in einen einzelnen Container schreiben, nicht Thread-sicher ist”. Dies gilt nur, wie dort korrekt angegeben, wenn Sie Methoden aufrufen, die eine Neuzuordnung des zugrunde liegenden Arrays verursachen können std::vector hält. push_back(), pop_back() und insert() sind Beispiele für diese gefährlichen Methoden.

Wenn Sie eine Thread-sichere Neuzuweisung benötigen, dann die Bibliothek Intel-Thread-Baustein bietet Dir gleichzeitige Vektorcontainer . Sie sollten tbb::concurrent_vector nicht in Single-Thread-Programmen verwenden, da die Zeit, die für den Zugriff auf zufällige Elemente benötigt wird, länger ist als die Zeit, die std::vector benötigt, um dasselbe zu tun (was O (1) ist). Jedoch gleichzeitige Vektoraufrufe push_back(), pop_back(), insert() auf Thread-sichere Weise, auch wenn eine Neuzuweisung erfolgt.

EDIT 1: Die Folien 46 und 47 von die folgende Intel-Präsentation Geben Sie ein anschauliches Beispiel für die gleichzeitige Neuzuweisung mit tbb::concurrent_vector

EDIT 2: Übrigens, wenn Sie anfangen, Intel Tread Building Block zu verwenden (es ist Open Source, funktioniert mit den meisten Compilern und ist viel besser in C++/C++11-Funktionen integriert als Openmp), dann brauchen Sie es nicht um openmp zu verwenden, um ein parallel_for zu erstellen, Hier ist ein nettes Beispiel für parallel_for mit tbb.

964810cookie-checkC++ OpenMP Parallel For Loop – Alternativen zu std::vector [closed]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy