TBB并行加速---parallel_for使用

一、OpenMP TBB选择

参考https://blog.csdn.net/yuwei629/article/details/9302343

  • OpenMP需要编译器支持,TBB需要下载运行库
  • TBB 大量使用了 C++ 模板和用户自定义类型,所以特别适宜对象导向程度较高的代码, 但TBB只支持C++

二、TBB parallel_for

2.1 构建类或者结构体

依据实际问题,构建类或者结构体,主要包含初始化和重载函数运算符注意这里重载函数运算符需要加入const限定,避免更改函数体。

struct MT{
  MT(...){}
  void operator() (const tbb::blocked_range<int>& r) const{}
};

2.2 调用

默认参数调用

调用tbb::parallel_for,先初始化一个类或结构体,设置并行的一个范围,可以采用如下调用方式。

MT mt(datas, ratio); 
tbb::parallel_for(tbb::blocked_range<int>(0, static_cast<int>(datas.size())), mt);  
控制块大小

在传入范围给tbb::parallel_for时可以控制其分块的大小,blocked_range<T>(begin,end,grainsize)grainsize默认大小为1,如果想将并行时每块大小设置为2,可以更改grainsize的大小,如下所示:

MT mt(datas, ratio); 
tbb::parallel_for(tbb::blocked_range<int>(0, static_cast<int>(datas.size()), 2), mt); 


如上图所示case A 和case B 具有相同的总灰色区域。case A 中粒度太小有较高的并行度,但会导致资源开销比例相对较高,case B 中相对大一点的粒度可以降低此比例,但也降低了并行度。实际使用时可以参考硬件资源和并行度对grainSize进行跳转

官网示例给出了不同grainsize对应的时间消耗。

带宽及缓存

由数据集和缓存的相对大小决定的亲和力的好处如下:

parallel_for提供了不同的方式

官网示例演示了affinity_partitionerauto_partitioner两种方式的运行时间对比。

MT mt(datas, ratio); 
static tbb::affinity_partitioner ap;
tbb::parallel_for(tbb::blocked_range<int>(0, static_cast<int>(datas.size()), 2), mt, ap); 

2.3 加锁

并行时有时会不同线程访问同一对象,避免冲突可以在相应位置加锁,然后执行完再解锁。

mutex_.lock();   //加锁
mutex_.unlock(); //解锁

2.4 完整示例

struct MT { 
	 MT(std::vector<double>& datas, double ratio, tbb::mutex& mutex) : datas_(&datas), ratio_(ratio), mutex_(mutex)
	 {}

	 void operator() (const tbb::blocked_range<int>& r) const
	 {
                 // mutex_.lock();
		 for (int idx = r.begin(); idx != r.end(); ++idx)
		 { 
			 (*datas_)[idx] *= ratio_;
		 }
                 // mutex_.unlock();
	 }
			 
	 static tbb::mutex mutex_;
	 std::vector<double>* datas_;
	 const double ratio_;
 };

// tbb::mutex CompresionMT::mutex_;  // global define not need init

void TestTBB(std::vector<double>& datas, double ratio, bool bMT)
 {
	 if (bMT) {
		 /*std::vector<int> edgeOrders;
		 for (int eId = 0; eId < datas.size(); ++eId) {
			 edgeOrders.push_back(eId);
		 }
		 MT mt(edgeOrders, datas, ratio); */
                 tbb::mutex mutex;   // if global define the not need
		 MT mt(datas, ratio, mutex);
		 static tbb::affinity_partitioner ap;
		 tbb::parallel_for(tbb::blocked_range<int>(0, static_cast<int>(datas.size()),2), mt, ap); 
	 }
	 else {
		 for (int idx = 0; idx < datas.size(); ++idx) {
			 datas[idx] *= ratio;
		 }
	 }
 }
    #include <chrono>
    std::vector<double> datas1, datas2;
    int num = 10000000;
    for (int i = 1; i <= num; ++i) {
        datas1.push_back(i);
        datas2.push_back(i);
    }
    double ratio = 2.0;
    std::chrono::time_point<std::chrono::steady_clock> t1 = std::chrono::steady_clock::now();
    TestTBB(datas1, ratio, false);
    std::chrono::time_point<std::chrono::steady_clock> t2 = std::chrono::steady_clock::now();
    std::chrono::duration<double> elapsed1 = t2 - t1;
    std::cout << "t2 - t1: " << elapsed1.count() << "s\n"<< std::endl;
  
    std::chrono::time_point<std::chrono::steady_clock> t3 = std::chrono::steady_clock::now();
    TestTBB(datas2, ratio, true);
    std::chrono::time_point<std::chrono::steady_clock> t4 = std::chrono::steady_clock::now();
    std::chrono::duration<double> elapsed2 = t4 - t3;
    std::cout << "t4 - t3: " << elapsed2.count() << "s\n" << std::endl;

参考资料

tbb::parallel_reduce的使用及其他可参考官网链接:
https://oneapi-src.github.io/oneTBB/

posted @   半夜打老虎  阅读(2450)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
历史上的今天:
2020-02-20 【数据预处理】:图像去均值:image mean 和 pixel mean
点击右上角即可分享
微信分享提示