日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

常见性能优化小技巧原理

發布時間:2023/12/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 常见性能优化小技巧原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、多用有序數組+折半查找
金山衛士開源后立馬招來各種批判,其中有一段批評金山衛士源碼說太多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("%Favorites%"))) {::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_FAVORITES, 0); } else if (0 == _tcsicmp(szVariable, _T("%CommonDocuments%"))) {::SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_COMMON_DOCUMENTS, 0); }

.......

算上中間省去的代碼這代碼這一共有三十項比較,代碼寫成這樣的確不雅。曾經在和朋友討論提出使用map,通過以字符串大小做key,這樣查找效率會提高很多。不過筆者認為最好的方法還是結構體數組,然后通過字符串大小規則排序,然后使用折半查找法保證效率和map一致(其實略高于紅黑樹結構的map,嚴格的說map的查找效率只是接近lgN,具體可以參考紅黑樹數據結構),這樣做能保證查找效率的時候還能夠節約內存,雖說這地方因為數據量小而體驗不出明顯節約內存。

為什么數組比map更節約內存?因為map是采用紅黑樹數據結構,每個節點都是一個單獨的堆節點,考慮到每次申請內存時多余的堆數據結構和指向其他節點的指針,當節點數達到一定時候內存占用量就會表現越加明顯。

其實可以做一個簡單的實驗,分別寫兩個程序測試;

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

上面兩個程序運行起來你會從任務管理器發現后者占內存明顯比前者大,其實是一樣的道理。類似這種寫法在開源的duilib里面也存在不少,對話框上空間創建時會根據各種名字判斷來做相應的操作,很多比較也是不斷的if else,但是這種方法并不是什么時候都適用的,考慮到數組增加元素的麻煩性,如果元素需要不斷增加刪除,那么map就是更好的選擇,另外如果能預先知道元素的查找頻率,得知極少數元素查找頻率極高而大部分元素極少查詢,那么用表驅動,將查找頻率最高的元素放在首位按照無序遍歷也是不錯的選擇。

筆者曾經開發一款安全軟件,由于該軟件啟動時會搜素本機上可保護的軟件列表,同時能監控進程啟動時判斷該程序是否需要被保護起來(主要是防止進程被打開,注入等操作),但是軟件攜帶的保護列表數以千計,前人在開發過程中或許是當時軟件數目較少于是沒做排序直接放到vector中,然后每次有進程啟動都要遍歷數組而效率很低,但是按照自己的規則進行排序后以折半查找方式則很大幅度減少資源以至于啟動時搜索可保護軟件列表明顯提高一個檔次。

二、巧用哨兵元素

問題由來:對于一個有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) {int nlastIndex = nSize-1;//首先判斷最后一個元素是不是關鍵字,如果是直接返回if (a[nlastIndex] == nKey){return TRUE;}//保存數組中最后一個元素,同時將最后一個元素賦值成keyint nSavelastValue = a[nlastIndex];a[nlastIndex] = nKey;int i = 0;while (a[i]!=nKey){i++;}//由于最后一個元素為key,所以上面的循環必定有出口//當循環跳出后如果此時索引指向最后一個元素,說明查找失敗//反之成功a[nlastIndex] = nSavelastValue;if (i < nlastIndex){return TRUE;}return FALSE; }

通過以上寫法可以講比較次數降低到N+2,同時增加4次賦值與一次減法操作,比起之前2N次比較工作量明顯小了許多,同時CPU執行的指令也會大幅度減少,效率自然就更高了。

?

三、多用參數引用化

相信讀者一眼就能看出來以下代碼效率上的區別,不加引用程序會為參數創造出一個臨時對象,并且在函數退出時析構,而換成引用后則省去這一步驟。如果這樣的函數調用太過頻繁這種寫法節省出來的性能開銷還是相當可觀,但是很多人的代碼并沒有太過注意這一點。

void SetFileInfo(ThreadSortData fileinfo) {ThreadSortData fileinfo1 = fileinfo; }void SetFileInfo1(const ThreadSortData &fileinfo) {ThreadSortData fileinfo1 = fileinfo; }

其實對于這種簡單的結構體這種引用化參數并沒帶來太多意義上的性能節省,但是對于下面這個例子相信只要試過的讀者肯定記憶極深,看代碼

void SetlistInfo(list<int> listTest) {return; }int _tmain(int argc, _TCHAR* argv[]) {list<int> listtest;for (int i = 0;i < 1000000;i++){listtest.push_back(i);}SetlistInfo(listtest);return 0; }

筆者在VS2005 debug版本下測試,當程序執行到SetlistInfo內部時內存占用量立刻翻倍,而且由于大量拷貝操作使得進該函數時耗時間和外面的for循環一樣多,但是一旦參數引用化后進入SetlistInfo函數簡直毫無壓力。也就是說進入該函數的時候程序也重新拷貝一份list,并且復制listtest內部值到這個臨時對象,由于listtest元素量特別多以至于這個拷貝耗時間和空間幾乎讓人無法容忍,到這里相信大家都能明白參數引用化可以帶來多大的性能節省。

?

四、迭代器自加的正確寫法

關于STL的教程很多都提到過迭代器自加應該寫成++iter而不是iter++,對于類似整形數據來說其實這樣寫根本沒什么區別,但是對于迭代器來說效率上卻區別很大。下面通過反匯編代碼看看加號寫在前面和后面的區別(以Visual C++ 2005 Debug版本為例):

list<ThreadSortData>::iterator iter = listtest.begin(); 004136EE lea eax,[ebp-64h] 004136F1 push eax 004136F2 lea ecx,[ebp-44h] 004136F5 call std::list<ThreadSortData,std::allocator<ThreadSortData> >::begin (411276h) 004136FA mov byte ptr [ebp-4],2 iter++; 004136FE push 0 00413700 lea eax,[ebp-144h] 00413706 push eax 00413707 lea ecx,[ebp-64h] 0041370A call std::list<ThreadSortData,std::allocator<ThreadSortData> >::_Iterator<1>::operator++ (41122Bh) 0041370F lea ecx,[ebp-144h] 00413715 call std::list<ThreadSortData,std::allocator<ThreadSortData> >::_Iterator<1>::~_Iterator<1> (41155Fh) ++iter; 0041371A lea ecx,[ebp-64h] 0041371D call std::list<ThreadSortData,std::allocator<ThreadSortData> >::_Iterator<1>::operator++ (411389h)

而在++iter第一個call當中調用的是以下代碼(部分簡化并沒有實際貼全):

_Myt_iter _Tmp = *this; 00413FFC mov eax,dword ptr [ebp-14h] 00413FFF push eax 00414000 lea ecx,[ebp-28h] 00414003 call std::list<ThreadSortData,std::allocator<ThreadSortData> >::_Iterator<1>::_Iterator<1> (41163Bh) 00414008 mov dword ptr [ebp-4],1 ++*this; 0041400F mov ecx,dword ptr [ebp-14h] 00414012 call std::list<ThreadSortData,std::allocator<ThreadSortData> >::_Iterator<1>::operator++ (411389h)

相信即使是完全沒看過反匯編的朋友此時也能明白上述代碼創建了一個臨時對象,之后再去下一個值,然后析構之前創建的臨時對象。但是對于++iter來說只是在地址0041371D處調用了一個call,這個call跟蹤進去發現其實代碼如下:

++(*(_Mybase_iter *)this); 00413F73 mov ecx,dword ptr [this] 00413F76 call std::list<ThreadSortData,std::allocator<ThreadSortData> >::_Const_iterator<1>::operator++ (411050h) return (*this); 00413F7B mov eax,dword ptr [this]

相對++寫在后面,此時少了一步構造析構的操作,當然這部分操作帶來的性能節省將會隨著對象的構造析構的復雜性或者調用頻率而產生不同的效果。

未完待續...

轉載于:https://www.cnblogs.com/mod109/p/3886589.html

總結

以上是生活随笔為你收集整理的常见性能优化小技巧原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。