将迭代器的地址传递给STL :: for_each中的函数

时间:2011-10-21 01:02:00

标签: c++ function stl iterator

我有一个我最终想并行化的功能。

目前,我在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版本一样自动神奇地并行化。

思想,想法,建议?

3 个答案:

答案 0 :(得分:3)

如果您想在此处进行真正的并行化,请使用

  • GCC在(-O3)和SIMD上使用树矢量化优化(例如-march = native以获得SSE支持)。如果操作(dostuff)非常重要,您可以提前选择(std::transformstd::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 _

如果您想要真正的并行性,请使用以下

之一

GNU Parallel Mode

使用g++ -fopenmp -D_GLIBCXX_PARALLEL编译:

__gnu_parallel::accumulate(data.begin(), data.end(), 0.0);
直接

OpenMP

使用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。