【问题标题】:Why std::for_each is faster than __gnu_parallel::for_each为什么 std::for_each 比 __gnu_parallel::for_each 快
【发布时间】:2019-01-15 16:19:39
【问题描述】:

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

Time =0.478101 milliseconds

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(表中用红色提到)并且我的代码正在并行运行!?

【问题讨论】:

  • 请注意,&lt;parallel/algorithm&gt; 不是 C++17 的官方并行库。它在文件中说:“这个文件是标准 C++ 库的 GNU 扩展”。
  • 你能显示使用__gnu_parallel::for_each的代码吗?
  • std::foreach 可能正在使用 SIMD 指令优化为一个简单的循环
  • 循环也可能太短以至于并行性没有任何用处。创建线程并加入它们是有成本的。
  • @NathanOliver 完全一样,只需将“std::”替换为“__gnu_parallel::”这就是我每次都更改的内容

标签: c++ parallel-processing stl


【解决方案1】:

你的函数[](auto v){ v=0;}非常简单。

该函数可以用对memset 的一次调用来替换它,或者使用SIMD 指令来实现单线程并行。知道它会覆盖与向量最初具有的相同状态,整个循环可以被优化掉。优化器替换std::for_each 可能比并行实现更容易。

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

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

【讨论】:

  • 是的,我认为你是对的。它优化得非常好,时间比实现更适合并行化 (lambda)
【解决方案2】:

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

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

由于voperator() 的本地参数,没有读取,我希望它会被您的编译器删除。 由于您现在有一个带有主体的循环,因此可以删除该循环并且没有可观察到的效果。 与此类似,向量可以被删除,就像你没有任何阅读器一样。

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

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-04
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
相关资源
最近更新 更多