当前位置:  开发笔记 > 编程语言 > 正文

我如何使用大小的运算符删除/删除[],为什么它们更好?

如何解决《我如何使用大小的运算符删除/删除[],为什么它们更好?》经验,为你挑选了2个好方法。

C++ 14引入了"大小"版本operator delete,即

void operator delete( void* ptr, std::size_t sz );

void operator delete[]( void* ptr, std::size_t sz );

通过N3536阅读,似乎引入了这些运营商以提高性能.我知道典型的分配器operator new"存储"大容量内存的大小,这就是典型的operator delete"知道"多少内存返回到免费商店.

我不确定为什么"大小"版本operator delete会在性能方面有所帮助.唯一可以加快速度的是从控制块开始减少关于大小的读取操作.这确实是唯一的优势吗?

其次,我该如何处理阵列版本?AFAIK,分配的数组的大小不是简单的sizeof(type)*number_elements,但可能会分配一些额外的字节,因为实现可能会使用这些字节作为控制字节.operator delete[]在这种情况下我应该传递什么"大小" ?你能提供一个简短的使用例子吗?



1> Jerry Coffin..:

首先处理你的第二个问题:

如果存在,则std :: size_t size参数必须等于传递给返回ptr的分配函数的size参数.

因此,可能分配的任何额外空间是运行时库的责任,而不是客户端代码.

第一个问题更难以回答.主要想法是(或者至少似乎是)块的大小通常不存储在块本身的旁边.在大多数情况下,会写入块的大小,并且在取消分配块之前不会再次写入.为了避免在使用块时数据污染高速缓存,可以单独保存.然后,当你去解除块时,大小将经常被分页到磁盘,所以重新读取它是非常慢的.

避免明确地显式存储每个块的大小也是相当普遍的.分配器通常具有用于不同大小的块的单独池(例如,从16左右到大约几千字节左右的2的幂).它将从操作系统为每个池分配一个(相当)大块,然后将该大块的块分配给用户.当你传回一个地址时,它基本上通过不同大小的池搜索该地址,以找到它来自哪个池.如果每个池中有很多池和很多块,那可能会相对较慢.

这里的想法是避免这两种可能性.在典型情况下,您的分配/解除分配或多或少地与堆栈相关联,并且当它们是您分配的大小时,可能是在本地变量中.当您取消分配时,您通常会(或至少接近)与分配的位置相同的堆栈级别,因此相同的本地变量将很容易获得,并且可能不会被分页到磁盘(或类似的东西)因为附近存储的其他变量也在使用中.对于非数组形式,调用::operator new通常源于a new expression,以及::operator delete来自匹配的调用delete expression.在这种情况下,生成用于构造/销毁对象的代码"仅仅根据正在创建/销毁的对象的类型""知道"它将要请求(和销毁)的大小.



2> John Zwinck..:

对于sizeC++ 14 的参数,operator delete您必须传递与给定的大小相同的大小operator new,以字节为单位.但是你发现它对于数组来说更复杂.为什么它更复杂,请看这里:数组placement-new需要缓冲区中未指定的开销?

所以,如果你这样做:

std::string* arr = new std::string[100]

执行此操作可能无效:

operator delete[](arr, 100 * sizeof(std::string)); # BAD CODE?

因为原始new表达式等同于:

std::string* arr = new (new char[100 * sizeof(std::string)]) std::string[100];

至于为什么大小合适的deleteAPI更好,似乎今天它实际上不是,但希望是一些标准库将提高解除分配的性能,因为它们实际上不存储每个分配块旁边的分配大小(经典/教科书模型) ).有关详细信息,请参阅此处:C++ 1y中内存管理中的大小重新分配功能

当然,不在每个分配旁边存储大小的原因是,如果你真的不需要它,那就是浪费空间.对于进行许多小动态分配的程序(比它们应该更受欢迎!),这种开销可能很大.例如,在"普通vanilla" std::shared_ptr构造函数(而不是make_shared)中,引用计数是动态分配的,因此如果您的分配器存储它旁边的大小,它可能天真地需要大约25%的开销:分配器的一个"大小"整数加上的四槽控制块.更不用说内存压力了:如果大小没有存储在分配的块旁边,你可以避免在重新分配时从内存中加载一行 - 你需要的唯一信息是在函数调用中给出的(好吧,你还需要看看竞技场或自由列表或其他什么,但在任何情况下你都需要它,你仍然可以跳过一个负载).

推荐阅读
个性2402852463
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有