Performance 在Skylake上展开1周期循环会降低25%的性能。uops调度问题?
TL;DR我有一个循环,在Skylake上执行需要1个周期(它执行3次添加+1次inc/跳跃) 当我将它展开超过2次(无论多少次)时,我的程序运行速度大约慢25%。这可能与对齐有关,但我不清楚是什么 编辑:该问题用于询问为什么UOP由DSB而不是MITE交付。现在已将此移到Performance 在Skylake上展开1周期循环会降低25%的性能。uops调度问题?,performance,assembly,x86,intel,perf,Performance,Assembly,X86,Intel,Perf,TL;DR我有一个循环,在Skylake上执行需要1个周期(它执行3次添加+1次inc/跳跃) 当我将它展开超过2次(无论多少次)时,我的程序运行速度大约慢25%。这可能与对齐有关,但我不清楚是什么 编辑:该问题用于询问为什么UOP由DSB而不是MITE交付。现在已将此移到 我试图对一个循环进行基准测试,该循环在我的Skylake上进行了3次添加。这个循环应该在一个周期内执行,因为3加+1增量与条件跳转融合,一旦融合,可以在一个周期内执行。果然如此 然而,在某个时刻,我的C编译器试图展开该循环
我试图对一个循环进行基准测试,该循环在我的Skylake上进行了3次添加。这个循环应该在一个周期内执行,因为3加+1增量与条件跳转融合,一旦融合,可以在一个周期内执行。果然如此 然而,在某个时刻,我的C编译器试图展开该循环,从而导致性能下降。我现在试图理解为什么展开的循环的性能比未展开的循环差,因为我希望两者的性能相同,或者展开的循环的性能比未展开的循环差 这是我的C代码:
intmain(){
INTA、b、c、d;
#布拉格展开(2)
对于(无符号长i=0;i<2000000;i++){
asm volatile(“:“+r”(a),“+r”(b),“+r”(c),“+r”(d));
a=a+d;
b=b+d;
c=c+d;
}
//防止数据被优化
asm volatile(“:“+r”(a),“+r”(b),“+r”(c));
}
使用Clang 7.0.0-O3编译将生成以下(已清理的)程序集(从现在起称为v1
):
使用perf stat-e cycles
进行基准测试表明,每次迭代大约需要2个周期
但是,用“新的64位寄存器”(r8到r15)替换任何寄存器会导致循环以3个周期而不是2个周期执行(我们称此代码为v2
):
这不是一个随机的例子:如果我在我的程序中添加了一些东西,并且运气不好,那么Clang实际上会产生这个循环(我的初始版本是相同的C代码,另外随机初始化变量、预热阶段和rdtscp来计时循环,Clang在循环中使用了r14d
)。此循环以大约3个周期/迭代执行
进一步的测试表明,如果循环展开次数大于2,则程序执行周期为25亿次(非展开循环为20亿次)
循环中的UOP数为3*n+1
(其中n
为展开因子,1
表示融合的add/jne
),这意味着展开3次的循环有10个UOP;4乘以13个uop等。这些uop的数量相当小,应该放在DSB(uop缓存)中。我和你在天湖上
此外,展开3、4、10或50次根本不会改变性能:我的代码总是以25亿个周期运行(而未展开的代码则以20亿个周期运行)。这有点令人惊讶,因为3个加法应该总是在1个周期内执行,因此,如果由于某种原因在循环结束时损失了一个额外的周期,它的开销应该在展开增加时摊销,渐进(在展开因子中)性能应该接近20亿个周期
llvmmca
和iaca
都预测,展开n次将使循环以n个周期执行(这将使整个程序以20亿个周期执行)
总而言之,问题是:为什么我的循环一展开超过2次就慢了25%?评论不用于扩展讨论;这段对话已经结束。
movl $2000000000, %esi
.p2align 4, 0x90
.LBB0_1:
addl %edi, %edx
addl %edi, %ecx
addl %edi, %eax
addl %edi, %edx
addl %edi, %ecx
addl %edi, %eax
addq $-2, %rsi
jne .LBB0_1
movl $2000000000, %esi
.p2align 4, 0x90
.LBB0_1:
addl %edi, %r14d
addl %edi, %ecx
addl %edi, %eax
addl %edi, %r14d
addl %edi, %ecx
addl %edi, %eax
addq $-2, %rsi
jne .LBB0_1