没有碎片stl容器的大容量内存

Bulk memory free of fragmented stl containers

本文关键字:大容量 内存 碎片 stl      更新时间:2023-10-16

当前,当我们想要用非常分散的内存分配来破坏复杂对象的非常大的嵌套列表/映射时,我假设C++要调用析构函数并逐个递归地释放内存,这需要大量时间,而且效率很低?

在我的例子中,我发现有时需要1分钟或更长的时间来销毁一个300GB的对象。

操作系统可以有效地杀死占用大量内存的进程,因为它只释放了所有内存,而不考虑进程内部的逻辑。

我想知道是否有任何现有的C/C++库可以做到这一点?要提供一个维护id系统的自定义内存分配器吗?这样,如果我指定一个id来为给定的大型STL容器(及其元素)创建分配器。当我想对它进行析构函数时,我可以释放所有用指定id分配的内存,只需丢弃指向外部容器的指针(它将跳过所有析构函数)?就像我们可以"杀死"一个pid。。。

谢谢!

这可以通过池分配器和新的位置来完成,当然你会有一些限制,比如在池中为你的插槽找到一个通用的大小(如果你不想要精细的粒度),但通常情况下,一个简单的场景如下:

struct Foo {
double x, y;
Foo(double x, double y) { this->x = x; this->y = y; };
};
std::byte* buffer = new std::byte[sizeof(Foo) * 10];
Foo* foo1 = new(buffer) Foo(1.0, 2.0);
Foo* foo2 = new(buffer + sizeof(Foo)) Foo(1.0, 2.0);
delete[] buffer;

解释了基本原理。不过,这必须采取预防措施,因为没有人调用析构函数(您应该通过foo1->~Foo()手动调用)。但是,如果析构函数没有副作用,或者你可以立即处理它们,那么标准允许你不显式调用它

现在棘手的部分是,如果您使用STL集合,那么它们会在内部进行大量分配来存储它们的需求(尤其是像std::mapstd::list这样的容器)。因此,您需要编写一个自定义的allocator<T>,它封装了一个高效的池化方案。

如果您想要高效的freeing内存,只需执行一次删除即可。不过请记住,释放内存并不是delete调用所做的唯一事情。它还调用析构函数。如果不是琐碎的或不可见的,编译器仍然必须通过函数调用来调用它。

也就是说,尽可能使用std::vector。我已经在vector上编写了自定义集和映射,功能较少(无需移除),以获得内存和性能。

如果你有很多小物体,比如一个向量,通常取1,2。。。16个元素,您可以通过使用更多内存来提高速度。boost小矢量和其他容器可以帮助您不进行分配。在算法中使用它,已经为我在现实世界中的代码节省了显著的百分比(>90%)。

最后,你不可能总是赢。如果您可以估计内存使用情况,或者已经很接近了,您可以使用[https://howardhinnant.github.io/stack_alloc.html](Howard Hinnant的堆栈分配器)。不要被这个名字骗了,你也可以在堆上分配内存。通过一些操作,我怀疑您应该能够将其更改为接受运行时大小。不过,它并不完美,你可以涵盖很大一部分。

也就是说,您总是可以故意创建内存泄漏,尽管这可能会消除析构函数的副作用。您可以从地图中提取节点,并将它们存储在分配的向量中。这可能会进入UB,我不是这方面的专家。

哦,最后,您可以从标准分配器继承,并且只覆盖deallocate函数。查看全局以决定是否要调用实际的解除分配。翻转开关,在出口处进行内存泄漏。