我想使用OpenMP以并行的方式遍历std::list中的所有元素。循环应该能够更改列表的元素。有没有一个简单的解决方案?当迭代器是随机访问迭代器时,OpenMP 3.0似乎支持并行for循环,但在其他情况下不支持。在任何情况下,我都更喜欢使用OpenMP 2.0,因为我不能完全控制我可以使用哪些编译器。
如果我的容器是一个向量,我可能会使用:
#pragma omp parallel for
for (auto it = v.begin(); it != v.end(); ++it) {
it->process();
}我知道我可以将列表复制到一个向量中,执行循环,然后将所有内容复制回来。但是,如果可能的话,我希望避免这种复杂性和开销。
发布于 2012-01-03 23:48:22
如果您决定使用Openmp 3.0,则可以使用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
}这将在一个线程中执行循环,但将元素的处理委托给其他线程。
如果没有OpenMP 3.0,最简单的方法是将所有指针都写入列表中的元素(或向量中的迭代器,然后遍历该向量)。这样你就不需要复制任何东西了,避免了复制元素本身的开销,所以它不应该有太多的开销:
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();
}如果你想避免复制指针,你可以手动创建一个并行化的for循环。您可以让线程访问列表的交错元素(如KennyTM所建议的),或者在迭代和遍历这些元素之前将范围拆分为大致相等的连续部分。后者似乎更可取,因为线程避免访问当前由其他线程处理的listnode(即使只访问下一个指针),这可能导致错误共享。这大概看起来像这样:
#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();
}屏障并不是严格需要的,但是如果process改变了被处理的元素(这意味着它不是一个常量方法),如果线程在一个已经被变异的序列上迭代,那么在没有它的情况下可能会有某种虚假的共享。这种方式将在序列中迭代3*n次(其中n是线程的数量),因此对于大量线程来说,缩放可能不是最优的。
为了减少开销,您可以将范围的生成放在#pragma omp parallel之外,但是您需要知道有多少线程将形成并行部分。因此,您可能需要手动设置num_threads,或者使用omp_get_max_threads()来处理创建的线程数少于omp_get_max_threads() (这只是一个上限)的情况。最后一种方法可以通过在这种情况下为每个线程分配几个块来处理(使用#pragma omp for应该可以做到这一点):
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();
}这在list上只需要三次迭代(两次,如果您可以在不迭代的情况下获得列表的大小)。我认为这是你对非随机访问迭代器所能做的最好的事情,而不需要使用tasks或者迭代一些不合适的数据结构(比如指针的向量)。
发布于 2012-01-01 12:44:59
我怀疑这是不可能的,因为你不能只跳到列表的中间而不遍历列表。列表不存储在连续的内存中,std::list迭代器也不是随机访问。它们只是双向的。
发布于 2014-02-25 14:40:29
http://openmp.org/forum/viewtopic.php?f=3&t=51
#pragma omp parallel
{
for(it= list1.begin(); it!= list1.end(); it++)
{
#pragma omp single nowait
{
it->compute();
}
} // end for
} // end ompparallel这可以理解为展开为:
{
it = listl.begin
#pragma omp single nowait
{
it->compute();
}
it++;
#pragma omp single nowait
{
it->compute();
}
it++;
...
}给定如下代码:
int main()
{
std::vector<int> l(4,0);
#pragma omp parallel for
for(int i=0; i<l.size(); ++i){
printf("th %d = %d \n",omp_get_thread_num(),l[i]=i);
}
printf("\n");
#pragma omp parallel
{
for (auto i = l.begin(); i != l.end(); ++i) {
#pragma omp single nowait
{
printf("th %d = %d \n",omp_get_thread_num(),*i);
}
}
}
return 0;
} 导出OMP_NUM_THREADS=4,输出如下(注意第二节,工作线程数可以重复):
th 2 = 2
th 1 = 1
th 0 = 0
th 3 = 3
th 2 = 0
th 1 = 1
th 2 = 2
th 3 = 3https://stackoverflow.com/questions/8691459
复制相似问题