Jeśli użytkownik zdecyduje się użyć Openmp 3.0
, można użyć task
cechę:
#pragma omp parallel
#pragma omp single
{
for(auto it = l.begin(); it != l.end(); ++it)
#pragma omp task firstprivate(it)
it->process();
#pragma omp taskwait
}
ten wykona pętlę w jednym wątku, ale delegowania przetwarzania elementów do innych.
Bez OpenMP 3.0
najprostszym sposobem byłoby zapisanie wszystkich wskaźników do elementów na liście (lub iteratorów w wektorze i iteracja nad tym, w ten sposób nie trzeba kopiować niczego z powrotem i uniknąć narzutu na kopiowanie elementów sami, więc nie powinien mieć zbyt dużo napowietrznych:
std::vector<my_element*> elements; //my_element is whatever is in list
for(auto it = list.begin(); it != list.end(); ++it)
elements.push_back(&(*it));
#pragma omp parallel shared(chunks)
{
#pragma omp for
for(size_t i = 0; i < elements.size(); ++i) // or use iterators in newer OpenMP
elements[i]->process();
}
Jeśli chcesz uniknąć kopiowania nawet wskazówek, zawsze można stworzyć parallelized pętli ręcznie można też mieć dostęp przeplatanych nici elementy. listę (zgodnie z propozycją KennyTM) lub podziel zakres w mniej więcej równych, contious częściach przed iterowaniem i iterowaniem nad nimi. Później wydaje się, że lepiej, ponieważ nici unikają dostępu do li stnodes obecnie przetwarzane przez inne wątki (nawet jeśli tylko następny wskaźnik), co może prowadzić do fałszywego udostępniania. To będzie wyglądać mniej więcej tak:
#pragma omp parallel
{
int thread_count = omp_get_num_threads();
int thread_num = omp_get_thread_num();
size_t chunk_size= list.size()/thread_count;
auto begin = list.begin();
std::advance(begin, thread_num * chunk_size);
auto end = begin;
if(thread_num = thread_count - 1) // last thread iterates the remaining sequence
end = list.end();
else
std::advance(end, chunk_size);
#pragma omp barrier
for(auto it = begin; it != end; ++it)
it->process();
}
Bariera nie jest bezwzględnie konieczne, jednak jeśli process
mutuje przetworzonego elementu (co oznacza, że nie jest to metoda const), nie może być jakiś rodzaj fałszywego dzielenia bez niej, jeśli wątki iterują po sekwencji, która jest już mutowana. W ten sposób będzie iterować 3 * n razy w sekwencji (gdzie n jest liczbą wątków), więc skalowanie może być mniejsze niż optymalne dla dużej liczby wątków.
Aby zmniejszyć obciążenie, można ustawić generowanie zakresów poza wartością #pragma omp parallel
, jednak trzeba będzie wiedzieć, ile wątków utworzy sekcję równoległą. Prawdopodobnie będziesz musiał ręcznie ustawić num_threads
lub użyć omp_get_max_threads()
i obsłużyć wielkość liter, że liczba utworzonych wątków jest mniejsza niż omp_get_max_threads()
(która jest tylko górną granicą). Ostatni sposób może być obsługiwany przez możliwie przypisując każdy kawałki nici Severa w tym przypadku (używając #pragma omp for
powinien zrobić):
int max_threads = omp_get_max_threads();
std::vector<std::pair<std::list<...>::iterator, std::list<...>::iterator> > chunks;
chunks.reserve(max_threads);
size_t chunk_size= list.size()/max_threads;
auto cur_iter = list.begin();
for(int i = 0; i < max_threads - 1; ++i)
{
auto last_iter = cur_iter;
std::advance(cur_iter, chunk_size);
chunks.push_back(std::make_pair(last_iter, cur_iter);
}
chunks.push_back(cur_iter, list.end();
#pragma omp parallel shared(chunks)
{
#pragma omp for
for(int i = 0; i < max_threads; ++i)
for(auto it = chunks[i].first; it != chunks[i].second; ++it)
it->process();
}
To zajmie tylko trzy iteracje nad list
(dwóch, jeśli można uzyskać rozmiaru lista bez iteracji).Myślę, że chodzi o to, co najlepsze, co można zrobić dla iteratorów bez dostępu losowego, bez korzystania z tasks
lub iterowania nad pewną nieaktualną strukturą danych (jak wektor wskaźnika).
Dzięki za szczegółową odpowiedź. – mshang
Chcę iterować nad całą 'mapą'. Jak mogę iterować przy użyciu OpenMp całej mapy? – user