Se si decide di utilizzare Openmp 3.0
, è possibile utilizzare la funzione task
:
#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
}
Questo eseguirà il ciclo in un thread, ma delegare l'elaborazione di elementi per gli altri.
Senza OpenMP 3.0
il modo più semplice sarebbe quella di scrittura tutti i puntatori a elementi della lista (o iteratori in un vettore e l'iterazione di quello. In questo modo si potrebbe non dover copiare nulla di nuovo e di evitare il sovraccarico di copiare degli elementi stessi, quindi non dovrebbe avere a molto in alto:
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();
}
Se si vuole evitare di copiare anche i puntatori, è sempre possibile creare un ciclo for parallelized a mano è possibile avere i fili accedere agli elementi interlacciati di. l'elenco (come proposto da KennyTM) o dividere l'intervallo in parti contiue approssimativamente uguali prima di iterare e iterare su quelle. Più tardi sembra preferibile in quanto i thread evitano l'accesso a stnodi attualmente elaborati da altri thread (anche se solo il prossimo puntatore), che potrebbe portare a false condivisioni. Ciò sembra grosso modo così:
#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();
}
La barriera non è strettamente necessario, se process
muta l'elemento elaborato (che significa che non è un metodo const), ci potrebbe essere una sorta di falsa condivisione senza di essa, se I thread si ripetono su una sequenza che è già stata modificata. In questo modo itererà 3 volte e n volte sulla sequenza (dove n è il numero di thread), quindi il ridimensionamento potrebbe essere inferiore all'ottimale per un numero elevato di thread.
Per ridurre il sovraccarico, è possibile impostare la generazione degli intervalli al di fuori dello #pragma omp parallel
, tuttavia è necessario sapere quanti thread formeranno la sezione parallela. Quindi probabilmente dovresti impostare manualmente il num_threads
, o usare omp_get_max_threads()
e gestire il caso in cui il numero di thread creati sia inferiore a omp_get_max_threads()
(che è solo un limite superiore). L'ultimo modo potrebbe essere gestito da possibilmente assegnando ad ogni pezzi filo Severa in quel caso (usando #pragma omp for
dovrebbe farlo):
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();
}
Questo richiederà solo tre iterazioni oltre list
(due, se è possibile ottenere la dimensione del elenco senza iterare).Penso che sia il meglio che puoi fare per gli iteratori di accesso non casuale senza usare tasks
o iterando su qualche datastructure fuori posto (come un vettore di puntatore).
Grazie per la risposta dettagliata. – mshang
Voglio iterare su tutta la 'mappa'. Come posso eseguire iterazioni usando OpenMp per l'intera mappa? – user