首页 > 代码库 > 常见性能优化技巧总结

常见性能优化技巧总结

一、多用有序数组+折半查找
金山卫士开源后立马招来各种批判,其中有一段批评金山卫士源码说太多if else而不用表驱动使得代码可读性不高,笔者看了下大致如下:

TCHAR  szFolderPath[MAX_PATH + 1] = {0}; // MichaelPeng: if else太多,应做成表驱动if (0 == _tcsicmp(szVariable, _T("%Desktop%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_DESKTOP, 0);} else if (0 == _tcsicmp(szVariable, _T("%Internet%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_INTERNET, 0);} else if (0 == _tcsicmp(szVariable, _T("%Programs%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_PROGRAMS, 0);} else if (0 == _tcsicmp(szVariable, _T("%Controls%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_CONTROLS, 0);} else if (0 == _tcsicmp(szVariable, _T("%Printers%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_PRINTERS, 0);} else if (0 == _tcsicmp(szVariable, _T("%Personal%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_PERSONAL, 0);} else if (0 == _tcsicmp(szVariable, _T("%Favorites%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_FAVORITES, 0);} else if (0 == _tcsicmp(szVariable, _T("%Startup%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_STARTUP, 0);} else if (0 == _tcsicmp(szVariable, _T("%Recent%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_RECENT, 0);} else if (0 == _tcsicmp(szVariable, _T("%Sendto%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_SENDTO, 0);} else if (0 == _tcsicmp(szVariable, _T("%Bitbucket%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_BITBUCKET, 0);} else if (0 == _tcsicmp(szVariable, _T("%StartMenu%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_STARTMENU, 0);} else if (0 == _tcsicmp(szVariable, _T("%Mydocuments%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_MYDOCUMENTS, 0);} else if (0 == _tcsicmp(szVariable, _T("%Mymusic%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_MYMUSIC, 0);} else if (0 == _tcsicmp(szVariable, _T("%Myvideo%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_MYVIDEO, 0);} else if (0 == _tcsicmp(szVariable, _T("%Desktopdirectory%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_DESKTOPDIRECTORY, 0);} else if (0 == _tcsicmp(szVariable, _T("%Drives%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_DRIVES, 0);} else if (0 == _tcsicmp(szVariable, _T("%Network%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_NETWORK, 0);} else if (0 == _tcsicmp(szVariable, _T("%Nethood%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_NETHOOD, 0);} else if (0 == _tcsicmp(szVariable, _T("%Fonts%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_FONTS, 0);} else if (0 == _tcsicmp(szVariable, _T("%Templates%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_TEMPLATES, 0);} else if (0 == _tcsicmp(szVariable, _T("%CommonStartMenu%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_COMMON_STARTMENU, 0);} else if (0 == _tcsicmp(szVariable, _T("%CommonPrograms%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_COMMON_PROGRAMS, 0);} else if (0 == _tcsicmp(szVariable, _T("%CommonStartup%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_COMMON_STARTUP, 0);} else if (0 == _tcsicmp(szVariable, _T("%LocalAppdate%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_LOCAL_APPDATA, 0);} else if (0 == _tcsicmp(szVariable, _T("%CommonAppdata%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_COMMON_APPDATA, 0);} else if (0 == _tcsicmp(szVariable, _T("%Windows%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_WINDOWS, 0);} else if (0 == _tcsicmp(szVariable, _T("%System32%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_SYSTEM, 0);} else if (0 == _tcsicmp(szVariable, _T("%ProgramFilesComm%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_PROGRAM_FILES_COMMON, 0);} else if (0 == _tcsicmp(szVariable, _T("%CommonDocuments%"))) {	::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_COMMON_DOCUMENTS, 0);}

代码写成这样可读性的确不高而且难看,曾经在和朋友讨论时候他们提到应该使用结构体数组做映射然后遍历查找,但是笔者认为这并非良策,同时也有人提出使用map,通过以字符串大小做key,这样查找效率会提高很多。不过笔者认为最好的方法还是结构体数组,然后通过字符串大小规则排序,然后使用折半查找法保证效率和map一致。这样做能保证查找效率的时候还能够节约内存,虽说这地方体验不出明显节约内存。

为什么数组比map更节约内存?因为map是采用红黑树数据结构,每个节点都是一个单独的堆节点,考虑到每次神奇怪内存时多余的堆数据结构和指向其他节点的指针,当节点数达到一定时候内存占用量就会表现越加明显。

其实可以做一个简单的实验,分别写两个程序测试;

void main(){	char *p = new char[100000];	getchar();}void main(){ 	for (int i = 0;i <100000;i++)		char *p = new char;	getchar();}

上面两个程序运行起来你会发现后者占内存明显比前者大,其实是一样的道理。
其实这种方法并不是什么时候都适用的,考虑到数组增加元素的麻烦性,如果元素需要不断增加删除,那么map就是更好的选择,另外如果能预先知道元素的查找频率,得知极少数元素查找频率极高而大部分元素极少查询,那么用表驱动,将查找频率最高的元素放在首位按照无序遍历也是不错的选择。

二、巧用哨兵元素

问题由来:对于一个有N个元素的无序数组a[n],判断其中是否有key这个值,写一个函数。

很多人拿到这个问题直接写出如下代码

BOOL GetIndexBkey(int a[],int nSize,int nKey){	for (int i = 0; i < nSize;i++)	{		if (nKey == a[i])		{			return TRUE;		}	}	return FALSE;}

这么写其实也能达到目的,但是细细看来每一次循环的时候都会比较两次i < nSize和nKey == a[i]。事实上我们可以用一种很巧妙的方法减少一次比较,这样以来在数据量很大的时候效率就会较明显提升,如果这个函数调用相当频繁那么优化效果还是很明显,具体如下:

BOOL GetIndexBkey(int a[],int nSize,int nKey){	//首先判断最后一个元素是不是关键字,如果是直接返回	if (a[nSize-1] == nKey)	{		return TRUE;	}	//保存数组中最后一个元素,同时将最后一个元素赋值成key	int nSavelastValue = http://www.mamicode.com/a[nSize-1];>

通过以上写法可以讲比较次数降低到N+2,同时增加3次赋值操作,比起之前2N次比较工作量明显小了许多,同时CPU执行的指令也会大幅度减少,效率自然就更高了。

 

未完待续...