我有一个我最终想并行化的功能。
目前,我在for循环中调用内容。
double temp = 0;
int y = 123; // is a value set by other code
for(vector<double>::iterator i=data.begin(); i != data.end(); i++){
temp += doStuff(i, y);
}
doStuff需要知道列表中的距离。所以我用i - data.begin()来计算。
接下来,我想使用stl :: for_each函数。我的挑战是我需要传递迭代器的地址和 y的值。我已经看到使用bind2nd将参数传递给函数的示例,但是如何将迭代器的地址作为第一个参数传递?
boost FOREACH函数看起来也是可能的,但我不知道它是否会像STL版本一样自动神奇地并行化。
思想,想法,建议?
答案 0 :(得分:3)
如果您想在此处进行真正的并行化,请使用
GCC在(-O3)和SIMD上使用树矢量化优化(例如-march = native以获得SSE支持)。如果操作(dostuff)非常重要,您可以提前选择(std::transform
或std::for_each
)并累积下一个(std::accumulate
),因为累积将像SSE指令别无其他!
void apply_function(double& value)
{
value *= 3; // just a sample...
}
// ...
std::vector<double> data(1000);
std::for_each(data.begin(), data.end(), &apply_function);
double sum = std::accumulate(data.begin(), data.end(), 0);
注意虽然这实际上不会在多个线程上运行,但性能提升将是巨大的,因为SSE4指令可以在单个内核中处理许多浮动操作* in parallell _
如果您想要真正的并行性,请使用以下
之一使用g++ -fopenmp -D_GLIBCXX_PARALLEL
编译:
__gnu_parallel::accumulate(data.begin(), data.end(), 0.0);
直接使用g++ -fopenmp
double sum = 0.0;
#pragma omp parallel for reduction (+:sum)
for (size_t i=0; i<data.end(); i++)
{
sum += do_stuff(i, data[i]);
}
这将导致循环并行化为与实际机器上的(逻辑)CPU核心一样多的线程(OMP团队),并且结果“神奇地”组合和同步。
您可以使用有状态函数对象来模拟for_each的二进制函数。这不是完全推荐的做法。它也似乎效率很低(在没有优化的情况下进行编译时)。这是因为函数对象在STL中通过值传递。但是,期望编译器完全优化它的潜在开销是合理的,特别是对于如下的简单情况:
struct myfunctor
{
size_t index;
myfunctor() : index(0) {}
double operator()(const double& v) const
{
return v * 3; // again, just a sample
}
};
// ...
std::for_each(data.begin(), data.end(), myfunctor());
答案 1 :(得分:1)
temp += doStuff( i, y );
无法自动并行化。运算符+=
在并发性方面表现不佳。
此外,stl算法不会并行化任何内容。 Visual Studio和GCC都具有类似于std::for_each
的并行算法。如果那就是你必须使用的那些。
OpenMP可以自动并行化for循环,但你必须使用编译指示告诉编译器何时以及如何(它无法为你弄清楚)。
您可能会将并行化与循环展开混淆,这是std::for_each
实现中的常见优化。
答案 2 :(得分:1)
如果您可以更改doStuff
,以便将当前元素的值与当前元素所在的索引分开,则这非常简单。考虑:
struct context {
std::size_type _index;
int _y;
double _result;
};
context do_stuff_wrapper(context current, double value)
{
current._result += doStuff(current._index, value, current._y);
current._index++;
}
context c = { 0, 123, 0.0 };
context result = std::accumulate(data.begin(), data.end(), c, do_stuff_wrapper);
但请注意,标准库算法不能“自动并行化”,因为它们调用的函数可能有副作用(编译器知道是否产生副作用,但库函数不会产生副作用)。如果你想要一个并行循环,你将不得不使用一个特殊用途的并行算法库,如PPL或TBB。