new运算符的几种优化

来源:互联网 发布:三维制图软件下载 编辑:程序博客网 时间:2024/05/07 04:30


近日在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出对应的文件名和代码行数了。

写完了,欢迎批评~ 

原创粉丝点击