C++ OpenMP Parallel For Loop-Alternatives to std::vector [closed]

W oparciu o ten wątek, OpenMP I stl vector, które struktury danych są dobrymi alternatywami dla współdzielonego std::vector W pętli równoległej for? Głównym aspektem jest szybkość, a wektor może wymagać zmiany rozmiaru podczas pętli.

Author: Community, 2013-09-07

2 answers

Pytanie, które podlinkowałeś, mówiło o tym, że "kontener wektorowy STL nie jest bezpieczny dla wątków w sytuacji, gdy wiele wątków pisze do jednego kontenera". Jest to prawdą tylko wtedy, gdy wywołujesz metody, które mogą spowodować realokację tablicy bazowej, która przechowuje std::vector. push_back(), pop_back() i insert() są przykładami tych niebezpiecznych metod.

Jeśli potrzebujesz bezpiecznej realokacji wątku, biblioteka intel thread building block oferuje Ci współbieżne kontenery wektorowe . Nie powinieneś używać TBB:: concurrent_vector w programach z pojedynczym wątkiem, ponieważ czas potrzebny na dostęp do losowych elementów jest wyższy niż czas potrzebny na wykonanie tego samego (który wynosi O(1)). Jednak wywołania wektorowe współbieżnepush_back(), pop_back(), insert() w bezpieczny sposób, nawet w przypadku realokacji.

EDIT 1: slajdy 46 i 47 Z poniższa prezentacja Intela daje ilustracyjny przykład równoczesnej realokacji przy użyciu tbb:: concurrent_vector

EDIT 2: przy okazji, jeśli zaczniesz używać Intel Tread Building Block (jest open source, działa z większością kompilatorów i jest znacznie lepiej zintegrowany z funkcjami C++/C++11 niż openmp), to nie musisz używać openmp do tworzenia parallel_for, tutaj {[11] } jest miłym przykładem parallel_for używającego tbb.

 12
Author: Vivian Miranda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:30

Myślę, że możesz używać std::vector z OpenMP przez większość czasu i nadal mieć dobrą wydajność. Poniższy kod na przykład wypełnia std::vectors równolegle, a następnie łączy je na końcu. Tak długo, jak główna funkcja pętli/wypełnienia jest wąskim gardłem, powinno to działać dobrze ogólnie i być bezpieczne dla wątku.

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());
}

Edit:

OpenMP 4.0 pozwala definiować redukcje za pomocą #pragma omp declare reduction. Powyższy kod można uprościć za pomocą

#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);

Edytuj: To co dotychczas pokazałem nie wypełnia wektor w porządku. Jeśli kolejność ma znaczenie, można to zrobić tak:

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());
    }
}

Pozwala to uniknąć zapisywania std:: vector dla każdego wątku, a następnie łączenia ich szeregowo poza obszarem równoległym. O tej "sztuczce" dowiedziałem się tutaj . nie jestem pewien, jak to zrobić (lub czy jest to w ogóle możliwe) dla redukcji zdefiniowanych przez użytkownika.. Nie można tego zrobić z redukcjami zdefiniowanymi przez użytkownika.

Właśnie zdałem sobie sprawę, że sekcja krytyczna nie jest konieczna, co zorientowałem się z to pytanie parallel-cumulative-prefix-sums-in-openmp-communicating-values-between-thread. Ta metoda również pobiera poprawną kolejność

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;
 32
Author: Z boson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:30