代码之家  ›  专栏  ›  技术社区  ›  Ja_cpp

为什么std::for_each比\u gnu并行::for_each快

  •  0
  • Ja_cpp  · 技术社区  · 7 年前

    我想知道为什么 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 
    

    同样的代码不能用GCC8.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 据说GCC Standardization of Parallelism TS 不支持(表中提到红色),我的代码正在并行运行!?

    2 回复  |  直到 7 年前
        1
  •  4
  •   eerorika    7 年前

    你的功能 [](auto v){ v=0;} 极其 简单。

    该函数可以替换为对 memset 或者使用SIMD指令实现单线程并行性。有了它覆盖和向量最初相同的状态的知识,整个循环可以被优化掉。乐观主义者可能更容易更换 std::for_each 而不是并行实现。

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

    线程并行性通常只在计算代价很高的任务中才值得。 v=0 是计算成本最低的操作之一。

        2
  •  1
  •   JVApen    7 年前

    你的基准测试是错误的,我甚至很惊讶运行它需要时间。

    你写道: std::对于每个(v.begin(),v.end(),[](auto v)v=0;);

    AS v 是的局部参数 operator() 如果没有读取,我希望它被编译器删除。 因为你现在有了一个带身体的循环,这个循环可以被移除,而且没有明显的效果。 与此类似,向量也可以被删除,因为您没有任何读者。

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

    为了解决这一问题,很多基准测试都在宏中使用卡车来强制编译器承担副作用。在lambda中使用它们,这样编译器就不会删除它。