微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Xcode Instrument的拆卸时间分析的可靠性

我使用Instrument的时间分析器来描述我的代码,并放大到反汇编,这里是结果的片段:

我不希望mov指令占用23.3%的时间而div命令几乎不采取任何操作.
这使我相信这些结果是不可靠的.
这是真的吗?或者我只是遇到了一个仪器错误?或者我是否需要使用一些选项来获得可靠的结果?

在这个问题上是否有任何参考?

解决方法

首先,有可能一些真正属于divss的计数被收取到后来的指令 which is called a “skid”.(另请参阅其余的注释线程以获取更多细节.)大概Xcode就像Linux perf,并使用固定的cpu_clk_unhalted .thread计数器用于循环而不是其中一个可编程计数器.这不是“精确”事件(PEBS),因此可以进行滑行. As @BeeOnRope points out,你可以使用一个PEBS事件,每个周期滴答一次(如UOPS_RETIRED< 16)作为固定周期计数器的PEBS替代,消除了对中断行为的一些依赖. 但是计数器从根本上适用于流水线/无序执行的方式也解释了你所看到的大部分内容.或者它可能;您没有显示完整的循环,因此我们无法像IACA那样在简单的管道模型上模拟代码,或者手动使用 http://agner.org/optimize/和英特尔优化手册等硬件指南. (而且你甚至没有指定你所拥有的微架构.我猜它是Mac上英特尔Sandybridge系列的一部分).

循环计数通常按等待结果的指令计费,通常不是产生结果的指令.在尝试读取尚未准备好的结果之前,流水线cpu不会停止.

乱序执行会使这种情况大为复杂,但是当有一个非常慢的指令时,它仍然通常是正确的,就像在缓存中经常错过的负载一样.当循环计数器溢出(触发中断)时,有许多指令在运行中,但只有一个可以是与该性能计数器事件相关联的RIP.它也是在中断后执行将恢复的RIP.

那么当引发中断时会发生什么?请参阅Andy Glew’s answer,其中解释了英特尔P6微体系结构管道中的计数器中断的内部结构,以及为什么(在PEBS之前)它们总是被延迟. Sandybridge家族与P6类似.

我认为英特尔cpu上的计数器中断的合理心理模型是它丢弃尚未分派给执行单元的任何uop.但是已经发送的ALU uops已经通过管道退出(如果没有任何更年轻的uops被丢弃)而不是被中止,这是有道理的,因为sqrtpd的最大额外延迟是~16个周期,并且冲洗存储队列很容易花费更长的时间. (已退休的待处理商店无法回滚).关于尚未退休的货物/商店的IDK;至少负载可能被丢弃.

我基于这样的猜测:当cpu有时等待它产生输出时,很容易构造不显示divss计数的循环.如果它在没有退役的情况下被丢弃,它将是恢复中断时的下一条指令,因此(除了滑行)你会看到很多计数.

因此,循环计数的分布显示哪些指令花费最多的时间是调度程序中最旧的尚未调度的指令. (或者在前端停顿的情况下,cpu停止尝试获取/解码/发出的指令).请记住,这通常意味着它会向您显示等待输入的指令,而不是生成它们的指令.

(嗯,这可能不对,我没有测试过这么多.我通常使用perf stat来查看微基准测试中整个循环的整体计数,而不是带有perf记录的统计模型.addss和mulss的延迟时间比andps,所以如果我提出的模型是正确的,你会期望andps获得等待xmm5输入的计数.)

无论如何,一般的问题是,在一次飞行中有多个指令,当周期计数器环绕时,哪一个硬件“责备”?

请注意,divss生成结果的速度很慢,但只是单指令(与AMD和Intel上的微编码整数div不同).如果您没有对其延迟或其未完全流水线的吞吐量进行瓶颈,it’s not slower than mulss因为它也可以与周围的代码重叠.

(divss / divps没有完全流水线化.例如,在Haswell上,一个独立的divps可以每7个周期启动一次.但是每个只需要10-13个周期来产生它的结果.所有其他执行单元都是完全流水线的;能够启动一个新的操作关于每个周期的独立数据.)

考虑一个吞吐量瓶颈的大循环,而不是任何循环携带依赖的延迟,并且每20个FP指令只需要divss运行一次.使用divss乘以常数而不是mulss与倒数常数应该(几乎)没有性能差异. (实际上,无序调度并不完美,并且即使没有循环传输,更长的依赖链也会损害一些,因为它们需要更多指令才能隐藏所有延迟并维持最大吞吐量.即-of-order core来查找指令级并行性.)

无论如何,这里的重点是divss是一个单独的uop,它有意义的是它不会得到很多循环事件的计数,这取决于周围的代码.

您可以看到缓存未命中加载的相同效果:如果必须在寻址模式下等待寄存器,则加载本身通常只会获得计数,并且使用加载数据的依赖关系链中的第一条指令会获得大量计数.

您的个人资料结果可能告诉我们:

> divss不必等待其输入准备就绪. (在divss之前的movaps%xmm3,%xmm5有时需要一些周期,但是divss永远不会.)
>我们可能接近于对divss吞吐量的瓶颈
>在divss之后涉及xmm5的依赖链正在得到一些计数.乱序执行必须能够立即保持多次独立迭代.
> maxss / movaps循环携带的依赖链可能是一个重要的瓶颈. (特别是如果你在Skylake上,每3个时钟的吞吐量是一个,但是maxss延迟是4个周期.来自端口0和1的竞争的资源冲突将延迟maxss.)

movaps的高计数可能是由于它遵循maxss,在你显示的循环部分形成唯一的循环携带依赖.因此,maxss确实很慢产生结果是合理的.但如果它确实是一个循环携带的dep链,这是主要的瓶颈,你会期望看到很多关于maxss本身的计数,因为它将等待上一次迭代的输入.

但也许mov-elimination是“特殊的”,并且由于某些原因所有的计数都被收取到movaps?在Ivybridge和更高版本的cpu上,register copies doesn’t need an execution unit,but instead are handled in the issue/rename stage of the pipeline.

原文地址:https://www.jb51.cc/iOS/333995.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐