如何将for循环转换为STL的for_each语句

5

我希望将我的for循环转换为STL std::for_each循环。

 bool CMyclass::SomeMember()
 {
    int ii;
        for(int i=0;i<iR20;i++)
            {
              ii=indexR[i];
              ishell=static_cast<int>(R[ii]/xStep);
              theta=atan2(data->pPOS[ii*3+1], data->pPOS[ii*3]);
              al2[ishell] += massp*cos(fm*theta);
            }
 }

实际上,我打算使用g++4.4中的并行STL。

 g++ -D_GLIBCXX_PARALLEL -fopenmp

如果代码是使用标准STL库编写的,则可以在不更改代码的情况下并行运行代码。


1
为什么不直接在for循环前加上#pragma omp parallel for呢? - stephan
1
+1 Stephan,虽然al2[ishell]的**+=**需要一个reduction子句或原子屏障。不确定这将如何转换为STL并行。 - peterchen
是的,其中一个解决方案将是“#pragma omp parallel for”,但始终需要在每个for之前考虑共享和私有变量,这通常是错误来源之一。 - Arman
@peterchen:同意,可能需要进行缩减(除非“ishell”仅使用每个值一次)。 - stephan
1
顺便说一下,C++0x 很可能也会包含 lambda 表达式,它可以用来简化代码:http://www2.research.att.com/~bs/C++0xFAQ.html#lambda - amit kumar
4个回答

5
你需要将循环体分离到单独的函数或函数对象中;我假设所有未声明的变量都是成员变量。
void CMyclass::LoopFunc(int ii)  {
    ishell=static_cast<int>(R[ii]/xStep);
    theta=atan2(data->pPOS[ii*3+1],
    data->pPOS[ii*3]);
    al2[ishell] += massp*cos(fm*theta);
}

bool CMyclass::SomeMember()  { 
    std::for_each(&indexR[0],&indexR[iR20],std::tr1::bind(&CMyclass::LoopFunc,std::tr1::ref(*this));
}

太棒了!这个概念是最好的。谢谢! - Arman
1
这个解决方案是线程安全的吗?我认为不是。如果并行的 for_each 将循环分成不同的线程,所有线程都将访问相同的成员属性,而没有任何保护措施。 - David Rodríguez - dribeas
他需要注意线程安全 - 他原来的代码没有清楚地说明哪些变量是局部变量,哪些是成员变量。在不同线程中写入索引i和索引i + 1对于一个整型数组来说应该是安全的,不是吗? - JoeG
如果你从不同的线程读取成员,则无需担心。对于写入操作,可能需要加锁。 - Arman

1
class F {
   public:
   void operator()(int ii) {
              ishell=static_cast<int>(R[ii]/xStep);
              theta=atan2(data->pPOS[ii*3+1], data->pPOS[ii*3]);
              al2[ishell] += massp*cos(fm*theta);
   } 
   F(int[] r): //and other parameters should also be passed into the constructor
      r_(r) {}
   void:
   int[] r_; // refers to R[ii] array
   // and other parameters should also be stored
};

F f(R); // pass other parameters too  
for_each(&indexR[0], &indexR[iR20], f);

然而,使用这种“自动并行化”可能不是一个好主意,因为您需要记住每个并行计算的颗粒大小--我不确定编译器如何考虑颗粒大小。


这看起来不错,但是想象一下,如果在SomeMember()函数中有几个循环,我们需要为每个循环定义类似于函数对象的类。 - Arman
是的,这就是C++函数式编程的方式。如果你要这样做,最好使用Intel TBB并使用parallel_for进行并行化(我更喜欢TBB的方式)。 - amit kumar
是的,TBB 设计良好,但我是一名研究人员,更喜欢 GPL 软件。 - Arman
不确定为什么您更喜欢GPL。TBB许可证(带有运行时异常的GPL)应与GPL兼容(尽管我不是律师,无法做出可靠的陈述)。或者您是指您更喜欢开源?那就是另一回事了。 - amit kumar

1

你不能仅仅将循环体分离成函数对象并假设它会被并行化,因为循环体内部有太多的依赖关系。只有当循环体内没有全局数组或指针时,循环才能并行运行。如果您提供完整的函数体,我们可以考虑如何将其改为并行版本。


0

你需要将循环体转换为函数或者函数对象。由于有很多未声明的变量,所以我无法轻易地分离出循环体。以下是我的尝试:

class DoStuff
{
  int* R;
  int xStep;
  Data* data;
  double massp;
  double fm;
  double* al2;
public:
  DoStuff(int* R_, int xStep_, Data* data_, double massp_, double fm_, double* al2_) :
    R(R_), xStep(xStep_), data(data_), massp(massp_), fm(fm_), al2(al2_) {}

  void operator()(int ii)
  {
     int ishell = static_cast<int>(R[ii]/xStep);
     double theta = atan2(data->pPOS[ii*3+1], data->pPOS[ii*3]);
     al2[ishell] += massp*cos(fm*theta);
  }
};

for_each(indexR, indexR+iR20, DoStuff(R, xStep, data, massp, fm, al2));

使用初始化列表时,您不必重命名参数。例如,R(R)等都可以正常工作。 - Cat Plus Plus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接