C++ 在C/C+中使用汇编程序优化某些函数+;这个计划真的值得吗?
在某些开发领域,如游戏开发、实时系统等,有一个快速优化的程序是很重要的。另一方面,现代编译器已经进行了大量优化,在一个需要考虑截止日期因素的世界中,在汇编中进行优化可能非常耗时 问题:C++ 在C/C+中使用汇编程序优化某些函数+;这个计划真的值得吗?,c++,c,optimization,assembly,C++,C,Optimization,Assembly,在某些开发领域,如游戏开发、实时系统等,有一个快速优化的程序是很重要的。另一方面,现代编译器已经进行了大量优化,在一个需要考虑截止日期因素的世界中,在汇编中进行优化可能非常耗时 问题: 是优化某些函数,并在 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);