首先,这可能更像是一个数学问题。
我正在编写一个需要逐个内存的模块,并且在实例失效之前永远不会释放它,所以我编写了一个简单的内存管理器来减少malloc
。内存管理器在初始化期间需要一块内存,并且内存块的大小可由用户控制,然后管理器在需要时将内存块传递给用户。如果管理器内存不足,则会将其内存块大小加倍realloc
。最后,我们可以弄清楚所需内存大小与浪费的内存总量之间的关系是:
f(x) = 2^k - x, 2^(k-1) < x <= 2^k
现在我有几个内存用户,我可以为每个用户创建一个内存管理器(管理器的开销不值得考虑),或只创建一个内存管理器并在所有用户之间共享。用户数量和每个用户使用内存的大小可能在很大范围内变化。那么,哪种策略更有可能浪费更少的内存?
内存管理器会隐藏实际内存块位置并向用户提供偏移量,以避免realloc
问题。界面非常简单:
void *memo_ref(Memo memo, MemoOffset offset)
{
panic(offset < memo->used, "invalid offset is passed to memo");
return &memo->memory[offset];
}
所以我认为编译器会内联它并且优化并不困难。
此外,没有必要担心数据竞争,因为经理的所有用户都来自同一个线程。他们只是以交错的方式要求。
在我看来,一位大经理会带来更快的计划,因为realloc
的成本较低。所以我的重点是内存使用情况。谢谢你的帮助。
答案 0 :(得分:2)
这无论如何都不会起作用:realloc
无法保证成功调整大小 - 可以自由分配更大的块并将所有数据复制到更大的块中。我认为用户希望数据保持固定地址。
解决此问题的最简单方法是不使用C库,而是使用特定于平台的虚拟内存API来保留大块地址空间,然后根据需要向其提交内存。例如。在Windows上,您将使用VirtualAlloc(NULL, size, MEM_RESERVE, 0)
来保留所需的连续地址空间,然后VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE)
在您使用的内存区域增长时提交页面。 这意味着您每个内存池最多只有一个额外页面 。如果您坚持使用小(4k)页面,这意味着每个池(一个页面的一个单词)永远不会浪费超过4092个字节。
此外,在64位系统上,不需要使用base + offset传递地址:重新分配实际上不会耗尽地址空间,因此不需要移动映射的内存视图。虚拟地址空间。你可以使用普通的指针/引用!
为每个用户提供单独的内存区域有好处:它改善了引用的位置 - 用户的数据靠得很近,从而提高了所有级别的缓存性能,包括页面交换器应该进行分页。
在64位应用程序中,为每个用户保留大的地址空间不是问题,并且开销最小。例如。您可以为每个用户保留1Gbyte。值得保留两倍于用户可能需要的最大区域。
在32位应用程序中,如果所需的地址空间很大,则用户可能需要处理其数据在地址空间内移动的情况。这可以通过重新映射页面来实现,因此不需要复制任何内容。假设存在支持应用程序的64位操作系统,您可以将内存区域映射到文件中。这使您可以在不丢失内容的情况下从地址空间中完全取消映射,并且这些内容也不必命中磁盘 - 如果可以,操作系统将缓存它们。因此,您可以为用户增加地址空间,而无需复制任何内容,也不会在增长操作期间浪费较小的地址空间:首先取消映射文件的较小视图,然后映射文件的较大视图。用户需要应对为存储区域提供新的起始地址。用户可以通过向基址添加偏移来引用存储器:这样做性能足够好并且允许可移动地址空间的灵活性。