使用Intel线程构建块(TBB)的Parallel_Scan组件无法实现加速
No Speedup achieved using Parallel_Scan Component of Intel Thread Building Blocks (TBB)
我正在探索Intel线程构建块中的Parallel_Scan组件,这是在关联操作的情况下使用的,我发现Parallel_Scan比它在串行中完成的要多10倍。
我写的检查代码是:
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include "tbb/task_scheduler_init.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_scan.h"
#include "tbb/tick_count.h"
using namespace std;
using namespace tbb;
template <class T>
class Body
{
T reduced_result;
T* const y;
const T* const x;
public:
Body( T y_[], const T x_[] ) : reduced_result(0), x(x_), y(y_) {}
T get_reduced_result() const {return reduced_result;}
template<typename Tag>
void operator()( const blocked_range<int>& r, Tag )
{
T temp = reduced_result;
for( int i=r.begin(); i<r.end(); ++i )
{
temp = temp+x[i];
if( Tag::is_final_scan() )
y[i] = temp;
}
reduced_result = temp;
}
Body( Body& b, split ) : x(b.x), y(b.y), reduced_result(10) {}
void reverse_join( Body& a )
{
reduced_result = a.reduced_result + reduced_result;
}
void assign( Body& b )
{
reduced_result = b.reduced_result;
}
};
template<class T>
float DoParallelScan( T y[], const T x[], int n)
{
Body<int> body(y,x);
tick_count t1,t2,t3,t4;
t1=tick_count::now();
parallel_scan( blocked_range<int>(0,n), body , auto_partitioner() );
t2=tick_count::now();
cout<<"Time Taken for parallel scan is t"<<(t2-t1).seconds()<<endl;
return body.get_reduced_result();
}
template<class T1>
float SerialScan(T1 y[], const T1 x[], int n)
{
tick_count t3,t4;
t3=tick_count::now();
T1 temp = 10;
for( int i=1; i<n; ++i )
{
temp = temp+x[i];
y[i] = temp;
}
t4=tick_count::now();
cout<<"Time Taken for serial scan is t"<<(t4-t3).seconds()<<endl;
return temp;
}
int main()
{
task_scheduler_init init1;
int y1[100000],x1[100000];
for(int i=0;i<100000;i++)
x1[i]=i;
cout<<fixed;
cout<<"n serial scan output is t"<<SerialScan(y1,x1,100000)<<endl;
cout<<"n parallel scan output is t"<<DoParallelScan(y1,x1,100000)<<endl;
return 0;
}
请帮我找出我错在哪里
我是tbb::parallel_scan的原作者。
在使用"大核"的多核系统上获得并行扫描的加速可能很难。原因是并行扫描本质上是一个两步算法。如果数据不适合外部缓存,并行扫描必须从内存中传入数据两次,而串行算法只需要做一次。对于像整数加法这样简单的操作,内存流量(而不是ALU)通常是"大核心"的瓶颈,因为"大核心"将大量硬件资源用于快速串行执行。如果数据不适合外部缓存,则可能没有足够的工作来分摊并行开销。
我能够通过以下更改和条件为您的示例获得一些并行加速(约2x):
-
我在循环之前将r.r end()的读提升到一个局部变量中,如下所示:
int rend = r.end(); for( int i=r.begin(); i<rend; ++i )
这样做有助于编译器生成更好的代码,因为它知道rend是循环不变的。如果没有提升,编译器必须假设对y[i]的写入可能会覆盖r.end()读取的r字段。类似地,将字段x和y的读取提升到局部变量中可能会有所帮助,尽管编译器应该能够从基于类型的别名消歧中判断对y[i]的写入不会影响这些字段。
-
我将输入数组增加到有10,000,000个元素,因此有更多的工作要做,从而更好地分摊并行调度开销。为了避免堆栈溢出,我在堆中分配了数组。
-
我预热了TBB运行时。一般来说,在进行这种计时练习时,最好先进行一次"丢弃"运行,这样就不会计算一次性启动成本。为了预热(对于串行和并行代码),我在时序逻辑周围包装了一个三次循环,如下所示:
for( int k=0; k<3; ++k ) { cout<<"n serial scan output is t"<<SerialScan(y1,x1,n)<<endl; cout<<"n parallel scan output is t"<<DoParallelScan(y1,x1,n)<<endl; }
这是我在大多数计时实验中所做的事情,所以我可以看到首次成本是否显著,或者是否存在其他兴趣变化。
-
我用"gcc -O2 -ltbb"编译。
-
我在一个16核系统上运行,有两个"Sandy Bridge"芯片。
查看内存带宽影响的另一种方法是在具有许多小内核的系统上尝试这个示例。我在英特尔Xeon Phi处理器(TM)上尝试了这个例子,并根据前面描述的类型进行了修改,该处理器具有高内存带宽和许多小内核。我可以得到4 -7倍的平行加速。将问题大小提高到100,000,000,我的速度提高了10 -20倍。
总而言之:只有当并行计算的好处超过两次传递数据的开销时,多线程扫描才能获得回报。
总共需要多长时间?也许你的输入太小了,因此上下文切换主导了并行版本的运行时间。如果你增加习题集,它会有什么表现?如果你做一些计算量更大的事情比如你现在做的简单求和,它会有什么表现?
- 如何将 txt 文件中的行分隔为组件C++
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 到连接组件算法的问题(递归)
- 两个 COM 组件中 ENUM 的重复条目
- 为什么 gcc 会产生这种奇怪的组件与叮当声?
- 使用 bfs 解决连接组件问题时得到错误的答案
- 如何在ECS框架中更新组件数据和通知系统
- 如何从ECS中的模板方法获取组件?
- 使用蓝牙组件将数据从Android手机传输到串行设备时出现问题
- 组件上的虚幻引擎可蓝图UFUNCTION会导致构建错误
- 在C++中访问蓝图类组件的正确方法是什么?
- 有关纯 ECS(实体组件系统)和更新系统的问题
- 提高基于组件的游戏引擎的效率
- 从组件的 std::type_index 获取派生最多的类型
- 我们可以将阈值应用于色彩空间模型的单个组件(如 RGB 和 LAB)吗?
- 如何通过 UActor组件获取演员列表?
- 在Visual Studio 2017中使用c ++组件构建python解决方案时出现问题
- 如何访问基类向量中的子类变量?(对于实体组件系统)
- 根据组件 1、2(和 3)对空间 (2D/3D) 向量进行排序
- 从 WinRT C++组件访问 COM 对象