代码之家  ›  专栏  ›  技术社区  ›  motam79

英特尔TBB流程图开销

  •  3
  • motam79  · 技术社区  · 6 年前

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

    • 一个广播节点发送 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 回复  |  直到 5 年前
        1
  •  4
  •   Richard    6 年前

    并行的开销

    您的成本模型错误。

    理想的并行计算时间为:

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

    哪里 Pstart 开始并行化所需的时间 Pend 是完成它所花费的时间。这对于 初始压力 大约是几十毫秒。

    我不确定您是否熟悉OpenMP(尽管知道这是件好事),但就我们的目的而言,它是一种线程模型,它将工作划分为多个线程组。下图显示了与线程团队大小相关的一些命令的开销:

    OpenMP thread overheads

    要点是让你的平行性( parallel for line)可能很昂贵,并且会随着线程数的增加而增加。结束平行度(the barrier 线路)具有类似的成本。

    事实上,如果你看一下TBB的教程,第3.2.2节(“自动分块”)说:

    警告:通常情况下,一个循环需要至少100万个时钟周期才能进行parallel\u,以提高其性能。例如,在2 GHz处理器上至少需要500微秒的循环可能会受益于parallel\u 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)
    
        2
  •  2
  •   user3666197    6 年前

    广告1)

    这是一个很好的例子,细节很重要。 Tpar(ideal) = N * t / C 与其说是现实中可能发生的事情,不如说是一种愿望。

    英特尔在将其硬件技术重新构建为发布软件工具方面确实做得很好,这可以得益于他们对自己处理器微体系结构魔术的超详细知识。没有人能够更好地为Intel CPU-s提供支持,也没有人能够轻松地对其进行移植,以便在其他CPU微体系结构上提供任何类似的性能(因此,请小心您的实际CPU,如果它是云抽象的,就越重要)

    为什么? overhead-strict Amdahl's Law?

    为什么?因为这些开销决定的不仅仅是核的数量。

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

    因此,随着我们在 [PARALLEL] 有效载荷,即每个核心调度实际产生的{设置|终止}的主要非零成本,在某个时刻会变得比任何“下一个”受益于成反比的因素更高 1 / numCOREs 仅适用于净额的线性持续时间 [平行] -计算路径,但所有这些附加开销总结并扩展了 [序列号] -计算路径比任何增长的 numCOREs 可以补偿和 速度增长低于<&书信电报;1台 .
    Q、 E.D。


    广告2)

    这个是 在上面的操场设置中, 最小痛苦游戏 .

    如果有人想加速 ~ 4,000 CPU uops ~ <= 1 [us] 绝对不能花费一纳秒 在所有延迟和附加开销上,如果尝试这样做, 假设最终加速至少保持>=1台

    如果我们不相信童话故事,那么我们可以考虑使用FPGA进行PoC原型设计,使用ASIC/SoC进行生产级部署。

    如果项目的经济性无法处理所有相关成本,请忘记免费获得任何魔法。它的成本。总是但如果你的商业领域或研究基金能够应付,这是一个方向。


    另一个好处是:矢量化代码可能会在某些CPU上崩溃 (最好避免这种情况) :

    在定量建模中,性能就是金钱,所以让我也分享一个最近已知的问题,即微有效载荷的极为严格的性能调整(组装时双手脏)。如果在量化建模工作中进行代码性能优化,希望它可以避免任何不必要的问题:

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

    问题:
    在复杂的微体系结构条件下,使用AH、BH、CH或DH寄存器及其相应的较宽寄存器(例如,AH的RAX、EAX或AX)的少于64条指令的短循环 也许 原因 不可预测的系统行为 . 只有在逻辑和 同一物理处理器上的处理器处于活动状态。

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

    参考文献:
    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

        3
  •  2
  •   cahuson    6 年前

    @Richard给出了正确的答案(TBB教程讨论了计划开销摊销的概念),通常我会将此作为评论,但我想补充一点。

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

    1. 上一个任务可能已经加载或生成了下一个任务正在使用的数据,这有助于数据位置的确定。
    2. 我们跳过选择下一个要运行的任务的过程(无论它是否在本地线程的队列中,是否可以从另一个线程中窃取)。这大大减少了调度开销。

    tbb::flow_graph 使用相同的想法;如果节点有一个或多个后续节点,则在其执行完成时,将选择其中一个后续节点作为下一个要运行的节点。

    这样做的缺点是,如果您想改变这种行为(以“广度优先”而不是“深度优先”的顺序执行节点),您必须跳过一些障碍。它还将使您在调度开销和位置丢失方面付出代价。