有效的堆管理为重流失,小小的分配?
-
03-07-2019 - |
题
我在寻找的想法一堆管理人来处理一个非常具体的情况:大量的很小部分拨款,从12至64字节的每一个。任何东西越大,我将通过在以经常堆管理,所以只有小块需要被照顾。只有4个字节的准是必要的。
我的主要关注的问题是
- 开销。经常libc堆通常会的圆形分配一个多的16个字节,然后再添加另一个16字节的标题-这意味着超过50%的开销上一个20字节的分配,这糟透了。
- 性能
一个有帮助的方面是,Lua(这是用户的这堆)会告诉你,大小的块这是释放时,它呼吁free()-这可能使某些优化.
我会后我目前的做法,其工作确定,但我想提高它如果在所有可能的。任何想法?
解决方案
它是可能建立一堆管理,这是非常有效的对象是所有大小相同。你可以创建一个这堆每个尺寸对象的需要,或者如果你不介意用点空间,创建一个为16字节的目的,一个用于32个,另一个用于64个。最大的开销31字节33字节的分配(这将在64blocksize堆).
其他提示
扩大在什么Greg Hewgill说,一个办法做到的超高效率的固定大堆是:
- 分裂一个很大的缓冲区进入点。节点尺寸必须至少sizeof(void*).
- 串他们在一起成为一个单独联清单("免费清单"),使用第一sizeof(void*)字节的每一个免费的节点作为一个链接指针。已分配的节点将不需要一个链接指针,使每个节点的开销为0。
- 分配通过删除头的清单并将其返回(2载荷,1商店)。
- 免费的,通过插入在总的清单(1载荷,2店)。
很明显步骤3还有若要检查列表是空的,并且如果这样做了一堆的工作得到一个新的大缓冲器(或不).
甚至更有效率,以格雷格*D和hazzen说,是分配,通过增加或减少一个指(1载,1储存),而不是提供一种免费的一个单一的节点。
编辑:在这两种情况下,自由可以处理与复杂的"的任何更大的我通过在定期堆管理"的有用事实上,你的尺寸回来的呼吁。否则你会看着一个标志(开销可能的4个字节每个节点),或者查找某种的记录,缓冲区(s)你已经使用。
答案可能取决于生模式为这些对象。如果对象是所有实例为你继续,然后所有删除一举,也可能是有意义的创建一个非常简单堆管理,分配内存通过简单地增加一个指针。然后,当你完成后,吹走的整个堆。
雷蒙德陈做了一个 有趣的后 这可能有助于激励你。:)
我喜欢onebyones的答案。
你也可能会考虑的 伙计系统 你的套固定的尺寸堆。
如果一堆存是分配、使用和释放之前移动到下一轮的分配,我建议使用最简单的分配程序可能:
typedef struct _allocator {
void* buffer;
int start;
int max;
} allocator;
void init_allocator(size_t size, allocator* alloc) {
alloc->buffer = malloc(size);
alloc->start = 0;
alloc->max = size;
}
void* allocator_malloc(allocator* alloc, size_t amount) {
if (alloc->max - alloc->start < 0) return NULL;
void* mem = alloc->buffer + alloc->start;
alloc->start += bytes;
return mem;
}
void allocator_free(allocator* alloc) {
alloc->start = 0;
}
我用一个主要O(1)小块存管理(SBMM).基本上它的工作原理是这样的:
1)它分配较大的超级区块,从操作系统和轨道开始+端地址作为一个范围。大小超级可调节,但1MB使一个很好的大小。
2)的超级区块分为块(也可调节尺寸...4K-64K是好的,这取决于你的应用程序).每个这些区块处理拨款的特定尺寸和储存中的所有项目的框为一个单独联系名单。当你分配了一个超级块,你犯了一个链接清单的免费块。
3)分配一个项目意味着一)检查,看是否有一块免费的项目处理,大小如果不是,分配一个新的框从超级区块.B)删除该项目从块是免费的列表。
4)释放一个项目通过地址装A)寻找超级块含有地址(*)B)寻找块超级(减去超级启动的地址和分块的大小)C)推动项目的背块上是免费的项目清单。
正如我所说,这SBMM是非常快,因为它与运行O(1)表现(*).在该版本的我已经实现,我使用一个AtomicSList(类似于SLIST在Windows)因此,它不仅是O(1)表现,但也线程安全和无锁的执行情况。你其实可以实现的算法使用Win32SLIST如果你想要的。
有趣的是,算法用于分配区块的超级区块或项目从块的结果在几乎identicaly代码(他们都是O(1)拨款掉一个免费的列表)。
(*)的超级区块排列在一rangemap与O(1)平均表现(但是一个潜在的O(Lg N)为最差,其中N为数量的超级区块).宽rangemap取决于知道大概多少内存你需要为了得到O(1)的性能。如果你过你会浪费一点点记忆但仍然得到O(1)的性能。如果你下,你会做法O(Lg N)性能,但N是超级计数--不该项计数。由于超级计数相比是非常低的项目计数(大约20二进制数量级,在我的代码),它不是作为关键,因为其余的分配器。