在另一个答案[Deoptimizing a program for the pipeline in Intel Sandybridge-family CPUs]上声明了这一点:
每次迭代的时间独立,甚至比RDTSC更重。例如CPUID / RDTSC或进行系统调用的时间函数。序列化指令本质上是管道不友好的。
我认为应该是相反的。序列化指令非常适合管道。例如,
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
按g++ main.cpp -S
汇总
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
管道更好,而不是:
for( int i = 0; i < 7; i++ )
{
sum = 5 * sum;
}
sum = sum + 5;
按g++ main.cpp -S
汇总
movl $0, -4(%rbp)
movl $0, -8(%rbp)
.L3:
cmpl $6, -8(%rbp)
jg .L2
movl -4(%rbp), %edx
movl %edx, %eax
sall $2, %eax
addl %edx, %eax
movl %eax, -4(%rbp)
addl $1, -8(%rbp)
jmp .L3
.L2:
addl $5, -4(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
因为每次循环都是:
if( i < 7 )
sum = sum + 5
将被丢弃。sum = 5 * sum
,if( i < 7 )
失败,sum = 5 * sum
将被丢弃sum = sum + 5
。答案 0 :(得分:4)
您将“序列化”与“序列化”混淆了。序列化指令是保证数据排序的指令,即此指令之前的所有内容都发生在此指令之后的所有内容之前。
这对于超标量和流水线处理器来说是个坏消息,这些处理器通常不能做出这种保证,并且必须对它进行特殊的指导,例如:通过刷新管道或等待所有执行单元完成。
顺便说一下,这有点像你想要的基准测试,因为它迫使管道进入可预测状态,所有执行单元都准备好执行你的代码;在基准测试之前没有过时的写入会导致任何性能偏差。
答案 1 :(得分:0)
我认为他将序列化视为依赖。
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
会比并行版慢:
sum1 = 5 * sum1;
sum2 = 5 * sum2;
sum1 = 5 * sum1;
sum2 = 5 * sum2;
sum1 = 5 * sum1;
sum2 = 5 * sum2;
sum1 = 5 * sum1;
sum = sum2*sum1;
因为有多个管道,每个管道可以在飞行中的多个指令上工作,所以可能有sum1 sum2 ... sum8同时发出多个累加器。
如果串行器指令足够长,它会在N个周期后使管道准备好进行测量,因为新指令无法在不完成最后一个指令的情况下启动(对于串行器指令)。