当前位置: 首页 > 知识库问答 >
问题:

在预测现代超标量处理器上操作的延迟时,需要考虑哪些因素?如何手动计算它们?

何禄
2023-03-14

我希望能够手动预测任意算术(即没有分支或内存,尽管那也很好)x86-64汇编代码将在给定特定架构时花费多长时间,同时考虑到指令重新排序、超标、延迟、CPI等。

为实现这一目标,必须遵循哪些规则?

我想我已经找到了一些初步的规则,但我还没有找到任何关于将任何示例代码分解到这种详细程度的参考资料,所以我不得不进行一些猜测。(例如,《英特尔优化手册》几乎没有提到指令重新排序。)

至少,我需要(1)确认每个规则是正确的,或者每个规则的正确声明,以及(2)我可能忘记的任何规则的列表。

  • 每个周期发出尽可能多的指令,从当前周期开始按顺序排列,并可能与重新排序缓冲区大小一样远。
  • 在以下情况下,可以在给定周期发出指令:
    • 没有影响其操作数的指令仍在执行。并且:
    • 如果是浮点指令,则在它发出之前的每条浮点指令(浮点指令具有静态指令重新排序)。并且:
    • 在该周期上有一个可用于该指令的功能单元。每个(?)功能单元是流水线的,这意味着它每个周期可以接受1条新指令,并且总功能单元的数量为1/CPI,对于给定函数类的CPI(此处模糊不清:大概例如addpssubps使用相同的功能单元?我如何确定这一点?)。和:
    • 少于超标量宽度(通常4)的指令数量已经在这个周期发布。

    作为示例,请考虑以下示例代码(计算叉积):

    shufps   xmm3, xmm2, 210
    shufps   xmm0, xmm1, 201
    shufps   xmm2, xmm2, 201
    mulps    xmm0, xmm3
    shufps   xmm1, xmm1, 210
    mulps    xmm1, xmm2
    subps    xmm0, xmm1
    

    我尝试预测Haswell的延迟时间如下所示:

    ; `mulps`  Haswell latency=5, CPI=0.5
    ; `shufps` Haswell latency=1, CPI=1
    ; `subps`  Haswell latency=3, CPI=1
    
    shufps   xmm3, xmm2, 210   ; cycle  1
    shufps   xmm0, xmm1, 201   ; cycle  2
    shufps   xmm2, xmm2, 201   ; cycle  3
    mulps    xmm0, xmm3        ;   (superscalar execution)
    shufps   xmm1, xmm1, 210   ; cycle  4
    mulps    xmm1, xmm2        ; cycle  5
                               ; cycle  6 (stall `xmm0` and `xmm1`)
                               ; cycle  7 (stall `xmm1`)
                               ; cycle  8 (stall `xmm1`)
    subps    xmm0, xmm1        ; cycle  9
                               ; cycle 10 (stall `xmm0`)
    

共有1个答案

拓拔辰钊
2023-03-14

TL:DR:寻找依赖链,尤其是循环承载链。对于长时间运行的循环,请查看哪个延迟、前端吞吐量或后端端口争用/吞吐量是最严重的瓶颈。如果没有缓存未命中或分支预测失误,那么您的循环平均每次迭代可能需要多少个周期。

对于必须按顺序发生的操作,处理器的延迟界限和吞吐量界限是一个很好的例子,可以分析特定循环中带有两个dep链的循环承载依赖链,一个从另一个中提取值。

相关:每个汇编指令需要多少CPU周期?很好地介绍了每条指令的吞吐量与延迟,以及这对多个指令序列意味着什么。

这被称为静态(性能)分析。维基百科说(https://en.wikipedia.org/wiki/List_of_performance_analysis_tools)AMD的AMD CodeXL有一个“静态内核分析器”(即计算内核,又名循环)。我从未尝试过。

Intel还提供了一个免费工具,用于分析循环如何通过Sandybridge系列CPU中的管道:什么是IACA以及如何使用它?

IACA还不错,但有错误(例如Sandybridge上shld的错误数据,据我所知,它不知道Haswell/Skylake可以为某些指令保持索引寻址模式微融合。但现在英特尔在他们的优化手册中添加了有关这方面的详细信息,这种情况可能会改变。)IACA也无助于计算前端uop以查看您离瓶颈有多近(它喜欢只给你未融合的域uop计数)。

静态分析通常很好,但绝对可以通过使用性能计数器进行分析来检查。请参阅x86的MOV真的是“免费的”吗?为什么我根本不能重现它?例如分析一个简单的循环以研究微架构特性的示例。

Agner Fog的Microach指南(第2章:无序执行)解释了依赖链和无序执行的一些基础知识。他的“优化装配”指南有更多优秀的入门级和高级性能方面的内容。

他的microarch指南的后面几章涵盖了Nehalem、Sandybridge、Haswell、K8/K10、Bulldozer和Ryzen等CPU中管道的详细信息。(以及Atom/Silvermont/Jaguar)。

Agner Fog的指令表(电子表格或PDF)通常也是指令延迟/吞吐量/执行端口故障的最佳来源。

David Kanter的微阵列分析文档非常好,有图表。e、 g。https://www.realworldtech.com/sandy-bridge/,则,https://www.realworldtech.com/haswell-cpu/和https://www.realworldtech.com/bulldozer/.

另请参阅x86标签wiki中的其他性能链接。

在这个答案中,我还尝试解释了CPU内核如何发现并利用指令级并行性,但我认为您已经掌握了这些与软件调优相关的基础知识。不过,我确实提到了SMT(超线程)是如何将更多ILP暴露到单个CPU核心的。

在英特尔术语中:

>

  • “问题”意味着将uop发送到核心的乱序部分;与寄存器重命名一起,这是前端的最后一步。问题/重命名阶段通常是管道中最窄的一点,例如自Core2以来Intel上的4宽。(由于SKL改进的解码器和uop缓存带宽,以及后端和缓存带宽的改进,像Haswell这样的后期升级,尤其是Skylake,实际上在一些实际代码中经常非常接近这一点。)这是融合域uops:微融合允许您通过前端发送2个uops,并且只占用一个ROB条目。(我能够在Skylake上构建一个循环,每个时钟支持7个未融合的域uops)。另请参阅http://blog.stuffedcow.net/2013/05/measuring-rob-capacity/re:乱序窗口大小。

    “调度”是指调度程序将uop发送到执行端口。只要所有输入就绪,并且相关的执行端口可用,就会发生这种情况。x86 UOP是如何安排的?。调度发生在“未使用”域中;微融合UOP在OoO调度程序(又名预约站,RS)中单独跟踪。

    许多其他计算机体系结构文献使用这些术语的意义相反,但这是您可以在英特尔的优化手册中找到的术语,以及诸如uops\U等硬件性能计数器的名称。任何或uops\u dispatched\u端口。端口5。

    任意算术x86-64汇编代码需要多长时间

    在CPU开始运行以后的指令之前,不必准备好最终的子指令结果。延迟只对需要该值作为输入的后续指令重要,而不是整数循环等。

    有时吞吐量很重要,乱序执行可以隐藏多个独立短依赖链的延迟。(例如,如果您对多个向量的大数组的每个元素都做同样的事情,则多个交叉产品可以同时运行。)您最终会同时进行多次迭代,即使按照程序顺序,您在进行任何下一次迭代之前完成了所有一次迭代。(如果OoO exec很难在HW中完成所有重新排序,软件流水线可以帮助解决高延迟循环体。)

    您可以根据这三个因素来近似描述一小段非分支代码。通常只有其中一个是给定用例的瓶颈。通常,您看到的是一个将用作循环一部分的块,而不是整个循环体,但OoO exec通常工作得很好,您可以将两个不同块的数字相加,如果它们的长度不足以使OoO窗口大小阻止查找所有ILP。

    • 从每个输入到输出的延迟。查看从每个输入到每个输出的依赖链上有哪些指令。e、 g.一个选择可能需要一个输入才能更快地准备好
    • 英特尔CPU上融合域的uop总数(针对前端吞吐量瓶颈)。e、 g.理论上,Core2和更高版本可以将每个时钟的4个融合域UOP发布/重命名为无序调度器/ROB。Sandybridge系列通常可以通过uop缓存和循环缓冲区实现这一点,尤其是Skylake,它改进了解码器和uop缓存吞吐量
    • 每个后端执行端口(未使用的域)的uop计数。e、 重洗牌代码通常会在Intel CPU的端口5上出现瓶颈。Intel通常只发布吞吐量数字,而不发布端口细分,这就是为什么如果您不只是重复无数次相同的指令,那么您必须查看Agner Fog的表(或IACA输出)来做任何有意义的事情

    通常,您可以假设最佳情况下的调度/分发,可以在其他端口上运行的uops不会经常窃取繁忙的端口,但确实会发生一些情况。(x86 uops到底是如何调度的?)

    仅仅看CPI是不够的;两条CPI=1指令可能会也可能不会竞争同一执行端口。如果不这样做,它们可以并行执行。e、 g.Haswell只能在端口0上运行psadbw(5c延迟,1c吞吐量,即CPI=1),但它是一个uop,因此1条psadbw指令和3条add指令的混合可以支持每个时钟4条指令。Intel CPU的3个不同端口上有向量ALU,其中一些操作在所有3个端口上复制(例如布尔值),一些仅在一个端口上复制(例如Skylake之前的移位)。

    有时,您可以想出两种不同的策略,一种可能会降低延迟,但会花费更多的UOP。一个典型的例子是乘以常数,如imul eax、ecx、10(Intel上为1 uop,3c延迟)与lea eax、[rcx rcx*4]/(2 uop,2c延迟)。现代编译器倾向于选择2 LEA而不是1 IMUL,尽管3.7版本的clang支持IMUL,除非它可以用一条其他指令完成任务。

    请参阅计算位置或更低位置的集合位的有效方法是什么?有关实现函数的几种不同方法的静态分析示例。

    另请参见为什么mulss在Haswell上只需要3个周期,这与Agner的指令表不同?(使用多个累加器展开FP循环)(最终比您从问题标题中猜到的要详细得多),以获得另一个静态分析摘要,以及一些有关使用多个累加器展开以进行缩减的简洁内容。

    每(?)功能单元采用管道连接

    在最近的CPU中,分配器是管道化的,但不是完全管道化的。(不过,FP divide是单uop,因此如果您将一个divps与几十个mulps混合使用,那么如果延迟无关紧要,那么它对吞吐量的影响可以忽略不计:浮点除法与浮点乘法。RCPP牛顿迭代的吞吐量更差,延迟大致相同。

    其他一切都在主流Intel CPU上完全流水线化;单个uop的多周期(互惠)吞吐量。(可变计数整数移位,如shl eax、cl等,其3个UOP的吞吐量低于预期,因为它们通过合并UOP的标志创建依赖关系。但如果通过添加标志或其他方式打破这种依赖关系,则可以获得更好的吞吐量和延迟。)

    在Ryzen之前的AMD上,整数乘法器也只是部分流水线化。例如推土机的imul ecx, edx只有1 uop,但具有4c延迟,2c吞吐量。

    Xeon Phi(KNL)也有一些未完全流水线化的洗牌指令,但它往往在前端(指令解码)而不是后端出现瓶颈,并且具有隐藏后端气泡的小缓冲区OoO exec功能。

    如果是浮点指令,则在发出之前的每条浮点指令(浮点指令具有静态指令重新排序)

    不。

    也许您会读到Silvermont的这篇文章,它不为FP/SIMD执行OoO exec,只为整数(有一个约20 uop的小窗口)。也许有些ARM芯片也是这样,为NEON提供了更简单的调度程序?我不太了解ARM uarch的细节。

    主流的大核微架构(如P6/SnB系列)和所有AMD OoO芯片,对SIMD和FP指令执行与整数相同的OoO执行。AMD CPU使用单独的调度程序,但Intel使用统一的调度程序,因此其完整大小可以应用于在整数或FP代码中查找ILP,以当前运行的为准。

    即使是总部位于锡尔佛蒙特州的骑士登陆(位于至强皮岛)也为SIMD做了OoO高管。

    x86通常对指令排序不太敏感,但uop调度不进行关键路径分析。因此,有时将指令首先放在关键路径上会有所帮助,这样它们就不会在其他指令在该端口上运行时等待输入准备就绪,从而导致稍后当我们到达需要关键路径结果的指令时出现更大的停顿。(这就是为什么它是关键路径。)

    我尝试预测Haswell的延迟时间如下所示:

    是的,看起来没错。shufps在端口5上运行,addps在p1上运行,mulps在p0或p1上运行。Skylake丢弃专用的FP-add单元并在p0/p1上的FMA单元上运行SIMD FP add/mul/FMA,所有这些都具有4c延迟(从Haswell的3/5/5或Broadwell的3/3/5向上/向下)。

    这是一个很好的例子,说明为什么在SIMD向量中保留整个XYZ方向向量通常很糟糕。保留一个X数组、一个Y数组和一个Z数组,可以让您并行执行4个叉积而不会进行任何洗牌。

    SSE标签wiki有一个指向这些幻灯片的链接:失眠症游戏中的SIMD(GDC 2015),其中涵盖了3D向量的结构数组与数组结构问题,以及为什么总是尝试SIMD单个操作而不是使用SIMD并行执行多个操作通常是错误的。

  •  类似资料:
    • 1. 尽可能使用批量操作: mget、hmget而不是get和hget,对于set也是如此。 lpush向一个list一次性导入多个元素,而不用lset一个个添加 LRANGE 一次取出一个范围的元素,也不用LINDEX一个个取出 2. 尽可能的把redis和APP SERVER部署在一个网段甚至一台机器。 3. 对于数据量较大的集合,不要轻易进行删除操作,这样会阻塞服务器,一般采用重命名+批量删

    • 问题内容: 我有一个,我想计算文档需要呈现的边界框的大小。 那可能吗? 纯文本(,)几乎是微不足道的,但是我该如何使用RTF,HTML或任何其他文档来实现呢? 问题答案: 该代码可能会给您一些想法:

    • 本文向大家介绍风险资本家在投资之前会考虑哪些因素?,包括了风险资本家在投资之前会考虑哪些因素?的使用技巧和注意事项,需要的朋友参考一下 风险资本家在投资之前考虑的因素如下- 管理团队-投资者寻找具有技能,知识和成就记录的管理团队。对目标的承诺是关键。 项目的可行性–资本公司在投资项目之前将关注产品市场,最终用户,竞争对手和行业增长。 商业计划 成本和收益-还详细研究了项目成本,融资计划,资金来源。

    • 我想将来自AWS Kinesis流的消息的处理延迟一个小时。我已将KCL消费者配置为每四分钟读取一批记录,检查每条记录的时间戳,如果任何记录不到一个小时,则停止处理该批次,无需检查点。我希望同一个消费者实例每四分钟重读一次相同的消息,直到整个批次足够旧可以处理,然后检查点消费者。但是,在实践中,消费者只读取一次消息,这意味着它们被忽略,并且在准备好处理时永远不会再次读取。有没有办法将消费者配置为每

    • 本文向大家介绍哪些机器学习算法不需要做归一化处理?相关面试题,主要包含被问及哪些机器学习算法不需要做归一化处理?时的应答技巧和注意事项,需要的朋友参考一下 概率模型不需要归一化,因为他们不关心变量的值,而是关心变量的分布和变量之间的条件概率,如决策树、RF。而像Adaboost、GBDT、SVM、LR、KNN、KMeans之类的最优化问题就需要归一化

    • 问题内容: 这是我的情况: 我有一个包含用户列表的页面。我通过Web界面创建一个新用户,并将其保存到服务器。服务器在elasticsearch中为文档建立索引并成功返回。然后,我被重定向到不包含新用户的列表页面,因为它可能需要1秒钟的时间才能使文档在Elasticsearch中可供搜索 elasticsearch中的近实时搜索。 elasticsearch指南说您可以手动刷新索引,但说在生产中不要