C++ 在C/C+中使用汇编程序优化某些函数+;这个计划真的值得吗?

C++ 在C/C+中使用汇编程序优化某些函数+;这个计划真的值得吗?,c++,c,optimization,assembly,C++,C,Optimization,Assembly,在某些开发领域,如游戏开发、实时系统等,有一个快速优化的程序是很重要的。另一方面,现代编译器已经进行了大量优化,在一个需要考虑截止日期因素的世界中,在汇编中进行优化可能非常耗时 问题: 是优化某些函数,并在 C/C++程序真的值得吗 这真的有足够的收益吗 优化C/C时的性能++ 程序集与今天的 现代编译器 据我所知,在某些领域,如嵌入式系统、多媒体编程(图形、声音等),能够获得的任何收益都很重要。此外,人们需要有能力(或有人有能力)在汇编中比现代编译器做得更好。做一些真正优化的C/C++可以花

在某些开发领域,如游戏开发、实时系统等,有一个快速优化的程序是很重要的。另一方面,现代编译器已经进行了大量优化,在一个需要考虑截止日期因素的世界中,在汇编中进行优化可能非常耗时

问题:
  • 是优化某些函数,并在 C/C++程序真的值得吗

  • 这真的有足够的收益吗 优化C/C时的性能++ 程序集与今天的 现代编译器



  • 据我所知,在某些领域,如嵌入式系统、多媒体编程(图形、声音等),能够获得的任何收益都很重要。此外,人们需要有能力(或有人有能力)在汇编中比现代编译器做得更好。做一些真正优化的C/C++可以花费更少的时间,并且可以做得足够好。最后一件事,学习汇编有助于理解程序的内部机制,最终使某人成为更好的程序员。

    唯一可能的答案是:是的,如果有相关和有用的性能提升

    我想真正的问题应该是:在C/C++程序中使用汇编语言能获得有意义的性能提升吗

    答案是肯定的

    随着库和编译器的改进,在过去10-20年中,性能得到有意义的提高的情况可能有所减少,但对于x86这样的体系结构,尤其是某些应用程序(特别是与图形相关的应用程序)中的手动优化,这是可以做到的

    但就像任何事情一样,在需要优化之前不要进行优化


    我认为,在绝大多数情况下,算法优化和编写高效的C(尤其是C)比用汇编语言重写所花费的时间要少得多。难点在于,考虑到现代CPU的体系结构,你能比编译器做得更好吗。如果您是为一个简单的cpu(比如嵌入式系统)设计的,那么您可以进行合理的优化,但是,对于流水线架构来说,优化要困难得多,因为您需要了解流水线是如何工作的

    因此,考虑到这一点,如果您可以进行此优化,并且您正在做一些探查器告诉您速度太慢的事情,并且这是一个应该尽可能快的部分,那么是的优化是有意义的。

    也许 这完全取决于单个程序 您需要一个配置文件,在您知道之前,您可以使用配置文件工具获得该配置文件。有些程序将所有的时间都花在等待数据库上,或者它们只是没有集中的运行时间。没有这一点,组装就没有多大帮助

    根据经验,90%的运行时间发生在10%的代码中。你真的想要一个非常严重的瓶颈,不是每个程序都有

    而且,现在的机器速度如此之快,可以说,编译器和CPU内核已经吃掉了一些低垂的果实。例如,假设您编写的代码比编译器好得多,并将指令数减半。即使这样,如果最终执行相同数量的内存引用,并且它们是瓶颈,那么您可能不会赢

    当然,您可以在以前的循环迭代中开始预加载寄存器,但编译器可能已经在尝试了


    学习汇编实际上更重要,因为它是理解机器真正是什么的一种方式,而不是击败编译器的一种方式。但是试试看

    有一个领域仍然定期进行装配优化—嵌入式软件。这些处理器通常不是很强大,并且有许多架构上的怪癖,编译器可能无法利用这些怪癖进行优化。这就是说,它仍然应该只在代码特别紧凑的区域进行,而且必须有很好的文档记录。

    我想说,对于大多数人和大多数应用程序来说,这不值得。编译器非常擅长精确地优化它们所编译的体系结构

    这并不是说在装配中进行优化不是没有根据的。许多数学和低级密集型代码通常通过使用特定的CPU指令(如SSE*等)进行优化,以克服编译器生成的指令/寄存器使用。最终,人类准确地知道程序的要点。编译器只能假设这么多


    我想说的是,如果您不知道自己的汇编速度会更快,那么我会让编译器来完成这项艰巨的工作。

    我假设您已经分析了代码,并且发现了一个占用大部分时间的小循环

    首先,尝试使用更积极的编译器优化重新编译,然后重新配置文件。如果您随意运行了所有编译器优化,并且仍然需要更高的性能,那么我建议您查看生成的程序集


    在查看函数的汇编代码之后,我通常要做的是查看如何更改C代码以使编译器编写更好的汇编代码。这样做的好处是,我最终得到的代码可以在我的处理器上与我的编译器一起运行,但可以移植到其他环境。

    对于编写应用程序的典型小店开发人员来说,性能增益/工作量的权衡几乎不能证明编写汇编是正确的。即使在组装速度可以使某些瓶颈速度加倍的情况下,这种努力也往往是不合理的。在一家较大的公司,如果你是“绩效人”,这可能是合理的

    然而,对于一个库作者来说,即使是小的改进也往往是合理的,因为它为成千上万的开发人员和用户节省了时间
        DWORD* panCRC32Table = NULL; // CRC-32 CCITT 0x04C11DB7
    
        void DoneCRCTables()
        {
            if (panCRC32Table )
            {
                delete[] panCRC32Table;
                panCRC32Table= NULL;
            }
        }
    
        void InitCRC32Table()
        {
            if (panCRC32Table) return;
            panCRC32Table= new DWORD[256];
    
            atexit(DoneCRCTables);
    
        /*
            for (int bx=0; bx<256; bx++)
            {
                DWORD eax= bx;
                for (int cx=8; cx>0; cx--)
                    if (eax & 1)
                        eax= (eax>>1) ^ 0xEDB88320;
                    else
                        eax= (eax>>1)             ;
                panCRC32Table[bx]= eax;
            }
        */
                _asm cld
                _asm mov    edi, panCRC32Table
                _asm xor    ebx, ebx
            p0: _asm mov    eax, ebx
                _asm mov    ecx, 8
            p1: _asm shr    eax, 1
                _asm jnc    p2
                _asm xor    eax, 0xEDB88320           // bit-swapped 0x04C11DB7
            p2: _asm loop   p1
                _asm stosd
                _asm inc    bl
                _asm jnz    p0
        }
    
    
    /*
    DWORD inline CalcCRC32(UINT nLen, const BYTE* cBuf, DWORD nInitVal= 0)
    {
        DWORD crc= ~nInitVal;
        for (DWORD n=0; n<nLen; n++)
            crc= (crc>>8) ^ panCRC32Table[(crc & 0xFF) ^ cBuf[n]];
        return ~crc;
    }
    */
    DWORD inline __declspec (naked) __fastcall CalcCRC32(UINT        nLen       ,
                                                         const BYTE* cBuf       ,
                                                         DWORD       nInitVal= 0 ) // used to calc CRC of chained bufs
    {
            _asm mov    eax, [esp+4]         // param3: nInitVal
            _asm jecxz  p2                   // __fastcall param1 ecx: nLen
            _asm not    eax
            _asm push   esi
            _asm push   ebp
            _asm mov    esi, edx             // __fastcall param2 edx: cBuf
            _asm xor    edx, edx
            _asm mov    ebp, panCRC32Table
            _asm cld
    
        p1: _asm mov    dl , al
            _asm shr    eax, 8
            _asm xor    dl , [esi]
            _asm xor    eax, [ebp+edx*4]
            _asm inc    esi
            _asm loop   p1
    
            _asm pop    ebp
            _asm pop    esi
            _asm not    eax
        p2: _asm ret    4                    // eax- returned value. 4 because there is 1 param in stack
    }
    
    // test code:
    
    #include "mmSystem.h"                      // timeGetTime
    #pragma comment(lib, "Winmm.lib" )
    
    InitCRC32Table();
    
    BYTE* x= new BYTE[1000000];
    for (int i= 0; i<1000000; i++) x[i]= 0;
    
    DWORD d1= ::timeGetTime();
    
    for (i= 0; i<1000; i++)
        CalcCRC32(1000000, x, 0);
    
    DWORD d2= ::timeGetTime();
    
    TRACE("%d\n", d2-d1);