x86上简单循环中的慢速指令

时间:2014-05-04 03:23:13

标签: c++ performance assembly

我有一个简单的循环,我用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];
    }
}

我用以下标志编译:

优化: Optimization

代码生成:

Code Generation

虽然我在64位计算机上运行它,但它是在Win32下的visual studio 2012中编译的。

注意buf_out上的volatile限定符。这只是阻止编译器优化循环。

我通过分析器(AMD's CodeXL)运行此代码,我发现乘法指令不会占用大部分CPU时间。 imul指令占用了大约30%,但是其他两个指令也花费了大约60%:

Profiler

请注意,Timer列显示了探测器在此指令中找到代码的计时器滴答数。计时器滴答是1ms,因此在该指令上花费2609个滴答约为2609ms。

乘法指令以外的两个指令占用了很多时间,它们是一个mov指令和一个jb(满足条件时跳转)指令。

mov指令,

mov [esp+eax+00001f40h],ecx

将乘法(ecx)的结果移回到eax的缓冲区buf_out缓冲区(这是表示i的寄存器)。这是有道理的,但为什么这比其他mov指令要花这么长时间?就是这个:

mov ecx,[esp+eax+00000fa0h]

它们都从内存中的类似位置读取,数组的长度为1000 uint32_t或长度为4000字节。那是4000 * 3 = 12kB。我的L1缓存是64kB所以它应该很容易适合L1,就我所见......

以下是显示我Coreinfo的缓存大小等的结果:

Coreinfo

关于跳转指令:

jb $-1ah (0x903732)

我不知道为什么它占用程序执行时间的33%。我的处理器行大小为64字节,跳转仅向后跳跃0x1A字节或26字节。可能是因为这个跳过了一个64字节的边界? (0x903740是64字节边界)

那么有人可以解释这些行为吗?

感谢。

3 个答案:

答案 0 :(得分:2)

正如神秘学所提到的,你所看到的时间并不是一对一的责任,而是显示出来的指示。

现代处理器并行运行许多指令(imuladd 4到eax都可以并行运行,mov寻址中涉及的数学运算也使用ALU可以在imul完成之前计算。

大多数分析器计算其时序的方式是使用定时中断,你看到的是恰好是在中断时执行的指令。

正确使用分析器,您希望针对大型程序运行,并查看该程序是否花费了大量时间。在每个指令的基础上,它没有太大的价值。

如果你真的想进行速度测试,你想在循环之前和之后使用CPU计时器,看看如何以某种方式改善它以使其更快地运行。

答案 1 :(得分:1)

我不会认为这一切都适合你的L1,因为你正在调试的代码不是唯一使用CPU的代码(除非你启动你的机器来运行那些代码,实际上它将是你的操作系统。)

还要注意那里有一种模式:最慢的操作都需要主存储器访问。由于此访问时间不受CPU控制,因此很难指出为什么它不会更快。这将需要硬件分析。

希望这有帮助。

答案 2 :(得分:0)

不幸的是,你没有给出一次通过循环所需的时间,但我认为它是三个CPU周期。如果这是真的,那么恰好得到时间的三条指令就是当时钟滴答时处理器正式开启的三条指令。其他三条指令与三条正式耗时的指令并行执行,隐藏在它们后面。