英特尔TBB流程图开销

时间:2018-01-03 17:06:13

标签: multithreading parallel-processing intel tbb parallelism-amdahl

以下是我尝试对英特尔TBB流程图的性能进行基准测试。 这是设置:

  • 一个广播节点向{strong> continue_msg 后继节点发送N (a broadcast_node<continue_msg>
  • 每个后继节点执行的计算需要 t 秒。
  • 连续执行的总计算时间为 Tserial = N * t
  • 如果使用所有核心,理想的计算时间为 Tpar(ideal) = N * t / C ,其中 C 是核心数。
  • 加速定义为 Tpar(actual) / Tserial
  • 我在16核PC上用gcc5测试了代码。

以下结果显示加速作为单个任务(即身体)处理时间的函数:

t = 100 microsecond  ,   speed-up =  14
t  = 10 microsecond  ,   speed-up =   7
t  =  1 microsecond  ,   speed-up =   1

对于轻量级任务(其计算时间小于1微秒),并行代码实际上比串行代码慢。

以下是我的问题:

1 )这些结果是否符合英特尔TBB基准测试标准? 2 )当有数千个任务每个花费不到1微秒时,有一个更好的范例,而不是案例的流程图?

3 个答案:

答案 0 :(得分:3)

并行的开销

您的费用模式错误。

理想的并行计算时间是:

Tpar(ideal) = N * t / C + Pstart + Pend

其中Pstart是启动并行性所需的时间,Pend是完成它所需的时间。 Pstart大约数十毫秒并不罕见。

我不确定您是否熟悉OpenMP(虽然知道这是件好事)但是,出于我们的目的,它是一个在团队之间划分工作的线程模型线程。下图显示了与线程组大小相关的一些命令的开销:

OpenMP thread overheads

需要注意的是,让你的并行性(parallel for行)变得非常昂贵并且随着线程的数量而增长。结束并行性(barrier行)具有相似的成本。

事实上,如果你看看TBB的教程,第3.2.2节(&#34;自动分块&#34;)说:

  

注意:对于parallel_for,循环通常需要至少一百万个时钟周期才能提高其性能。例如,在2 GHz处理器上占用至少500微秒的环路可能会受益于parallel_for。

实现更快的速度

加速这样的代码的最好方法是,只有在存在大量操作时才能并行执行操作和/或调整执行工作的线程数,以便每个线程都有很多工作要做。在TBB中你可以实现类似的行为:

#include <tbb/parallel_for.h>

// . . .
if(work_size>1000)
  tbb::serial::parallel_for( . . . );
else
  tbb::parallel_for( . . . );
// . . . 

您希望将1000调整为足够高的数字,以便开始从并行性中获得收益。

您还可以减少线程数,因为这会减少开销:

tbb::task_scheduler_init init(nthread);

TBB还执行动态负载平衡(请参阅here)。如果您希望循环迭代/任务具有广泛的运行时间分布,那么这很好,但如果预期的运行时间相同,则表示不必要的开销。我不确定TBB是否有静态调度,但可能值得研究。

如果人们在没有对TBB的坚定承诺的情况下结束这里,在OpenMP中你会做类似的事情:

#pragma omp parallel for if(work_size>1000) num_threads(4) schedule(static)

答案 1 :(得分:2)

Ad 1)

这是一个很好的例子,细节很重要。 Tpar(ideal) = N * t / C 比实际可能发生的事情更为希望。

英特尔在重新制作发布软件工具的硬件专业知识方面做得非常出色,这可以从他们对自己的处理器微体系结构魔法的超详细知识中获益。对于英特尔CPU来说,没有其他人可以做得更好,没有其他人可以轻松移植它,在其他一些CPU微体系结构上提供任何类似的性能(所以要小心你的实际CPU,如果它是抽象云就越多)

为什么overhead-strict Amdahl's Law?

为什么呢?因为这些非常大的开销决定了核心数量以上。

关键是,随着“有用” - 负载大小变得越来越小,开销(即使是那些非常小的,如在超级优化的工具中,就像TBB一样无关紧要) - 这些开销总是累积到纯问题计算图的[SERIAL]部分。

因此,随着我们在[PARALLEL]有效载荷中继续变得越来越小,它们的主要非零成本{setup |终止}每核心调度实际上是成本的,在某个时刻会变得更高,而不是反比例因子1 / numCOREs的任何“下一个”好处,它只适用于净[PARALLEL]的线性持续时间 - 计算路径,但所有这些附加开销总结和扩展[SERIAL] - 计算路径的速度比任何增长numCOREs都可以补偿并且加速增长低于&lt;&lt; 1X 即可。
 的 Q.E.D。

Ad 2)

这个在上面的游乐场设置最小的痛苦游戏

如果有人希望加速约 ~ 4,000 CPU uops ~ <= 1 [us] ,那么一个就不能在所有延迟和附加开销上花费一个纳秒所以,假设最终加速仍然至少> = 1x

如果我们不相信童话故事,那么首先要看的是采用FPGA进行PoC原型设计和ASIC / SoC进行生产级部署。

如果项目的经济性无法处理所有相关费用,那就忘了免费获得任何魔法。它的成本。总是。但是,如果您的业务领域或研究基金可以应对,这是一个方向。

奖励:矢量化代码可能会崩溃某些CPU-s (最好避免这种情况)

在Quant建模中,性能就是金钱,所以让我也分享一个最近的已知问题,从微负载的极其严格的性能调整(在装配中弄脏手)。如果在定量建模工作中进行代码性能优化,希望它可以保存任何不需要的问题:

  

英特尔超线程损坏勘误表 (SKZ7 / SKW144 / SKL150 / SKX150 / SKZ7 / KBL095 / KBW095)   使用AH / BH / CH / DH寄存器的短循环可能导致不可预测的系统行为。

  的问题:
  在复杂的微架构条件下,使用AH,BH,CH或DH寄存器的少于64条指令的短循环以及相应的更宽寄存器(例如AH的RAX,EAX或AX)可能导致不可预测的系统行为。这只有在两者合乎逻辑时才会发生   同一物理处理器上的处理器处于活动状态。

含义:
由于此错误,系统可能会遇到不可预测的系统行为。此勘误表可能会影响客户操作系统。

  的参考文献:
  https://caml.inria.fr/mantis/view.php?id=7452   http://metadata.ftp-master.debian.org/changelogs/non-free/i/intel-microcode/unstable_changelog   https://www.intel.com/content/www/us/en/processors/core/desktop-6th-gen-core-family-spec-update.html   https://www.intel.com/content/www/us/en/processors/core/7th-gen-core-family-spec-update.html   https://www.intel.com/content/www/us/en/processors/xeon/xeon-e3-1200v6-spec-update.html   https://www.intel.com/content/www/us/en/processors/xeon/xeon-e3-1200v5-spec-update.html   https://www.intel.com/content/www/us/en/products/processors/core/6th-gen-x-series-spec-update.html

答案 2 :(得分:1)

@Richard有正确的答案(和TBB教程讨论调度开销摊销的概念),通常我会把它留作评论,但有一件事我想补充。

TBB对任务使用“贪婪的调度”。如果先前任务的执行创建了一个任务(技术上,如果任务返回一个任务指针),则该任务是在线程上运行的下一个任务。这有两个好处:

  1. 上一个任务可能已经加载或生成了下一个任务正在使用的数据,这有助于数据的本地化。
  2. 我们跳过选择要运行的下一个任务的过程(是否在本地线程的队列中是否可以从另一个线程窃取)。这大大减少了调度开销。
  3. tbb::flow_graph使用相同的想法;如果一个节点有一个或多个后继节点,则在执行完成时,将选择其中一个后继节点作为下一个节点。

    这样做的缺点是,如果你想改变这种行为(以“广度优先”而不是“深度优先”顺序执行节点),你必须跳过一些箍。它还会花费您调度开销和丢失局部性。