new運算符的幾種優化


近日在msn上,朋友提了一個令他比較難受的問題,他做的程序性能偏低,簡單的看了一下,給出了幾點常用的建議,現列如此:
通常,通過重新定義new運算符可以用來產生一些比較有趣的行爲,但是注意如果重新定義了new運算符,那麼也應該定義一個和它配套的delete運算符

1.固定大小的對象的頻繁分配/釋放
多數情況下的多數系統,應用程序是在一個接一個的分配內存塊,那麼當應用程序釋放內存塊的時候,在已分配的內存中將出現空洞。當應用程序再次出現新的分配需求時,new函數將搜索所有的空洞來查看是否此空洞大小足以容下所要求的容量(有的系統是必須大於要求的容量)。
通常情況下,系統的這種做法會很正常,但是當特定的需求出現時,就會有好的辦法來優化它。那就是:當頻繁的分配/釋放固定大小的內存塊時,每一次的再次分配,系統都要去搜索所有的空洞來匹配大小,即便你剛剛釋放了一個大小相同的塊,這種情況在驅動或者消息系統下將非常的嚴重,大部分對象將會頻繁的分配,撤銷。
那麼我們可以在程序作以下優化,將每次釋放的內存指針暫時保存在Pool中,當有分配需求時,如果Pool中有,那麼返回給需求:
a.定義一個對象指針的數組,用來做緩衝池.例: Exp* Pool[CacheSize];
b.定義一個索引,指向當前的位置.例:int nPos = 0;
c.重載new/delete函數.   例:
void* Exp::operator new(size_t s)
{
 if( (s<sizeof(Exp)) && (nPos))
  return Pool[--nPos];
 else
  return ::operator new(s);
}

void Exp::operator::delete(void* p,size_t s)
{
  if(!p) return;
  if( (s<sizeof(Exp) && (nPos<CacheSize) )
   Pool[nPos++] = (Exp*)p;
  else
   ::operator delete(p);
}

2.虛擬存儲系統中的系統顛簸
虛擬存儲系統中,應用程序可以訪問遠大於物理空間的更多空間,OS將虛擬內存影射到主存上,併產生一個外頁表。
如果應用程序要存取映射到主存內的虛擬內存,OS將置換出一個主存中的頁。注:頁的大小一般爲4K,windows下用api可以得到SYSTEM_INFO   SI;
GetSystemInfo(&SI); DWORD aPageSize   =   SI.dwPageSize;
這時就會造成缺頁中斷,那麼缺頁中斷的時間由3部分組成:缺頁中斷服務時間、將缺頁讀入的時間、進程重新執行的時間。
簡單的來說就是慢100倍以上。大多數的應用程序中,系統將不常用的數據放到外頁面表中,這樣抖動不頻繁時,應用程序會運行得很好。
通常的應用程序,在每次new內存塊的時候,都會搜索整個內存空間來匹配一個區域,那麼也就是說,對象將被分配於內存空間的各個地方。在最差狀況裏,當應用程序的堆大小等於主存大小時,如果new查找空閒區域,OS就要不停的將內外頁交換,系統顛簸就會發生。
解決方法就是把相關的數據放在臨近的內存中。例如:一個文檔管理系統,將每個人的檔案中的相關對象都分配到一個內存區塊,也許會佔用比以前多的內存,但是對於
虛擬存儲系統來說,大小不重要,臨近才重要。(這個例子舉的並不怎麼好,因爲大部分文檔管理系統,是不需要這種性能優化的)
那麼重載一個new來做相關的分配是再適合不過了

3.利用重載的new運算符debug內存泄露
在new運算符中對引用計數++,在delete運算符中對引用計數--,在程序退出時,檢查如果技術是>0的話,那麼將有內存泄露發生。要是<0的話???
那應該是你多次刪除了一個對象所致,一般半路就崩潰了,到不了程序退出這裏。要檢查一下你在局部刪除一個對象後應該設它爲NULL.
還有一個小技巧可以定位到底是哪裏的指針泄露
#define new new(__FILE__,__LINE__) //unix下可能是_FILE_,_LINE_是編譯器定義的全局宏,代表文件名和行數
那麼重載new運算符後,可以自己管理一個數組來維護對應的文件和代碼行數,當程序運行到最後,可以查看哪個index對應的
對象沒有被釋放,那麼就可以print出對應的文件名和代碼行數了。

寫完了,歡迎批評~ 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章