为什么std :: for_each比__gnu_parallel :: for_each快

时间:2019-01-15 16:19:39

标签: c++ parallel-processing stl

我试图理解为什么在以下示例中运行在单线程上的std::for_each~3__gnu_parallel::for_each

Time =0.478101 milliseconds

vs

Time =0.166421 milliseconds

这是我用来进行基准测试的代码:

#include <iostream>
#include <chrono>
#include <parallel/algorithm>

//The struct I'm using for timming
struct   TimerAvrg
{
    std::vector<double> times;
    size_t curr=0,n;
    std::chrono::high_resolution_clock::time_point begin,end;
    TimerAvrg(int _n=30)
    {
        n=_n;
        times.reserve(n);
    }

    inline void start()
    {
        begin= std::chrono::high_resolution_clock::now();
    }

    inline void stop()
    {
        end= std::chrono::high_resolution_clock::now();
        double duration=double(std::chrono::duration_cast<std::chrono::microseconds>(end-begin).count())*1e-6;
        if ( times.size()<n)
            times.push_back(duration);
        else{
            times[curr]=duration;
            curr++;
            if (curr>=times.size()) curr=0;}
    }

    double getAvrg()
    {
        double sum=0;
        for(auto t:times)
            sum+=t;
        return sum/double(times.size());
    }
};



int main( int argc, char** argv )
{
    float sum=0;
    for(int alpha = 0; alpha <5000; alpha++)
    {
        TimerAvrg Fps;
        Fps.start();
        std::vector<float> v(1000000);
        std::for_each(v.begin(), v.end(),[](auto v){ v=0;});
        Fps.stop();
        sum = sum + Fps.getAvrg()*1000;
    }

    std::cout << "\rTime =" << sum/5000<< " milliseconds" << std::endl;
    return 0;
}

这是我的配置:

gcc version 7.3.0 (Ubuntu 7.3.0-21ubuntu1~16.04) 

Intel® Core™ i7-7600U CPU @ 2.80GHz × 4

htop检查程序是否在单线程或多线程中运行

g++ -std=c++17 -fomit-frame-pointer -Ofast -march=native -ffast-math -mmmx -msse -msse2 -msse3 -DNDEBUG -Wall -fopenmp benchmark.cpp -o benchmark 

gcc 8.1.0不会编译相同的代码。我收到了错误消息:

/usr/include/c++/8/tr1/cmath:1163:20: error: ‘__gnu_cxx::conf_hypergf’ has not been declared
   using __gnu_cxx::conf_hypergf;

我已经检查了几则帖子,但是它们很老或不是同一问题。

我的问题是:

为什么并行速度较慢?

我使用了错误的功能?

cppreference中,这表示不支持带有Standardization of Parallelism TS的gcc(表中以红色表示),并且我的代码正在并行运行!?

2 个答案:

答案 0 :(得分:4)

您的功能[](auto v){ v=0;}非常简单。

可以通过单次调用memset来替换该函数,也可以将SIMD指令用于单线程并行性。知道它会覆盖与向量最初相同的状态,因此可以优化整个循环。对于优化者来说,替换std::for_each比并行实现要容易。

此外,假设并行循环使用线程,则必须记住创建和最终同步(在这种情况下,在处理期间无需同步)会产生开销,这对于您的琐碎操作而言可能很重要。

线程并行性通常仅在计算量大的任务上才值得。 v=0是计算成本最低的操作之一。

答案 1 :(得分:1)

您的基准测试有问题,我什至感到惊讶,它需要花费一些时间来运行。

您写道:      std :: for_each(v.begin(),v.end(),[](auto v){v = 0;});

由于voperator()的局部参数,没有任何读取,所以我希望它会被编译器删除。 由于您现在有了一个带有主体的循环,因此可以除去该循环,并且没有明显的效果。 与此类似,向量也可以删除,因为您没有任何读取器。

因此,没有任何副作用,可以将其全部消除。如果您将使用并行算法,那么您可能会有某种同步,这会使优化变得更加困难,因为另一个线程可能会有副作用?证明它并不复杂,更不用说可能存在的线程管理的副作用了?

为解决此问题,许多基准测试在宏程序中都有大量使用,以迫使编译器承担副作用。在lambda中使用它们,以便编译器不会将其删除。