繁体   English   中英

什么可能导致相同的SSE代码在同一个函数中运行速度慢几倍?

[英]What might cause the same SSE code to run a few times slower in the same function?

编辑3:图像是指向完整版本的链接。 对于文本图片感到抱歉,但图表很难复制/粘贴到文本表中。


对于使用icc --std=c++14 -qopenmp -axS -O3 -fPIC编译的程序,我有以下VTune配置文件icc --std=c++14 -qopenmp -axS -O3 -fPIC

VTune简介

在该配置文件中,组装视图中突出显示了两组指令。 尽管指令相同且顺序相同,但上部集群比下部集群花费的时间少得多。 两个集群都位于同一个函数内,显然都被称为n次。 每次我运行探查器时都会发生这种情况,我现在正在使用Westmere Xeon和Haswell笔记本电脑(使用SSE编译,因为这就是我现在正在瞄准和学习的东西)。

我错过了什么?

忽略糟糕的并发性,这很可能是由于笔记本电脑节流,因为它不会发生在桌面Xeon机器上。

我认为这不是微优化的一个例子,因为这三个加在一起相当于总时间的百分之一,我真的对这种行为的可能原因感兴趣。

编辑: OMP_NUM_THREADS=1 taskset -c 1 /opt/intel/vtune...

VTune简介

相同的资料,虽然此次CPI略低。

那么,分析汇编代码请注意运行时间归因于下一条指令 - 因此,需要仔细解释您通过指令查找的数据。 VTune发行说明”中有相应的说明

运行时间归因于下一条指令(200108041)

为了收集有关目标耗时的运行区域的数据,英特尔®VTune™放大器会中断执行目标线程并将时间归因于上下文IP地址。

由于收集机制,捕获的IP地址指向大部分时间实际消耗的指令之后的指令。 这导致运行时间归因于Assembly视图中的下一条指令(或很少与后续指令之一)。 在极少数情况下,这也可能导致源中运行时间的错误归属 - 时间可能错误地归因于实际热线之后的源线。

如果内联模式为ON且程序在热点处内联函数较小,则可能导致运行时间归因于错误的函数,因为下一条指令可能属于紧密内联代码中的不同函数。

HW perf计数器通常将停顿充电到必须等待其输入的指令,而不是缓慢产生输出的指令。

第一组的输入来自您的聚会。 这可能是缓存 - 错过了很多,并且成本不会被这些SUBPS / MULPS / ADDPS指令收费。 它们的输入直接来自voxel[]向量加载,因此存储转发失败将导致一些延迟。 但这只是~10个周期的IIRC,与聚集期间的缓存未命中相比较小。 (那些缓存未命中您突出显示的第一个组之前显示为指令的大条)

第二组的输入直接来自可能在高速缓存中丢失的负载。 在第一组中,缓存未命中加载的直接使用者是诸如设置voxel[0]行的指令,其具有非常大的条。

但在第二组中, a_transfer[]缓存未命中的时间将归因于您突出显示的组。 或者,如果它不是缓存未命中,那么可能是慢速地址计算,因为负载必须等待RAX准备好。


它看起来像有很多 ,你可以在这里最优化

  • 而不是为a_pointf存储/重新加载,只需在__m128变量中的循环迭代中保持热。 如果您发现编译器在哪个向量寄存器溢出(如果它用完寄存器)方面做得不好,那么在C源中存储/重新加载才有意义。

  • 使用_mm_cvttps_epi32(vf)计算vi ,因此ROUNDPS不是聚集索引的依赖关系链的一部分。

  • voxel通过将窄负载拖入向量来收集自己,而不是编写复制到数组然后从中加载的代码。 (保证存储转发失败,请参阅Agner Fog的优化指南标签wiki中的其他链接)。

    部分矢量化地址数学(计算base_0 ,使用带有常量向量的PMULDQ )可能是值得的,所以不是存储/重载(~5个周期延迟),而是只有一个MOVQ或两个(~1或2个周期) Haswell的延迟,我忘了。)

    使用MOVD加载两个相邻的short值,并使用PINSRD将另一对合并到第二个元素中。 您可能会从_mm_setr_epi32(*(const int*)base_0, *(const int*)(base_0 + dim_x), 0, 0)获得良好的代码,除了指针别名是未定义的行为。 您可能会从_mm_setr_epi16(*base_0, *(base_0 + 1), *(base_0 + dim_x), *(base_0 + dim_x + 1), 0,0,0,0)获得更差的代码。

    然后使用PMOVSX将低四个16位元素扩展为32位元素整数,并将它们全部转换为与_mm_cvtepi32_ps (CVTDQ2PS)并行float

  • 你的标量LERP不是自动矢量化的,但你并行做两个(并且可能保存一条指令,因为你想要结果在矢量中)。

  • 调用floorf()是愚蠢的,函数调用强制编译器将所有xmm寄存器溢出到内存中。 使用-ffast-math或其他内容编译以使其内联到ROUNDSS,或手动执行。 特别是因为你继续将你计算的浮点数加载到向量中!

  • 使用向量比较而不是标量prev_x / prev_y / prev_z。 使用MOVMASKPS将结果转换为可以测试的整数。 (您只关心较低的3个元素,因此请使用compare_mask & 0b0111测试(如果设置了4位掩码中的任何低3位,则在与_mm_cmpneq_ps不相等的比较后为_mm_cmpneq_ps 。请参阅double版本有关如何运作的更多表格的说明: http//www.felixcloutier.com/x86/CMPPD.html )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM