2016-03-27 21 views
9

Ich frage mich, ob es eine leichte, direkte Möglichkeit gibt, Schleifen in C++ zu berechnen, z. Wie würdest du so etwas umsetzen? Von Scala kenne ich die Karten-, Filter- und Foreach-Funktionen vielleicht könnte man diese auch parallel durchführen? Gibt es einen einfachen Weg, dies in C++ zu erreichen? Meine primäre Plattform ist Linux, aber es wäre schön, wenn es plattformübergreifend funktioniert.Parallele Schleifen in C++

+0

mit Threads ist eine gute Option. –

+0

Ist es nicht wirklich teuer, Threads zu initialisieren? – Exagon

+1

Im Vergleich zum Aufruf von fork(). Threads sind nicht teuer, da sie die Ressourcen aus dem Hauptthread teilen, außer sie haben ihren eigenen PC, Register und Stack. –

Antwort

8

Was ist Ihre Plattform? Sie können OpenMP betrachten, obwohl es kein Teil von C++ ist. Aber es wird von Compilern weitgehend unterstützt.

Wie für bereichsbasierte for-Schleifen, siehe z. B. Using OpenMP with C++11 range-based for loops?.

Ich habe auch einige Dokumente bei http://www.open-std.org gesehen, die einige Bemühungen zeigen, parallele Konstrukte/Algorithmen in zukünftiges C++ einzubauen, aber nicht wissen, was ihr aktueller Status ist.

UPDATE

nur einige beispielhafte Code hinzufügen:

template <typename RAIter> 
void loop_in_parallel(RAIter first, RAIter last) { 
    const size_t n = std::distance(first, last); 

    #pragma omp parallel for 
    for (size_t i = 0; i < n; i++) { 
     auto& elem = *(first + i); 
     // do whatever you want with elem 
    } 
} 

Die Anzahl der Fäden zur Laufzeit über das OMP_NUM_THREADS Umgebungsvariable gesetzt werden kann.

+0

Lets sagen, ich habe eine nicht so teure Operation in der Schleife ist es möglich, die Schleife in zwei Hälften zu teilen? Auf dem Faden macht die eine Hälfte den Rest? Gleiches mit 3 und so weiter Themen? – Exagon

+0

Was wiederholst du? Können Sie Indizes zum Schleifen verwenden? –

+0

@Exagon Es hängt von Ihnen ab, auf welche Weise Sie Arbeit von Threads nehmen. Sie können Bedingungen in Schleifen festlegen, mit denen Sie die Arbeit teilen können. –

3

Dies kann mit threads speziell pthreads Bibliotheksfunktion durchgeführt werden, die verwendet werden kann, um Operationen gleichzeitig durchzuführen.

Sie können mehr über sie hier lesen: http://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm

std :: Thread kann auch verwendet werden: http://www.cplusplus.com/reference/thread/thread/

Unten ist ein Code, in dem ich die Thread-ID jedes Threads verwenden das Array aufgeteilt in zwei Hälften:

#include <iostream> 
#include <cstdlib> 
#include <pthread.h> 

using namespace std; 

#define NUM_THREADS 2 

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 

void *splitLoop(void *threadid) 
{ 
    long tid; 
    tid = (long)threadid; 
    //cout << "Hello World! Thread ID, " << tid << endl; 
    int start = (tid * 5); 
    int end = start + 5; 
    for(int i = start;i < end;i++){ 
     cout << arr[i] << " "; 
    } 
    cout << endl; 
    pthread_exit(NULL); 
} 

int main() 
{ 
    pthread_t threads[NUM_THREADS]; 
    int rc; 
    int i; 
    for(i=0; i < NUM_THREADS; i++){ 
     cout << "main() : creating thread, " << i << endl; 
     rc = pthread_create(&threads[i], NULL, 
          splitLoop, (void *)i); 
     if (rc){ 
     cout << "Error:unable to create thread," << rc << endl; 
     exit(-1); 
     } 
    } 
    pthread_exit(NULL); 
} 

Denken Sie auch daran beim Kompilieren Sie die -lpthread Flagge verwenden.

Link zur Lösung auf Ideone: http://ideone.com/KcsW4P

+1

Ja, das erreicht werden kann, können Sie den 'Start-Index' und den 'End-Index' in der Funktion, die Sie verwenden, angeben und für jeden zu verwendenden Thread entsprechend ändern. – uSeemSurprised

+1

Die Funktion 'pthread_create' nimmt ein Argument an, das den Namen der Funktion enthält, die die Threads verwenden sollen. Sie können diese Funktionsargumente ändern, um das gewünschte Ergebnis zu erzielen. – uSeemSurprised

+3

warum würde er pthreads verwenden wollen, wenn er 'std :: thread' hat? –

8

std::async kann hier eine gute Passform sein, wenn Sie gerne die C++ Laufzeit die Parallelität steuern lassen.

Beispiel aus der cppreference.com:

#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <numeric> 
#include <future> 

template <typename RAIter> 
int parallel_sum(RAIter beg, RAIter end) 
{ 
    auto len = end - beg; 
    if(len < 1000) 
     return std::accumulate(beg, end, 0); 

    RAIter mid = beg + len/2; 
    auto handle = std::async(std::launch::async, 
           parallel_sum<RAIter>, mid, end); 
    int sum = parallel_sum(beg, mid); 
    return sum + handle.get(); 
} 

int main() 
{ 
    std::vector<int> v(10000, 1); 
    std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n'; 
} 
+0

wow danke ich denke, ich werde das verwenden – Exagon

+0

gibt es irgendwelche Dokumentation darüber, wie C++ die parallelen Aufgaben und asyncs behandelt? – Exagon

+1

Der erste Parameter für 'std :: async()' gibt an, welche Freiheit Sie dem Framework geben (in erster Linie, ob Sie die Verwendung des Vordergrund-Threads zulassen). Was es für den Hintergrund tut - es ist Compiler-spezifisch, aber höchstwahrscheinlich wird es bei den meisten Compilern einen Singleton-Thread-Pool mit N = Anzahl von CPU-Kernen auf der Box geben. Die beste Gebrauchsdokumentation, die ich bisher gefunden habe, ist das Nebenläufigkeitskapitel aus dem letzten Mayer-Buch. – bobah

5

mit den Parallelalgorithmen in C++ 17 wir verwenden:

std::vector<std::string> foo; 
std::for_each(
    std::execution::par_unseq, 
    foo.begin(), 
    foo.end(), 
    [](auto&& item) 
    { 
     //do stuff with item 
    }); 

zu berechnen Schleifen parallel. Der erste Parameter gibt die execution policy

+0

Unterstützen das tatsächlich Compiler? – nitronoid

+0

Intel C++ Compiler, wenn Sie ein Student sind, können Sie es kostenlos bekommen. – Kaznov

1

Mit C++ 11 können Sie eine for-Schleife mit nur ein paar Zeilen von Codes parallelisieren.Diese spaltet eine for-Schleife in kleinere Stücke und weisen jede Unterschleife zu einem Thread:

/// Basically replacing: 
void sequential_for(){ 
    for(int i = 0; i < nb_elements; ++i) 
     computation(i); 
} 

/// By: 
void threaded_for(){ 
    parallel_for(nb_elements, [&](int start, int end){ 
     for(int i = start; i < end; ++i) 
      computation(i); 
    }); 
} 

Oder withing eine Klasse:

struct My_obj { 

    /// Replacing: 
    void sequential_for(){ 
     for(int i = 0; i < nb_elements; ++i) 
      computation(i); 
    } 

    /// By: 
    void threaded_for(){ 
     parallel_for(nb_elements, [this](int s, int e){ this->process_chunk(s, e); }); 
    } 

    void process_chunk(int start, int end) 
    { 
     for(int i = start; i < end; ++i) 
      computation(i); 
    } 
}; 

Um dies zu tun, müssen Sie nur den Code setzen unten eine Header-Datei und verwenden Sie es nach Belieben:

#include <algorithm> 
#include <thread> 
#include <functional> 
#include <vector> 

/// @param[in] nb_elements : size of your for loop 
/// @param[in] functor(start, end) : 
/// your function processing a sub chunk of the for loop. 
/// "start" is the first index to process (included) until the index "end" 
/// (excluded) 
/// @code 
///  for(int i = start; i < end; ++i) 
///   computation(i); 
/// @endcode 
/// @param use_threads : enable/disable threads. 
/// 
/// 
static 
void parallel_for(unsigned nb_elements, 
        std::function<void (int start, int end)> functor, 
        bool use_threads = true) 
{ 
    // ------- 
    unsigned nb_threads_hint = std::thread::hardware_concurrency(); 
    unsigned nb_threads = nb_threads_hint == 0 ? 8 : (nb_threads_hint); 

    unsigned batch_size = nb_elements/nb_threads; 
    unsigned batch_remainder = nb_elements % nb_threads; 

    std::vector<std::thread> my_threads(nb_threads); 

    if(use_threads) 
    { 
     // Multithread execution 
     for(unsigned i = 0; i < nb_threads; ++i) 
     { 
      int start = i * batch_size; 
      my_threads[i] = std::thread(functor, start, start+batch_size); 
     } 
    } 
    else 
    { 
     // Single thread execution (for easy debugging) 
     for(unsigned i = 0; i < nb_threads; ++i){ 
      int start = i * batch_size; 
      functor(start, start+batch_size); 
     } 
    } 

    // Deform the elements left 
    int start = nb_threads * batch_size; 
    functor(start, start+batch_remainder); 

    // Wait for the other thread to finish their task 
    if(use_threads) 
     std::for_each(my_threads.begin(), my_threads.end(), std::mem_fn(&std::thread::join)); 
}