std::unique_ptr是底层对象的两倍大
std::unique_ptr twice as big as underlying object
我遇到了std::unique_ptrs的问题(特别是MSFT VS 10.0实现(。当我创建一个它们的std::列表时,我使用的内存是创建一个仅包含底层对象的std::列表时的两倍(注意:这是一个大对象——大约200字节,所以它不仅仅是一个额外的引用计数器(。
换句话说,如果我运行:
std::list<MyObj> X;
X.resize( 1000, MyObj());
我的应用程序需要的内存是我运行时的一半:
std::list<std::unique_ptr<MyObj>> X;
for ( int i=0; i<1000; i++ ) X.push_back(std::unique_ptr<MyObj>(new MyObj()));
我已经检查了MSFT的实现,我没有看到任何明显的东西——有人遇到过这种情况,有什么想法吗?
编辑:好的,更清楚/具体一点。这显然是Windows内存使用问题,我显然缺少一些东西。我现在已经尝试了以下操作:
- 创建100000 MyObj的
std::list
- 创建100000 MyObj*的
std::list
- 创建100000 int*的
std::list
- 创建50000 int的
std::list
*
在每种情况下,列表中的每个add'l成员,无论是指针还是其他成员,都会使我的应用程序膨胀4400(!(字节。这是一个64位版本,不包含任何调试信息(Linker>debugging>Generate Debug Info设置为No(。
我显然需要对此进行更多的研究,以将其缩小到一个较小的测试用例中。
对于那些感兴趣的人,我将使用Process Explorer来确定应用程序的大小。
事实证明,这完全是堆碎片。真可笑。每8字节对象4400字节!我改为预分配,问题完全消失了——我习惯了依赖每个对象分配的低效率,但这太荒谬了。
MyObj实现如下:
class MyObj
{
public:
MyObj() { memset(this,0,sizeof(MyObj)); }
double m_1;
double m_2;
double m_3;
double m_4;
double m_5;
double m_6;
double m_7;
double m_8;
double m_9;
double m_10;
double m_11;
double m_12;
double m_13;
double m_14;
double m_15;
double m_16;
double m_17;
double m_18;
double m_19;
double m_20;
double m_21;
double m_22;
double m_23;
CUnit* m_UnitPtr;
CUnitPos* m_UnitPosPtr;
};
增加的内存可能是由于堆效率低下——由于内部碎片和malloc数据,您必须为分配的每个块支付额外的费用。您执行的分配量是将产生惩罚命中的分配量的两倍。
例如:
for(int i = 0; i < 100; ++i) {
new int;
}
将使用比以下更多的内存:
new int[100];
即使分配的金额是相同的。
编辑:
在Linux上使用GCC使用unique_ptr时,我使用的内存增加了约13%。
std::list<MyObj>
包含对象的N个副本(+列表指针所需的信息(。
std::unique_ptr<MyObj>
包含一个指向对象实例的指针。(它应该只包含一个MyObj*
(。
因此,std::list<std::unique_ptr<MyObj>>
并不直接等同于您的第一个列表。std::list<MyObj*>
应该给出与std::unque_ptr
列表相同的大小。
在验证了实现之后,唯一可以嵌入对象本身指针旁边的东西可能是"deleter",在默认情况下,它是一个调用operator delete
的空对象。
您有调试或发布版本吗?
这不是一个答案,但它不适合作为注释,而且可能是说明性的。
我无法复制索赔(GCC 4.6.2(。取此代码:
#include <memory>
#include <list>
struct Foo { char p[200]; };
int main()
{
//std::list<Foo> l1(100);
std::list<std::unique_ptr<Foo>> l2;
for (unsigned int i = 0; i != 100; ++i) l2.emplace_back(new Foo);
}
仅启用l1
生成(在Valgrind中(:
total heap usage: 100 allocs, 100 frees, 20,800 bytes allocated
仅启用l2
,循环给出:
total heap usage: 200 allocs, 200 frees, 21,200 bytes allocated
智能指针正好占据4 时代 100字节。
在这两种情况下,/usr/bin/time -v
给出:
Maximum resident set size (kbytes): 3136
此外,pmap
在两种情况下都显示:total 2996K
。为了确认,我将对象大小更改为20000,将元素数量更改为10000。现在的数字是198404K
和198484K
:正好有80000B的差异,每个唯一指针8B(可能在列表的分配器中有一些8B对齐(。在相同的变化下,time -v
报告的"最大驻留集大小"现在是162768
与164304
。
- 重复使用预分配的向量<复杂<double>>作为<double>长度两倍的向量
- 计算C++两倍的差值
- 为什么当我进入退出条件时,程序会打印两倍的默认条件而不是退出 while 循环?
- 内存移动的性能与memcpy相比是两倍?
- Qt 是 JSON 精度的两倍
- 不完整的多线程光线追踪器花费的时间是预期的两倍
- C++随机函数给出的相同整数是输出的两倍
- 为什么我的最终向量是它应该大小的两倍并且具有前导 0?
- 为什么即使在从int到两倍的类型施放后,小数也没有小数
- 如何将固定精度的两倍格式化为给定的长度
- getsockopt() 返回的值是之前由 setsockopt() 设置的值的两倍
- MF SinkWriter mp4文件的播放持续时间是添加音频样本时的一半,图像的播放速度也是添加音频样本的两倍
- 无论如何,是否可以使用setCursorPos(int,int)函数,但没有采用两个int使它需要速度较慢的两倍
- 是一个包含两个INT(只有两个INT)的结构,保证是大小(INT)的两倍
- C++ "time.h"测量的运行时间是实际值的两倍
- 退出申请致电两倍
- SSE42 & STTNI - PcmpEstrM比PcmpIstrM慢两倍,是真的吗?
- 在同一台机器上,是否保证两倍值的不精确性是一致的
- 处理数据的方法,速度是我处理速度的两倍
- C++11 getline 要求输入的两倍