以下是我尝试对英特尔TBB流程图的性能进行基准测试。 这是设置:
continue_msg
后继节点发送N
(a broadcast_node<continue_msg>
)t
秒。Tserial = N * t
Tpar(ideal) = N * t / C
,其中 C
是核心数。Tpar(actual) / Tserial
以下结果显示加速作为单个任务(即身体)处理时间的函数:
t = 100 microsecond , speed-up = 14
t = 10 microsecond , speed-up = 7
t = 1 microsecond , speed-up = 1
对于轻量级任务(其计算时间小于1微秒),并行代码实际上比串行代码慢。
1 )这些结果是否符合英特尔TBB基准测试标准? 2 )当有数千个任务每个花费不到1微秒时,有一个更好的范例,而不是案例的流程图?
答案 0 :(得分:3)
并行的开销
您的费用模式错误。
理想的并行计算时间是:
Tpar(ideal) = N * t / C + Pstart + Pend
其中Pstart
是启动并行性所需的时间,Pend
是完成它所需的时间。 Pstart
大约数十毫秒并不罕见。
我不确定您是否熟悉OpenMP(虽然知道这是件好事)但是,出于我们的目的,它是一个在团队之间划分工作的线程模型线程。下图显示了与线程组大小相关的一些命令的开销:
需要注意的是,让你的并行性(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)
这是一个很好的例子,细节很重要。 Tpar(ideal) = N * t / C
比实际可能发生的事情更为希望。
英特尔在重新制作发布软件工具的硬件专业知识方面做得非常出色,这可以从他们对自己的处理器微体系结构魔法的超详细知识中获益。对于英特尔CPU来说,没有其他人可以做得更好,没有其他人可以轻松移植它,在其他一些CPU微体系结构上提供任何类似的性能(所以要小心你的实际CPU,如果它是抽象云就越多)
为什么呢?因为这些非常大的开销决定了核心数量以上。
关键是,随着“有用” - 负载大小变得越来越小,开销(即使是那些非常小的,如在超级优化的工具中,就像TBB一样无关紧要) - 这些开销总是累积到纯问题计算图的[SERIAL]
部分。
因此,随着我们在[PARALLEL]
有效载荷中继续变得越来越小,它们的主要非零成本{setup |终止}每核心调度实际上是成本的,在某个时刻会变得更高,而不是反比例因子1 / numCOREs
的任何“下一个”好处,它只适用于净[PARALLEL]
的线性持续时间 - 计算路径,但所有这些附加开销总结和扩展[SERIAL]
- 计算路径的速度比任何增长numCOREs
都可以补偿并且加速增长低于&lt;&lt; 1X 即可。
的 Q.E.D。强>
这个在上面的游乐场设置最小的痛苦游戏。
如果有人希望加速约 ~ 4,000 CPU uops ~ <= 1 [us]
,那么一个就不能在所有延迟和附加开销上花费一个纳秒所以,假设最终加速仍然至少> = 1x
如果我们不相信童话故事,那么首先要看的是采用FPGA进行PoC原型设计和ASIC / SoC进行生产级部署。
如果项目的经济性无法处理所有相关费用,那就忘了免费获得任何魔法。它的成本。总是。但是,如果您的业务领域或研究基金可以应对,这是一个方向。
在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)
TBB对任务使用“贪婪的调度”。如果先前任务的执行创建了一个任务(技术上,如果任务返回一个任务指针),则该任务是在线程上运行的下一个任务。这有两个好处:
tbb::flow_graph
使用相同的想法;如果一个节点有一个或多个后继节点,则在执行完成时,将选择其中一个后继节点作为下一个节点。
这样做的缺点是,如果你想改变这种行为(以“广度优先”而不是“深度优先”顺序执行节点),你必须跳过一些箍。它还会花费您调度开销和丢失局部性。