我try 比较内联汇编语言和C++代码的性能,所以我写了一个函数,它将两个数组的大小2000增加100000次.以下是代码:

#define TIMES 100000
void calcuC(int *x,int *y,int length)
{
    for(int i = 0; i < TIMES; i++)
    {
        for(int j = 0; j < length; j++)
            x[j] += y[j];
    }
}


void calcuAsm(int *x,int *y,int lengthOfArray)
{
    __asm
    {
        mov edi,TIMES
        start:
        mov esi,0
        mov ecx,lengthOfArray
        label:
        mov edx,x
        push edx
        mov eax,DWORD PTR [edx + esi*4]
        mov edx,y
        mov ebx,DWORD PTR [edx + esi*4]
        add eax,ebx
        pop edx
        mov [edx + esi*4],eax
        inc esi
        loop label
        dec edi
        cmp edi,0
        jnz start
    };
}

以下是main():

int main() {
    bool errorOccured = false;
    setbuf(stdout,NULL);
    int *xC,*xAsm,*yC,*yAsm;
    xC = new int[2000];
    xAsm = new int[2000];
    yC = new int[2000];
    yAsm = new int[2000];
    for(int i = 0; i < 2000; i++)
    {
        xC[i] = 0;
        xAsm[i] = 0;
        yC[i] = i;
        yAsm[i] = i;
    }
    time_t start = clock();
    calcuC(xC,yC,2000);

    //    calcuAsm(xAsm,yAsm,2000);
    //    for(int i = 0; i < 2000; i++)
    //    {
    //        if(xC[i] != xAsm[i])
    //        {
    //            cout<<"xC["<<i<<"]="<<xC[i]<<" "<<"xAsm["<<i<<"]="<<xAsm[i]<<endl;
    //            errorOccured = true;
    //            break;
    //        }
    //    }
    //    if(errorOccured)
    //        cout<<"Error occurs!"<<endl;
    //    else
    //        cout<<"Works fine!"<<endl;

    time_t end = clock();

    //    cout<<"time = "<<(float)(end - start) / CLOCKS_PER_SEC<<"\n";

    cout<<"time = "<<end - start<<endl;
    return 0;
}

然后我运行程序五次,得到处理器的周期,这可以看作是时间.每次我只调用上面提到的函数之一.

结果来了.

汇编版本的功能:

Debug   Release
---------------
732        668
733        680
659        672
667        675
684        694
Average:   677

C++版本的功能:

Debug     Release
-----------------
1068      168
 999      166
1072      231
1002      166
1114      183
Average:  182

发布模式下的C++代码几乎比汇编代码快3.7倍.为什么?

我猜我写的汇编代码没有GCC生成的那么有效.对于像我这样的普通程序员来说,编写代码的速度很难超过它的对手由编译器生成的代码.这是不是意味着我不应该相信自己手写的汇编语言的性能,专注于C++,而忘记汇编语言?

推荐答案

是的,大多数时候.

首先,你从错误的假设出发,认为低级语言(本例中是汇编语言)总是比高级语言(本例中是C++和C)生成更快的代码.这不是真的.C代码总是比Java代码快吗?不,因为还有另一个变量:程序员.编写代码的方式和对体系结构细节的了解会极大地影响性能(正如您在本例中看到的).

您可以在always中产生一个示例,其中手工汇编代码比编译代码好,但usually是虚构的示例或单个例程,而不是500行C++代码的true程序.我认为编译器将产生更好的汇编代码95%的时间和sometimes, only some rare times,你可能需要编写汇编代码为少数,短,highly usedperformance critical例程或当你必须访问你最喜欢的高级语言没有公开的功能.你想了解一下这种复杂性吗?在这里读this awesome answer.

为什么会这样?

首先,因为编译器可以进行我们甚至无法想象的优化(参见this short list),它们将在seconds年(we may need days年时)进行优化.

在汇编中编写代码时,必须使用定义良好的调用接口生成定义良好的函数.然而,他们可以考虑whole-program optimizationinter-procedural optimization这样的数字

对于一些复杂的微控制器,甚至有system个库是用C语言编写的,而不是汇编语言,因为它们的编译器可以生成更好(且易于维护)的最终代码.

Compilers sometimes can automatically use some MMX/SIMDx instructions by themselves, and if you don't use them you simply can't compare (other answers already reviewed your assembly code very well). Just for loops this is a short list of loop optimizations of what is commonly checked for by a compiler (do you think you could do it by yourself when your schedule has been decided for a C# program?) If you write something in assembly, I think you have to consider at least some simple optimizations. The school-book example for arrays is to unroll the cycle (its size is known at compile time). Do it and run your test again.

如今,由于另一个原因,需要使用汇编语言也很少见:plethora of different CPUs.你想支持他们吗?每个都有一个特定的microarchitecture和大约specific instruction sets.它们有不同数量的功能单元,应安排装配说明,以保持它们全部busy个.如果你用C写,你可以用PGO,但在汇编中,你需要对特定的体系结构(和rethink and redo everything for another architecture)有很好的了解.对于小任务,编译器usually会做得更好,对于复杂任务usually,工作不会得到回报(无论如何,compiler may do better).

如果你坐下来看一看你的代码,你可能会发现重新设计你的算法比翻译成汇编语言(阅读这篇文章great post here on SO)会获得更多的收获,在你需要求助于汇编语言之前,有一些高级的优化(以及对编译器的提示)可以有效地应用.可能值得一提的是,经常使用内部函数将获得您正在寻找的性能提升,并且编译器仍然能够执行其大部分优化.

综上所述,即使你能产生快5~10倍的汇编代码,你也应该询问你的客户,他们是喜欢pay个一周的your time个还是buy a 50$ faster CPU个.大多数人都不需要极端优化(尤其是在LOB应用程序中).

C++相关问答推荐

这两个具有递归函数和静态变量的 C 程序谁有不同的输出?

如何获得 C 函数的基本编译二进制代码?

预处理器算法是否应该与编译所针对的体系结构相匹配?

为什么 memchr() 将 void 指针作为输入?

可能的堆栈粉碎?

DirectX11 与多视频适配器 (GPU) PC

如何让我的应用程序找到 gettext 翻译?

16 位 x86 MS-DOS 实模式下的分段远指针分配

OpenSSL 3 Diffie-Hellman 密钥交换 C++

OpenMP 线程 ID 如何与递归一起工作?

为什么`(char)~0`和`(unsigned char)~0`返回不同宽度的值?

为什么这个 .c 文件 #include 本身?

如果平台上“long”和“int”的大小相同 - “long”和“int”有什么不同吗?

为什么需要为每个操作系统重新编译 C/C++?

项目中包含程序集文件时,来自 mmap 的意外 exec 权限

将输出重定向到文件时 printf() 和 system() 的结果顺序错误

“保留用于任何用途”是什么意思?

为什么使用 abs() 或 fabs() 而不是条件否定?

编译器中的 8 位布尔值.对它们的操作效率低吗?

为什么“sizeof(a ? true : false)”会给出四个字节的输出?