代码之家  ›  专栏  ›  技术社区  ›  Fire Lancer

有效地分配许多短期小对象

  •  10
  • Fire Lancer  · 技术社区  · 15 年前

    我有一个小类(32位系统上有16个字节),需要动态分配。在大多数情况下,任何给定实例的生命周期都非常短。有些实例也可以跨线程边界传递。

    在做了一些分析之后,我发现我的程序似乎花费了比实际使用它们更多的时间来分配和释放这些东西,所以我想用一些更有效率的东西来替换默认的new和delete。

    对于一个大的对象(数据库连接,这是很昂贵的建设,而不是分配),我已经在使用一个池系统,但这涉及到一个列表,用于存储“自由”对象,也包括一个互斥锁线程安全。在互斥对象和列表之间,它的性能实际上比对小对象执行基本的new/delete要差。

    我在Google上发现了一些小对象分配器,但是它们似乎使用的是一个全局/静态池,该池没有以线程安全的方式使用,因此不适合我使用:(

    对于这种小对象的有效内存管理,我还有什么其他选择?

    3 回复  |  直到 15 年前
        1
  •  1
  •   Stephen    15 年前

    也许试着用谷歌的 tcmalloc ? 它针对线程程序中的快速分配/释放进行了优化,并且对于小对象的开销很低。

        2
  •  1
  •   Steve Jessop    15 年前

    一些实例也可以跨线程边界传递

    只有“一些”?因此,如果能让那些不能传递给其他线程的线程更便宜,那么您也许可以为这些线程支付额外的费用。有多种方法可以让我想到每个线程有一个分配器,避免在分配或释放分配器所属的线程时需要锁定。我不知道在你的程序中哪一个是可能的:

    • 复制线程边界上的内容,而不是传递它们。

    • 如果它们由于任何原因被传递到另一个线程,那么它们将被传递回原始线程以释放。这种情况不一定经常发生,您可以在接收线程中排队,稍后将它们全部传递回消息中。当然,这假设拥有分配器的线程将继续存在。

    • 每个分配器有两个空闲列表,一个是同步的(当对象从另一个线程中释放时添加到其中),另一个是不同步的。仅当未同步列表为空且正在分配(因此在拥有分配器的线程中)时,才需要锁定已同步的空闲列表并将其所有当前内容移动到未同步列表。如果很少有对象被传递到其他线程,那么这基本上消除了互斥锁上的争用,并大大减少了占用互斥锁的次数。

    再往后退一步,你的应用程序是否经常处于这样一种状态:你知道在某一点之后分配的所有单元格(可能在过去有一点)都不再使用了?如果是这样的话,假设你的小对象的析构函数没有做任何非常紧急的事情,那么就根本不用费心释放单元了——在“某个点”创建一个新的分配器,并将旧的标记为不再用于新的分配。当您“进入状态”时,释放整个分配器及其底层缓冲区。如果“某个点”和“状态”是同时的,那么就更容易了——只需重置分配器。

        3
  •  0
  •   Mark Wilkins    15 年前

    您可以确保使用 low fragmentation heap . 在Vista和以后的操作系统中,它是默认的,但我不认为以前的操作系统是这样的,这会对小对象的分配速度产生很大的影响。