Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何通过C++;std::使用OpenMP的列表?_C++_List_Parallel Processing_Openmp - Fatal编程技术网

C++ 如何通过C++;std::使用OpenMP的列表?

C++ 如何通过C++;std::使用OpenMP的列表?,c++,list,parallel-processing,openmp,C++,List,Parallel Processing,Openmp,我想使用OpenMP以并行方式遍历std::list中的所有元素。循环应该能够改变列表的元素。有没有简单的解决办法?当迭代器是随机访问迭代器时,OpenMP 3.0似乎支持并行for循环,但在其他情况下不支持。在任何情况下,我都希望使用OpenMP 2.0,因为我无法完全控制哪些编译器对我可用 如果我的容器是向量,我可以使用: #pragma omp parallel for for (auto it = v.begin(); it != v.end(); ++it) { it->

我想使用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();
}

我知道我可以将列表复制到一个向量中,进行循环,然后将所有内容复制回来。但是,如果可能的话,我希望避免这种复杂性和开销。

我怀疑这是可能的,因为你不能不遍历列表就跳入列表的中间。列表不存储在连续内存中,std::list迭代器也不是随机访问。它们只是双向的。

如果您决定使用
Openmp 3.0
,您可以使用
任务
功能:

#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();
}
这个屏障不是严格需要的,但是如果
process
改变了被处理的元素(意味着它不是一个const方法),如果线程在一个已经被改变的序列上迭代,那么在没有它的情况下可能会有某种错误的共享。这种方法将在序列上迭代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();
向量块;
chunks.reserve(最大线程数);
size\u t chunk\u size=list.size()/max\u线程;
auto cur_iter=list.begin();
对于(int i=0;iprocess();
}
这只需要在
列表
上进行三次迭代(两次,如果您不需要迭代就可以得到列表的大小)。我认为对于非随机访问迭代器来说,这大概是最好的了,无需使用
任务
或迭代某些不合适的数据结构(如指针向量)。

这可以理解为展开为:

{
  it = listl.begin
  #pragma omp single nowait
  {
    it->compute();
  }
  it++;
  #pragma omp single nowait
  {
    it->compute();
  }
  it++;
...
}
给出如下代码:

#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();
}
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;                                                               
} 
#pragma omp for ordered schedule(static)
for(int i=0; i<nthreads; i++) {
    #pragma omp ordered
    l.splice(l.end(), l, l2.begin(), l2.end());
}

不使用OpenMP 3.0您可以选择让所有线程在列表上迭代:

std::list<T>::iterator it;
#pragma omp parallel private(it)
{
   for(it = list1.begin(); it!= list1.end(); it++)
   {
      #pragma omp single nowait
      {
         it->compute();
      }
   } 
} 
std::list::iterator;
#pragma omp并行专用(it)
{
for(it=list1.begin();it!=list1.end();it++)
{
#pragma-omp-single-nowait
{
它->计算();
}
} 
} 
在这种情况下,每个线程都有自己的迭代器副本(private),但只有一个线程将访问特定元素(single),而其他线程将前进到下一个项目(nowait

或者,您可以循环一次以构建指针向量,然后在线程之间分布:

std::vector< T*> items;

items.reserve(list.size());
//put the pointers in the vector
std::transform(list.begin(), list.end(), std::back_inserter(items), 
               [](T& n){ return &n; }
);

#pragma omp parallel for
for (int i = 0; i < items.size(); i++)
{
  items[i]->compute();
}
std::vector项;
items.reserve(list.size());
//将指针放在向量中
std::transform(list.begin()、list.end()、std::back_插入器(项),
[](T&n){return&n;}
);
#pragma-omp并行
对于(int i=0;icompute();
}

根据您的具体情况,其中一种可能更快。测试哪一种更适合您很容易。

这里有一个解决方案,允许并行插入/删除列表中的新元素

对于包含
N
元素的列表,我们首先将列表切割为
N个读取项
列表 大致使用
N/nthreads
元素。在并行区域中,可以这样做

int ithread = omp_get_thread_num();
int nthreads = omp_get_num_threads();
int t0 = (ithread+0)*N/nthreads;
int t1 = (ithread+1)*N/nthreads;

std::list<int> l2;
#pragma omp for ordered schedule(static)
for(int i=0; i<nthreads; i++) {
    #pragma omp ordered
    {
        auto it0 = l.begin(), it1 = it0;
        std::advance(it1, t1-t0);       
        l2.splice(l2.begin(), l2, it0, it1);
    }
}
auto it = l2.begin();
for(int i=(t0+4)/5; i<(t1+4)/5; i++) {
    std::advance(it, 5*i-t0);
    l2.insert(it, -1);
}
最后,在对列表进行并行操作后,我们将每个线程的列表拼接回一个列表,顺序如下:

#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();
}
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;                                                               
} 
#pragma omp for ordered schedule(static)
for(int i=0; i<nthreads; i++) {
    #pragma omp ordered
    l.splice(l.end(), l, l2.begin(), l2.end());
}

如果每个元素的处理比迭代要昂贵得多,那么并行化仍然是可取的和
it2=it1+1
,然后
it1+=2
it2+=2
,如果有两个执行线程。@KennyTM,谢谢。这与我正在寻找的是一样的。但是非随机访问迭代器上的+=2仍然需要一次遍历迭代器一次。如果列表是con,我想这会起作用循环过程中的st。但我猜您需要进行大量的设置才能使其正常工作。您不能说it1+=2,因为您仍然需要测试it1!=list.end(),这两个增量。感谢您提供的详细答案。我想迭代整个
map
。如何使用OpenMp迭代整个map?应该是:#pragma omp parallel private(它)以便每个线程获得迭代器的副本
#include <algorithm>
#include <iostream>
#include <list>
#include <omp.h>

int main(void) {
  std::list<int> l;
  for(int i=0; i<22; i++) {
    l.push_back(i);
  }
  for (auto it = l.begin(); it != l.end(); ++it) {
    std::cout << *it << " ";
  } std::cout << std::endl;

  int N = l.size();
  #pragma omp parallel
  {
    int ithread = omp_get_thread_num();
    int nthreads = omp_get_num_threads();
    int t0 = (ithread+0)*N/nthreads;
    int t1 = (ithread+1)*N/nthreads;

    //cut list into nthreads lists with size=N/nthreads
    std::list<int> l2;
    #pragma omp for ordered schedule(static)
    for(int i=0; i<nthreads; i++) {
      #pragma omp ordered
      {
    auto it0 = l.begin(), it1 = it0;
    std::advance(it1, t1-t0);       
    l2.splice(l2.begin(), l2, it0, it1);
      }
    }
    //insert -1 every 5th postion
    auto it = l2.begin();
    for(int i=(t0+4)/5; i<(t1+4)/5; i++) {
      std::advance(it, 5*i-t0);
      l2.insert(it, -1);
    }

    //splice lists in order back together.
    #pragma omp for ordered schedule(static)
    for(int i=0; i<nthreads; i++) {
      #pragma omp ordered
      l.splice(l.end(), l, l2.begin(), l2.end());
    }  
  }

  for (auto it = l.begin(); it != l.end(); ++it) {
    std::cout << *it << " ";
  } std::cout << std::endl;  
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 
-1 0 1 2 3 4 -1 5 6 7 8 9 -1 10 11 12 13 14 -1 15 16 17 18 19 -1 20 21