C++ C++;:删除对象而不调用析构函数

C++ C++;:删除对象而不调用析构函数,c++,c++17,destructor,c++pmr,C++,C++17,Destructor,C++pmr,对于那些不熟悉多态内存资源(PMR)的人 : “类std::pmr::monotonic_buffer_resource是一种特殊用途的内存资源类,仅当资源被销毁时才释放分配的内存。它用于在内存用于构建几个对象,然后一次释放的情况下进行非常快速的内存分配。” 因此,如果析构函数的唯一目的是释放内存,那么在单调缓冲区资源上调用它是没有意义的: auto mbr=std::make_unique(); 自动向量=std::pmr::vector(mbr.get()); 向量。调整大小(1'000'0

对于那些不熟悉多态内存资源(PMR)的人

:

“类
std::pmr::monotonic_buffer_resource
是一种特殊用途的内存资源类,仅当资源被销毁时才释放分配的内存。它用于在内存用于构建几个对象,然后一次释放的情况下进行非常快速的内存分配。”

因此,如果析构函数的唯一目的是释放内存,那么在单调缓冲区资源上调用它是没有意义的:

auto mbr=std::make_unique();
自动向量=std::pmr::vector(mbr.get());
向量。调整大小(1'000'000,pmr_向量(100));//创建1M矢量,每个矢量包含100个整数
//重置向量
//这将导致不必要地调用1M析构函数
向量={};
由于所有内存都是从
mbr
中提取的,因此我只想销毁缓冲区。但是,这不会阻止调用
向量
”析构函数并尝试释放已释放的内存

一个非常非常糟糕的方法是调用
std::memset(&vectors,0,sizeof(vectors))。这将性能提高2.8倍,但对(现已删除)答案的评论强烈同意不应这样做


因为标准库还没有完全支持PMR,所以我在这里提供了一个基于boost的完整示例:-它需要-lboost_container

我不熟悉多态容器和分配器,但我怀疑问题是所有这些向量都使用不同的分配器。只有您的主服务器使用您的
mbr
。所以你真的有100000个不同的游泳池。我想这不是你想要的

所以试试这个:

// vectors of 1 element each as your actual code (see note bellow)
vectors.resize(1000000, pmr_vector<int>(1, 100, mbr.get())); 

// or
// vectors of 100 elements each as your comment (see note bellow)
vectors.resize(1000000, pmr_vector<int>(100, 0, mbr.get())); 
//每个元素的向量作为实际代码(参见下面的注释)
vectors.resize(1000000,pmr_vector(1100,mbr.get());
//或
//每100个元素的向量作为您的注释(参见下面的注释)
vectors.resize(1000000,pmr_vector(100,0,mbr.get());
除了所有100001个向量现在共享相同的
单调缓冲\u资源
之外,这做了同样的事情。现在,您只使用一个池,当您“重置”mbr时,该池将被释放


注意:你的评论是错误的<代码>向量。调整大小(1'000'000,{100})
创建了
1'000'000
向量,每个向量有一个值元素
100

t.niese在评论中提出了一个很好的建议,那就是更改容器的分配器。毕竟,我们的问题不在于容器,而是分配器认为它需要调用
do\u deallocate
。我们可以简单地复制boost的
多态分配器的代码
,并用no-op替换
解除分配
。您可以找到完整的代码。最后,它只是从中复制,并替换为以下内容:

void解除分配(T*p,size\T n)无例外{}
我们可以通过使用打印内存资源(参见此处或上面链接的要点中的一个)来验证这一点。如果我们使用原始的多态分配器分配3个内部向量,每个向量包含100个条目,我们会看到

my_vector vectors{mbr.get()};
vectors.resize(3,my_vector(100));
//马洛克96
//马洛克400
//马洛克400
//马洛克400
//免费400
//免费400
//免费400
//免费96
注意,我们不需要将
mbr
传递到内部向量中。这已经完成了。使用新的
无空闲分配器
,结果仅为

// malloc 96
// malloc 400
// malloc 400
// malloc 400
正如预期的那样,内存不再被释放,但对象仍然被正确解构。让我们看看这是如何改变解构主义的表现的。整个基准也包含在中。注意,我更改了
单调\u buffer\u资源的内部向量和预分配内存的大小。这是为了使效果更加明显,并特别强调PMR和MBR的好处。以下是结果(
g++10-O3
):

多态分配程序 无空闲分配器 使用默认资源调整大小 47910 62574 使用单调缓冲区资源调整大小 30298 30947 使用默认资源重置 12298 3(但有泄漏) 使用单调缓冲区资源重置 5463 2520
从语言的角度来看,我不认为调用1M析构函数是“不必要的”,你是对的。从执行的角度来看,它们没有什么区别,这就是为什么我称它们为“不必要的”。一旦MBR被删除,它们就会以某种方式消失。如果向量存储的是一种非平凡的可破坏类型,那么情况就不同了。调用析构函数不是问题,而是析构函数的作用。析构函数不仅释放内存,还可能结束托管对象的生存期。我不知道
pmr
,但听起来您需要更改容器的分配器。是否在启用优化的情况下完成性能度量?
vectors.resize(1'000'000,{100});//创建1M矢量,每个矢量有100个整数
您确定这就是这里要做的吗?它更像是包含单个值的1M向量
100
。PMR/MBR的工作原理略有不同。传递到外部向量的分配器隐式传播到内部向量。原始代码已经对所有内部向量使用了相同的MBR。因此,它们的取消分配已经是不可操作的,但它仍然需要一个虚拟方法调用。这是因为多态分配器也可能指向另一个内存资源,其
do\u deallocate
方法不是no-op.@mrks我认为你错了。复制向量时将复制分配器。但在这种情况下,您将创建新的向量。新向量与原始向量没有任何关系,也不共享分配器。下面的示例显示了内存资源是如何隐式传递的:。它返回320/400/400/…,表明test_资源用于外部和内部容器。硒