4

首先,请原谅我,因为我的问题可能看起来很愚蠢,但我真的很好奇为什么我在这个非常简单的代码中获得了性能提升。

这是汇编代码:

__asm {
    mov eax, 0
    mov ecx, 0
    jmp startloop
    notequal:
    inc eax
    mov ecx, eax
    sub ecx, 2
    startloop:
    cmp eax, 2000000000
    jne notequal
};

这是C代码:

long x = 0;
long ii = 0;
for(; ii < 2000000000; ++ii)
{
    x = ii - 2;
};

在我的 i5 2500k 机器上完成 C 代码大约需要 1060 毫秒(在发布版本中),并且组装在 780 毫秒内完成。速度提高了约 25%。我不明白为什么我会得到这个结果,因为 25% 是一个很大的差异。编译器是否不够聪明,无法生成我编写的相同的汇编代码?

顺便说一句,我正在使用 MSVC 2010。

谢谢


这是由 MSVC 生成的 (asm) 代码

$LL3@main:
; Line 36
    lea esi, DWORD PTR [eax-2]
    inc eax
    cmp eax, 2000000000             ; 77359400H
    jl  SHORT $LL3@main

在这种情况下,lea 指令会做什么?

更新 2


非常感谢大家。我刚刚在 Nehalem xeon cpu 上测试了这段代码,结果在这里是相同的。看起来不知什么原因,asm 代码在 Sandy 桥上运行得更快。

4

3 回答 3

2

@modelnine 的评论是正确的 -lea用于简化循环中的分配。你有:

x = ii - 2;

并且lea(加载有效地址)指令正在有效地执行:

esi = &(*(eax - 2));

&*相互抵消(这很重要 - 在这种情况下取​​消引用可能eax会导致问题),所以你得到:

esi = eax - 2;

正是您的 C 代码尝试执行的操作。

于 2012-04-09T23:27:20.880 回答
2

我比较了非asm版本:

#include <iostream>
#include <chrono>

int main() {
    auto start = std::chrono::high_resolution_clock::now();

    long x = 0;
    long ii = 0;
    for(; ii < 2000000000; ++ii)
    {
        x = ii - 2;
    };

    auto finish = std::chrono::high_resolution_clock::now();
    std::cout << (finish-start).count() << '\n';
    std::cout << x << ii << '\n';
}

使用 asm 版本:

#include <iostream>
#include <chrono>

int main() {
    auto start = std::chrono::high_resolution_clock::now();

    asm (R"(
         mov $0, %eax
         mov $0, %ecx
         jmp startloop
         notequal:
         inc %eax
         mov %eax,%ecx
         sub $2,%ecx
         startloop:
         cmp $2000000000,%eax
         jne notequal
    )");

    auto finish = std::chrono::high_resolution_clock::now();
    std::cout << (finish-start).count() << '\n';
}

使用铿锵声3.1

启用优化后,asm 版本大约需要 1.4 秒,而非 asm 版本需要 45 纳秒。这使得汇编版本慢了大约 3200 万%。

这是为非 asm 版本生成的程序集:

movl    $1999999997, %esi       ## imm = 0x773593FD
callq   __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
movq    %rax, %rdi
movl    $2000000000, %esi       ## imm = 0x77359400
callq   __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
于 2012-04-09T23:57:27.560 回答
1

你为什么不尝试gcc -Ofast或者也许gcc -O1

这是一个预告片:gcc -Q -Ofast --help=optimizers,就在 gnu 手册中!

这是一个比较:

section .text
global _start

_start:
    mov eax, 0
    mov ecx, 0
    jmp startloop
    notequal:
    inc eax
    mov ecx, eax
    sub ecx, 2
    startloop:
    cmp eax, 2000000000
    jne notequal

    int     0x80

    mov     ebx,0
    mov     eax,1
    int     0x80

我得到了1.306ms它,并且 C 被定时为:

real    0m0.001s
user    0m0.000s
sys     0m0.000s

使用gcc -O1定时是:

real    0m1.295s
user    0m1.262s
sys     0m0.006s

它实际上执行代码。

对于 MSVC,使用 /O2 或 /O1 编译选项应该能够获得类似的结果。详细信息在这里http://msdn.microsoft.com/en-us/library/k1ack8f1.aspx

于 2012-04-09T23:57:03.333 回答