3

我有一个 c++ 程序,它使用了几个非常大的双精度数组,我想减少程序这个特定部分的内存占用。目前,我分配了 100 个,每个可以是 100 Mb。

现在,我确实有优势,这些数组的一部分最终在程序执行的后期部分变得过时,并且几乎不需要在任何时候将其中任何一个都保存在内存中。

我的问题是这样的:

在我用 new 或 malloc 创建数组后,有什么方法可以告诉操作系统它的一部分不再需要了?我得出的结论是,实现这一点的唯一方法是声明一个指针数组,每个指针都可能指向一个块,比如 1Mb 的所需数组,这样不再需要的旧块可以重新用于数组的新位。在我看来,这就像编写一个自定义内存管理器,看起来有点像大锤,这也会对性能造成一些影响

我无法移动数组中的数据,因为它会导致过多的线程争用问题。数组可以随时被大量线程中的任何一个访问,尽管只有一个线程会写入任何给定的数组。

4

5 回答 5

4

这取决于操作系统。POSIX - 包括 Linux - 有系统调用madvise来提高内存性能。从手册页:

madvise() 系统调用建议内核如何处理地址范围内的分页输入/输出,地址范围从地址 addr 开始,大小为长度字节。它允许应用程序告诉内核它希望如何使用一些映射或共享内存区域,以便内核可以选择适当的预读和缓存技术。此调用不会影响应用程序的语义(MADV_DONTNEED 除外),但可能会影响其性能。内核可以随意忽略这些建议。

madvise有关更多信息,请参见手册页。

编辑:显然,上面的描述不够清楚。所以,这里有一些更多的细节,其中一些是特定于 Linux 的。

您可以使用它mmap来分配一块内存(直接来自操作系统而不是 libc),它不受任何文件的支持。对于大块内存,malloc正在做完全相同的事情。您必须使用munmap来释放内存 - 无论使用madvise

void* data = ::mmap(nullptr, size, PROT_READ | PROT_WRITE,
    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// ...
::munmap(data, size);

如果你想摆脱这个块的某些部分,你可以使用madvise告诉内核这样做:

madvise(static_cast<unsigned char*>(data) + 7 * page_size,
    3 * page_size, MADV_DONTNEED);

地址范围仍然有效,但不再受物理 RAM 和存储支持。如果您稍后访问这些页面,内核将动态分配一些新页面并将它们重新初始化为零。请注意,不需要的页面也是进程虚拟内存大小的一部分。可能需要对虚拟内存管理进行一些配置更改,例如激活过度提交。

于 2012-04-20T11:26:37.760 回答
1

如果我们有更多的细节会更容易回答。

1°)“在我用 new 或 malloc 创建数组后,有什么方法可以告诉操作系统它的一部分不再需要了?”这个问题的答案。是“不是真的”。这就是 C 和 C++ 以及任何让您手动处理内存的语言的意义所在。

2°)如果您使用的是 C++ 而不是 C,则不应使用 malloc。

3°) 也不是数组,除非有非常特殊的原因。使用 std::vector。

4°)最好,如果您需要经常更改数组的内容并减少内存占用,请使用链表(std::list),尽管单独“访问”列表的内容会更昂贵(但如果你只迭代它,速度几乎一样快)。

于 2012-04-20T11:28:46.783 回答
1

带有std::deque指向的指针std::array<double,LARGE_NUMBER>可以完成这项工作,但您最好使用双端队列创建一个专用容器,这样您就可以重新映射索引,最重要的是,定义何时不再使用条目。

专用容器还可以包含读/写锁,因此可以以线程安全的方式使用。

于 2012-04-20T11:29:53.693 回答
0

您可以尝试使用列表而不是数组。当然 list 比 array 更“重”,但另一方面,重建一个 list 很容易,这样你就可以在它过时时扔掉它的一部分。您还可以使用一个包装器,它只包含索引,说明列表的哪一部分是最新的,哪一部分可以重用。这将帮助您提高性能,但需要更多(可重用)内存。

于 2012-04-20T11:27:43.827 回答
0

delete[]在途中按块和-ing 和-ing分配new[]似乎是一个很好的解决方案。尽可能少地进行内存管理是可能的。不要自己重用块,只需释放旧块并在需要时分配新块。

于 2012-04-20T11:51:22.743 回答