为什么我要使用placement new

Why should I use placement new?

本文关键字:placement new 我要 为什么      更新时间:2023-10-16

看起来,placement new在预先分配的内存上创建了一个新对象,这是否意味着它将花费更少的时间?看起来它比使用旧的普通new进行分配更快。那么,如果这样既方便又快捷,为什么不一直使用placement new呢?

正常(非置换)new基本上等同于进行

T* ptr = static_cast<T*>(malloc(sizeof(T)));
new(ptr) T;

当然,由于错误检查等原因,实际情况看起来有点不同,但结果或多或少是相同的(由于不完全相同,您不能以这种方式分配指针delete,相反,您需要显式调用析构函数(ptr->~T()),然后使用free释放内存)。

因此,新放置确实应该比非新放置更快,因为它不需要分配内存。然而,问题是内存需要分配到某个地方。因此,您实际上已经将对new的一个调用替换为对placement new的调用和某个位置的分配代码(如果不是,为什么首先要使用new?)。很明显,这样做不那么方便,而且更容易出错。

现在,您当然可以编写一个更快的分配方法,但为此,您通常需要进行某种权衡。如果不使用更多的内存(额外的数据用于更快地识别空闲块)或使其非常具体(编写单个对象大小的快速分配要比一般对象大小容易得多),那么编写一个更快的分配器就不容易了。最终,它通常不值得付出努力(对于可能已经完成的值得付出努力的场景,因此您可以使用现有分配器(可能在内部使用placement new))。

当然,placement new有一些用途(有时确实预先分配了内存),但

并不是常见的情况。对于大多数程序来说,这是不必要的,因为它们的使用模式没有必要。对于不经常使用堆的程序来说,这没有什么区别,而且很难做到正确(也就是说,比你的操作系统更好)。你也只能通过优化你的分配来获得这么多。在大多数情况下,任何算法优化都会导致更大的总体加速。通常不需要定制分配器所能提供的许多保证(通过预先分配的内存保证分配的时间限制,低内存碎片)。

肯定有一些程序可以从自己的内存管理中受益,它们很难识别。在你发现内存分配实际上是一个瓶颈之后,就更难找到更好的分配方案了。当所有这些都完成了,它仍然不值得麻烦。

placement new的目的之一是使用自定义分配器来创建新对象,并调用它们的构造函数。它并不总是更快,因为它只和您的自定义分配器一样快。

用于将对象放置在内存中特定位置的Placement new可能需要更少的时间,因为在这一步中您实际上避免了分配内存

然而,它一定是在某个时刻分配的,在此之前可能需要时间。若您确实有理由将对象放置在预先分配的内存中,那个么使用它是有意义的。

这不是这种运算符的单一用途。更多详细信息请点击此处。

另外,请记住placementnew不会自动调用析构函数!您必须手动为您的Foo foo;执行foo->~Foo();

我发现的唯一一个新放置可以使您的分配速度显著加快的地方是,如果您有大量相同大小的对象,它们的使用寿命有限,导致它们经常被分配和销毁。如果不能保证这种类型的行为,那么最好使用默认的新实现。

需要大量相同大小对象的应用程序通常可以从池(或大容量)分配中看到显著的加速。基本上,您会为该对象分配一个大的缓冲区(或页面),然后在请求该对象时在其中调用placement new。虽然这可以大大加快速度,但对于大多数程序来说,这并不是真正必要的。

尝试为那些并不真正需要它的程序这样做可能只会给你带来最小的加速,但可能会花费你很多小时的调试时间。

所以,认真考虑你需要什么;如果你正在分配大量相同的对象,是的,放置新对象可能会更快。但只有几个物体?我不会打扰的。

不过,这并不总是时间问题。例如,可以使用placementnew来保证对象在堆上对齐。你可以做一些类似的事情:

void* buffer = aligned_malloc(sizeof(Object), 16); 
Object* object = new (buffer) Waypoint();

这对于某些类型是必要的,例如要与SSE函数和寄存器一起使用的浮点数组。