C++ 英特尔TBB it&x27;s 2时间比标准慢-tbb与标准

C++ 英特尔TBB it&x27;s 2时间比标准慢-tbb与标准,c++,c++11,vector,concurrency,tbb,C++,C++11,Vector,Concurrency,Tbb,我在两个程序之间做了一些比较,这两个程序使用mersenne twister中的伪随机整数填充给定向量,关键是TBB版本速度非常慢,std版本在TBB至少需要1.1秒的情况下,大约在0.6秒内完成任务 我还注意到,TBB并没有真正提供优化的算法来处理容器,但它只提供了通用构造(parallel_for,parallel_for each和类似)来处理通用任务,其中std提供了std::generate,在这种情况下,这是一个更好、更干净的解决方案 您可以在这里下载我的小测试,其中包含2个小源文件

我在两个程序之间做了一些比较,这两个程序使用mersenne twister中的伪随机整数填充给定向量,关键是TBB版本速度非常慢,std版本在TBB至少需要1.1秒的情况下,大约在0.6秒内完成任务

我还注意到,TBB并没有真正提供优化的算法来处理容器,但它只提供了通用构造(parallel_for,parallel_for each和类似)来处理通用任务,其中std提供了
std::generate
,在这种情况下,这是一个更好、更干净的解决方案

您可以在这里下载我的小测试,其中包含2个小源文件+一个用于gcc的Makefile

我做错什么了?我越是增加这个向量的大小,TBB的速度就越慢,我使用的是英特尔Q6600的Ubuntu 13.04 64位

TBB版本在某些方面可能更好

编辑:2个文件的完整源代码


config.hpp

#define N 10000000
#include <random>
#include <iostream>
#include <vector>
#include <algorithm>

#include "config.hpp"

int main() {

    std::vector<u_int32_t> v(N);

    std::mt19937 mt;
    std::uniform_int_distribution<u_int32_t> dist(0,499);

    std::generate(v.begin(),v.end(),[&]{return dist(mt);});

    return(0);
}
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for_each.h>

#include <random>
#include <iostream>

#include "config.hpp"

int main()
{
  tbb::concurrent_vector<u_int32_t> v(N);
  std::mt19937 mt;
  std::uniform_int_distribution<u_int32_t> dist(0, 499);
  tbb::parallel_for_each(v.begin(),v.end(),[&](u_int32_t& e){e = dist(mt); });

  return(0);
}
std.cpp

#define N 10000000
#include <random>
#include <iostream>
#include <vector>
#include <algorithm>

#include "config.hpp"

int main() {

    std::vector<u_int32_t> v(N);

    std::mt19937 mt;
    std::uniform_int_distribution<u_int32_t> dist(0,499);

    std::generate(v.begin(),v.end(),[&]{return dist(mt);});

    return(0);
}
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for_each.h>

#include <random>
#include <iostream>

#include "config.hpp"

int main()
{
  tbb::concurrent_vector<u_int32_t> v(N);
  std::mt19937 mt;
  std::uniform_int_distribution<u_int32_t> dist(0, 499);
  tbb::parallel_for_each(v.begin(),v.end(),[&](u_int32_t& e){e = dist(mt); });

  return(0);
}
#包括
#包括
#包括
#包括
#包括“config.hpp”
int main(){
std::向量v(N);
标准:MT19937MT;
标准:统一国际分布区(0499);
std::generate(v.begin(),v.end(),[&]{returndist(mt);});
返回(0);
}
tbb.cpp

#define N 10000000
#include <random>
#include <iostream>
#include <vector>
#include <algorithm>

#include "config.hpp"

int main() {

    std::vector<u_int32_t> v(N);

    std::mt19937 mt;
    std::uniform_int_distribution<u_int32_t> dist(0,499);

    std::generate(v.begin(),v.end(),[&]{return dist(mt);});

    return(0);
}
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for_each.h>

#include <random>
#include <iostream>

#include "config.hpp"

int main()
{
  tbb::concurrent_vector<u_int32_t> v(N);
  std::mt19937 mt;
  std::uniform_int_distribution<u_int32_t> dist(0, 499);
  tbb::parallel_for_each(v.begin(),v.end(),[&](u_int32_t& e){e = dist(mt); });

  return(0);
}
#包括
#包括
#包括
#包括
#包括“config.hpp”
int main()
{
tbb::并发向量v(N);
标准:MT19937MT;
标准:均匀分布区(0499);
tbb::每个(v.begin(),v.end(),[&](u_int32_t&e){e=dist(mt);})的并行;
返回(0);
}

您正在为ITBB将控制的所有员工共享随机数生成器(RNG),正如我从您的问题中看到的,这将是四个。撇开从多个线程中改变RNG状态的线程安全问题不谈,我想指出对缓存的影响:从四个处理器读取和写入RNG状态使用的相同内存,这很可能使缓存变得无用

让我们试试这个:

#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for_each.h>

#include <vector>
#include <functional>

#include <random>
#include <iostream>

#include "config.hpp"

static thread_local std::mt19937 mt;
static thread_local std::uniform_int_distribution<u_int32_t> dist(0, 499);

int main()
{
  std::vector<u_int32_t> v(N);

  auto f = [&v](std::pair<u_int32_t, u_int32_t> const& p) {
     for (size_t i=p.first; i < p.second; i++)
     {
        v[i] = dist( mt );
     }
  };

  std::vector< std::pair< u_int32_t, u_int32_t > > work;
  work.push_back( std::make_pair( 0, N/2) );
  work.push_back( std::make_pair( N/2, N) );

  tbb::parallel_for_each(
    work.begin(),
    work.end(),
    f 
    );

  return(0);
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括“config.hpp”
静态线程_本地标准::mt19937 mt;
静态线程本地标准::统一线程内部分布区(0499);
int main()
{
std::向量v(N);
自动f=[&v](标准::成对常数和p){
对于(大小i=p.first;i>工作;
工作。推回(标准::使配对(0,N/2));
工作。推回(标准::使配对(N/2,N));
tbb::每个并行_(
work.begin(),
work.end(),
F
);
返回(0);
}
现在时间减少到std版本的一半(我只有一个双核)。代码的作用是强制itbb在连续的内存块中工作,而不是分发数据,而是分发工作分配。我不认为这是使用ITBB的最佳方式,但另一方面,parallel_for_不能以块大小(从我在中看到的)馈送每个块,而使用*parallel_for*进行此操作需要一些研究。但这并不难:

#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for.h>

#include <vector>
#include <functional>

#include <random>
#include <iostream>

#include "config.hpp"

static thread_local std::mt19937 mt;
static thread_local std::uniform_int_distribution<u_int32_t> dist(0, 499);

int main()
{
  std::vector<u_int32_t> v(N);

  auto f = [&v](const tbb::blocked_range<u_int32_t>& p) {
     for (auto i=p.begin(); i < p.end(); i++)
     {
        v[i] = dist( mt );
     }
  };

  tbb::parallel_for(
    tbb::blocked_range<u_int32_t>(0,N), 
    f 
    );

  return(0);
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括“config.hpp”
静态线程_本地标准::mt19937 mt;
静态线程本地标准::统一线程内部分布区(0499);
int main()
{
std::向量v(N);
自动f=[&v](常数tbb::阻塞的范围&p){
对于(自动i=p.begin();i
您可能希望在OpenMP中使用一些并行构造,而不是使用ITBB,它已经与gcc捆绑多年了(而且您仍然可以谨慎地将ITBB与OpenMP一起使用)


那么随机数和并行代码呢?它们很乱。如果您想独立地为RNG和时钟进行种子设定,上面的代码可能就足够了。如果您希望获得可复制的结果和不相关的RNG,那么您必须注意每个生成器都由特定于线程的种子进行初始化,并且您还需要一种方法,使每个种子通过其线程接触工作的确定部分

您的代码实际上并没有做任何计算开销大的事情,而且也在写入内核之间共享的内存块,这将导致缓存线频繁失效。内存访问很有可能主导整个运行时,然后并行化对共享数据结构的访问(具有潜在的锁定和类似的开销)只会使这种开销更大

此外,正如dsign所提到的,您通过在线程之间共享随机数生成器引入了额外的开销,这将进一步增加开销


最后,你并不是真的在比较苹果和苹果。使用任何类型的并行运行时库(如Intel的TBB)都是有代价的,即此运行时带来的开销-您必须启动它,“
concurrent\u vector
将具有诸如锁之类的访问机制,这些机制会导致额外开销等。这些机制都不是免费的,您看到的性能差异很可能仅仅归因于您创建的额外运行时开销。

这会产生什么影响

void f(u_int32_t& e)
{
    thread_local std::mt19937 mt;
    thread_local std::uniform_int_distribution<u_int32_t> dist(0, 499);
    e = dist(mt);
}


int main()
{
  tbb::concurrent_vector<u_int32_t> v(N);
  tbb::parallel_for_each(v.begin(),v.end(),f);

  return(0);
}
void f(u_int32_t&e)
{
螺纹_本地标准::mt19937 mt;
线程本地标准:统一内部分布区(0499);
e=距离(mt);
}
int main()
{
tbb::并发向量v(N);
tbb::每个(v.begin(),v.end(),f)的并行;
返回(0);
}

在问题中张贴代码。大多数人都不愿意下载未知的文件。请解释什么是您的构建/运行环境。如果你只有一个单核,可能加速比低于开销…@Massa它已经存在了,我有一个四核CPU(没有HT,但我不这么认为)