哪一个更可能浪费更少的内存,一个大内存管理器或几个小内存管理器? [关闭]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哪一个更可能浪费更少的内存,一个大内存管理器或几个小内存管理器? [关闭]相关的知识,希望对你有一定的参考价值。

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

我正在编写一个需要逐个内存的模块,并且在它的实例死亡之前永远不会释放它,所以我写了一个简单的内存管理器来减少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 + offset传递地址:重新分配实际上不会耗尽地址空间,因此不需要在虚拟地址空间内移动映射的内存视图。你可以使用普通指针/参考!

为每个用户提供单独的内存区域是有好处的:它改善了引用的局部性 - 用户的数据靠得很近,从而提高了所有级别的缓存性能,包括页面交换器应该进行分页。

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

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

以上是关于哪一个更可能浪费更少的内存,一个大内存管理器或几个小内存管理器? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

linux内存--大内存页(大页)

linux内存--大内存页(大页)

linux内存--大内存页(大页)

如何让 MySQL 使用更少的内存?

为啥实习全局字符串值会导致每个多处理进程使用更少的内存?

哪个向量和地图,使用更少的内存(大量数据和未知大小)