我认为您可以在大部分时间将std::vector 与 OpenMP 一起使用,并且仍然具有良好的性能。例如下面的代码并行填充std::vectors,然后将它们组合到最后。只要您的主循环/填充功能是瓶颈,这通常应该可以正常工作并且是线程安全的。
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());
}
编辑:
OpenMP 4.0 允许使用#pragma omp declare reduction 进行用户定义的缩减。上面的代码可以用to来简化
#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);
编辑:
到目前为止我所展示的内容并没有按顺序填充向量。如果顺序很重要,那么可以这样完成
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());
}
}
这避免了为每个线程保存一个 std::vector,然后在并行区域之外将它们串行合并。我了解了这个“技巧”here。 我不确定如何为用户定义的减少执行此操作(或者是否可能)。。用户定义的缩减无法做到这一点。
我刚刚意识到关键部分不是必需的,我从这个问题parallel-cumulative-prefix-sums-in-openmp-communicating-values-between-thread 中发现了这一点。此方法也可以正确获取顺序
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;