TBB并行编程之旅
并发和并行
多核和异步
TBB并行编程库(Intel)
安装TBB
任务组task_group
tbb的任务不一定对应一个线程,它会根据电脑的核心,创建合适的线程池,
你的任务,会被分时的在写线程池中跑
封装好的parallel_invoke
时间复杂度和工作量复杂度
多线程,工作量不变,时间减少
并行映射
paraller_for
简单写法
parallel_for_each基于迭代器区间
blocked_range2d 二维区间
blocked_range3d 三维区间
n维
缩并reduce
并行缩并
改进的并行缩并(GPU)
图形渲染的计算非常适合这样并行缩并,于是人们发明了GPU
封装好了 Parallel_reduce
保证每次运行一样 parallel_deterministic_reduce
并行缩并:避免浮点误差
float类型避免大数加小数:1000000+0.00001
精度会失效
serial_avg += a[i]在,avg很大,a[i]比较小,在相加是,float会进度丢失,在串行运算时,这个误差比并行时更明显。
扫描
并行扫描
工作量更多,但时间更少
size_t n = 1<<8;
std::vector<float> a(n);
float res = tbb::parallel_scan(tbb::blocked_range<size_t>(0, n), (float)0,
[&] (tbb::blocked_range<size_t> r, float local_res, auto is_final) {
for (size_t i = r.begin(); i < r.end(); i++) {
local_res += std::sin(i);
std::cout << i <<'\t'<<local_res<< std::endl;
if (is_final) {
a[i] = local_res;
std::cout << i<<'\t'<<local_res << std::endl;
}
}
return local_res;
}, [] (float x, float y) {
return x + y;
});
std::cout << a[n / 2] << std::endl;
std::cout << res << std::endl;
是的,你的理解基本上是正确的。可以将整个并行计算过程看作是两个阶段的操作。
在第一阶段,每个线程独立地计算自己负责的处理范围内的累积值,这个累积值被称为「局部累积值」(`local_res`)。这个阶段中,各个线程并行地执行累积操作,没有依赖关系的限制。每个线程只关注自己负责的处理范围,通过累积操作计算出自己的`local_res`值。
在第二阶段,TBB库通过特定的算法和同步机制来处理各个处理范围之间的依赖关系,并将各个处理范围的结果正确地累积到最终的累积结果中。
具体来说,当一个线程完成自己处理范围的累积操作后,它会检查是否有依赖于前一个处理范围的`local_res`值。如果前一个处理范围的`local_res`值已经计算完毕,该线程就可以将自己的`local_res`值与前一个处理范围的最终累积结果进行合并,得到当前处理范围的最终累积结果。
如果前一个处理范围的`local_res`值尚未计算完毕,该线程则需要等待前一个处理范围的结果完成,并在合适的时机将自己的`local_res`值与前一个处理范围的最终累积结果进行合并。
通过这种方式,每个线程在第二阶段中能够获取到前一个处理范围的最终累积结果,并将其与自己的`local_res`值进行合并,得到当前处理范围的最终累积结果。这样,整个过程中的依赖关系得到了正确处理,最终得到了整个处理范围的最终累积结果。
需要注意的是,具体的算法和同步机制是由TBB库内部处理的,以确保正确的并行计算和结果的正确性。因此,尽管涉及两个阶段的操作,但开发者无需显式地编写这些逻辑,TBB库会自动处理并保证正确性和高效性。
所以这个is_final就是第2阶段判断其依赖值是否算完的。
parallel_can
评价标准
加速比:串行/并行