x86中简单循环中的慢指令
我用C ++编写了一个简单的循环,因为我想分析CPU上乘法指令的性能。 我在分析它时生成的汇编代码中发现了一些有趣的细微差别。
这是C ++程序:
#define TESTS 10000000
#define BUFSIZE 1000
uint32_t buf_in1[BUFSIZE];
uint32_t buf_in2[BUFSIZE];
uint32_t volatile buf_out[BUFSIZE];
unsigned int i, j;
for (i = 0; i < BUFSIZE; i++) {
buf_in1[i] = i;
buf_in2[i] = i;
}
for (j = 0; j < TESTS; j++) {
for (i = 0; i < BUFSIZE; i++) {
buf_out[i] = buf_in1[i] * buf_in2[i];
}
}
我使用以下标志进行编译:
优化:
代码生成:
它在Win32下的visual studio 2012中编译,虽然我在64位机器上运行它。
请注意buf_out上的volatile限定符。 这只是为了阻止编译器优化循环。
我通过一个profiler(AMD的CodeXL)运行这段代码,我发现乘法指令不占用大部分CPU时间。 大约30%被imul指令占用,但大约60%也用于其他两条指令:
请注意,Timer列显示探查器在此指令中找到代码的计时器滴答数。 计时器滴答是1ms,所以2609滴答是在该指令上花费的大约2609ms。
除了乘法指令占用大量时间之外的两条指令是mov指令和jb(满足条件时跳转)指令。
mov指令,
mov [esp+eax+00001f40h],ecx
将乘法(ecx)的结果移回到eax(这是表示i的寄存器)的缓冲区buf_out缓冲区中。 这是有道理的,但为什么它比其他mov指令需要更长的时间呢? 即这一个:
mov ecx,[esp+eax+00000fa0h]
它们都从内存中的类似位置读取,数组的长度为1000 uint32_t,长度为4000个字节。 那是4000 * 3 = 12kB。 我的L1缓存是64kB,所以它应该很容易适合L1,据我所见...
以下是来自Coreinfo的显示我的缓存大小等的结果:
至于跳转指令:
jb $-1ah (0x903732)
我无法分辨为什么它占用了程序执行时间的33%。 我的处理器线路大小为64字节,跳转只向后跳转0x1A字节或26字节。 难道是因为这个跳转跨越了64字节的边界吗? (0x903740是一个64字节的边界)
那么任何人都能解释这些行为吗
谢谢。
正如神秘主义提到的那样,你所看到的时机并不是一一对应的指示的责任。
现代处理器并行运行许多指令( imul
和add
4到eax都可以并行运行,而mov
寻址中涉及的数学运算也使用ALU,并且可以在imul
完成之前计算)。
大多数分析器计算它们的时序的方式是使用定时中断,而您看到的是那些恰好是在中断时执行的指令。
要正确使用分析器,您希望运行大型程序并查看该程序是否花费了大量时间。 在每个指令的基础上,它没有太多的价值。
如果你真的想进行速度测试,你希望在你的循环之前和之后使用CPU计时器,并且看你如何以某种方式改善它以使其运行得更快。
我不会认为这一切都适合你的L1,因为你正在调试的代码不是唯一使用CPU的东西(除非你启动了你的机器来运行该代码,实际上它将是你的操作系统)。
还要注意那里有一种模式:最慢的操作都需要主存储器访问。 由于这个访问时间不是由CPU控制的,因此很难指出它为什么不是更快。 这将需要硬件分析。
希望这可以帮助。
不幸的是,你没有给出一次循环所需的时间,但我假设它有三个CPU周期。 如果这是真的,那么碰巧得到时间的三条指令就是处理器在时钟滴答时正式运行的三条指令。 其他三条指令与三条正式耗时的指令并行执行,隐藏在它们后面。
链接地址: http://www.djcxy.com/p/28781.html上一篇: Slow Instructions in Simple Loop on x86
下一篇: Loop unrolling to achieve maximum throughput with Ivy Bridge and Haswell