几何尺寸与公差论坛

 找回密码
 注册
12
返回列表 发新帖
楼主: yogy

【转帖】stack与heap的不同之处

[复制链接]
 楼主| 发表于 2007-5-14 15:12:56 | 显示全部楼层

回复: 【转帖】stack与heap的不同之处

什么是常见的堆性能问题?     
   
  
以下是您使用堆时会遇到的最常见问题:     
  
分配操作造成的速度减慢。光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块,所以运行时分配程序代码会耗费周期寻找较大的空闲块,或从后端分配程序分配新块。     
   
  
释放操作造成的速度减慢。释放操作耗费较多周期,主要是启用了收集操作。收集期间,每个释放操作查找它的相邻块,取出它们并构造成较大块,然后再把此较大块插入空闲列表。在查找期间,内存可能会随机碰到,从而导致高速缓存不能命中,性能降低。     
  
堆竞争造成的速度减慢。当两个或多个线程同时访问数据,而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦;这也是目前多处理器系统遇到的最大问题。当大量使用内存块的应用程序或   DLL   以多线程方式运行(或运行于多处理器系统上)时将导致速度减慢。单一锁定的使用常用的解决方案意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。     
   
  
竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的,但开销更大的是数据从处理器高速缓存中丢失,以及后来线程复活时的数据重建。     
  
堆破坏造成的速度减慢。造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块,以及块的越界重写等明显问题。     
   
  
(破坏不在本文讨论范围之内。有关内存重写和泄漏等其他细节,请参见     
  Microsoft   Visual   C++(R)   
调试文档   。)     
   
  
频繁的分配和重分配造成的速度减慢。这是使用脚本语言时非常普遍的现象。如字符串被反复分配,随重分配增长和释放。不要这样做,如果可能,尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作。     
   
  
竞争是在分配和释放操作中导致速度减慢的问题。理想情况下,希望使用没有竞争和快速分配/释放的堆。可惜,现在还没有这样的通用堆,也许将来会有。     
   
  
在所有的服务器系统中(如   IISMSProxyDatabaseStacks、网络服务器、     
  Exchange   
和其他),   堆锁定实在是个大瓶颈。处理器数越多,竞争就越会恶化。     
   
  
尽量减少堆的使用     
   
  
现在您明白使用堆时存在的问题了,难道您不想拥有能解决这些问题的超级魔棒吗?我可希望有。但没有魔法能使堆运行加快因此不要期望在产品出货之前的最后一星期能够大为改观。如果提前规划堆策略,情况将会大大好转。调整使用堆的方法,减少对堆的操作是提高性能的良方。     
  
如何减少使用堆操作?通过利用数据结构内的位置可减少堆操作的次数。请考虑下列实例:     
  struct   ObjectA   {     
   
        //   objectA   
的数据     
   
  }     
   
  struct   ObjectB   {     
   
        //   objectB   
的数据     
   
  }     
   
  //   
同时使用   objectA      objectB     
  //     
   
  //   
使用指针     
  //     
  struct   ObjectB   {     
   
        struct   ObjectA   *   pObjA;     
   
        //   objectB   
的数据     
   
  }     
   
  //     
  //   
使用嵌入     
  //     
  struct   ObjectB   {     
        struct   ObjectA   pObjA;     
        //   objectB   
的数据     
  }     
   
        
  //     
  //   
集合   –   在另一对象内使用   objectA      objectB     
  //     
  struct   ObjectX   {     
        struct   ObjectA     objA;     
        struct   ObjectB     objB;     
  }     
   
        
  
避免使用指针关联两个数据结构。如果使用指针关联两个数据结构,前面实例中的对象   A      B   将被分别分配和释放。这会增加额外开销我们要避免这种做法。     
   
  
把带指针的子对象嵌入父对象。当对象中有指针时,则意味着对象中有动态元素(百分之八十)和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释放的需求。这将提高应用程序的性能。     
  
合并小对象形成大对象(聚合)。聚合减少分配和释放的块的数量。如果有几个开发者,各自开发设计的不同部分,则最终会有许多小对象需要合并。集成的挑战就是要找到正确的聚合边界。     
  
内联缓冲区能够满足百分之八十的需要(aka   80-20   规则)。个别情况下,需要内存缓冲区来保存字符串/二进制数据,但事先不知道总字节数。估计并内联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十,可以分配一个新的缓冲区和指向这个缓冲区的指针。这样,就减少分配和释放调用并增加数据的位置空间,从根本上提高代码的性能。     
  
在块中分配对象(块化)。块化是以组的方式一次分配多个对象的方法。如果对列表的项连续跟踪,例如对一个   {名称,值}   对的列表,有两种选择:选择一是为每一个名称-对分配一个节点;选择二是分配一个能容纳(如五个)名称-对的结构。例如,一般情况下,如果存储四对,就可减少节点的数量,如果需要额外的空间数量,则使用附加的链表指针。     
   
  
块化是友好的处理器高速缓存,特别是对于   L1-高速缓存,因为它提供了增加的位置   —不用说对于块分配,很多数据块会在同一个虚拟页中。     
  
正确使用   _amblksizC   运行时   (CRT)   有它的自定义前端分配程序,该分配程序从后端(Win32   堆)分配大小为   _amblksiz   的块。将   _amblksiz   设置为较高的值能潜在地减少对后端的调用次数。这只对广泛使用   CRT   的程序适用。     
   
  
使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能和可升缩性方面有所收获。另一方面,代码会有点特殊,但如果经过深思熟虑,代码还是很容易管理的。     
   


您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|Archiver|小黑屋|几何尺寸与公差论坛

GMT+8, 2024-11-16 08:45 , Processed in 0.109810 second(s), 13 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表