c语言 - 哪一个更有可能浪费更少的内存,一个大的内存管理器还是几个小的内存管理器?



首先,这可能更像是一个数学问题。

我正在编写一个需要内存的模块,直到它的实例死了才释放它,所以我写了一个简单的内存管理器来减少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较少,这是一个很大的成本。所以我的重点是内存使用情况。感谢您的帮助。

无论如何这都行不通:realloc不能保证就地成功调整大小 - 可以自由分配一个更大的块并将所有数据复制到更大的块中。我假设用户希望数据保留在固定地址。

解决此问题的最简单方法不是使用 C 库,而是使用特定于平台的虚拟内存 API 保留大量地址空间,然后根据需要向其提交内存。 例如,在 Windows 上,您将使用VirtualAlloc(NULL, size, MEM_RESERVE, 0)来保留所需的连续地址空间,然后随着已用内存区域的增长VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE)提交页面。这意味着每个内存池最多只有一个额外的页面。如果您坚持使用小 (4k) 页面,这意味着每个池浪费的字节永远不会超过 4092 个字节(比页面少一个字)。

此外,在 64 位系统上,无需使用 base+偏移量传递地址:重新分配实际上不会耗尽地址空间,因此无需在虚拟地址空间内移动内存的映射视图。您可以使用普通指针/引用!

为每个用户设置一个单独的内存区域有一些好处:它改进了引用的位置 - 用户的数据靠近在一起,从而提高了所有级别的缓存性能,包括发生分页时的页面交换器。

在 64 位应用程序中,为每个用户保留较大的地址空间不是问题,并且开销最小。 例如,您可以为每个用户保留 1GB。值得保留的是用户可能需要的最大面积的两倍。

在32 位应用程序中,如果所需的地址空间很大,则用户可能需要处理在地址空间内移动其数据的问题。这可以通过重新映射页面来实现,因此不需要复制任何内容。假设有一个 64 位操作系统支持应用程序,您可能会从将内存区域映射到文件中受益。这使您可以从地址空间中完全取消映射它,而不会丢失内容,并且这些内容也不必访问磁盘 - 如果可以,操作系统将缓存它们。因此,您可以为用户增加地址空间,而无需复制任何内容,也不会在增长操作期间浪费较小的地址空间:首先取消映射文件的较小视图,然后映射文件的较大视图。用户将需要应对为内存区域提供新的起始地址。用户可以通过向基址添加偏移来引用存储器:这表现得足够好,并允许可移动地址空间的灵活性。

最新更新