9
#include <vector>
typedef std::vector<char> vc;
typedef std::vector<vc> vvc;

vvc f() {
  const int N = (1 << 15);
  return vvc(N, vc(N)); // 1 GB
}

int main () {
  vvc v; 
  v = f();
  while(true);  // Why 2GB allocated ?
  return 0;
}

使用 clang -O2 和 g++ -O2 编译。相同的行为。

编辑:有多种方法可以修复某些答案中指出的此代码。但我的意思是理解这段代码。当然有一个临时对象,但它应该在分号处消失,并且应该向系统返回 1GB 内存。该问题旨在询问为什么它没有发生。

编辑2:临时对象的析构函数确实在分号之前调用。

4

5 回答 5

4

您的代码中有分配,我们有充分的理由徒劳地反对;-)

如果您使用初始化,则允许 RVO 优化,并且正如其他人所试验的那样,它实际上似乎也可以工作。

通过赋值,函数返回一个对象,然后必须将其复制到目标,然后才对临时对象进行核对。虽然使用数据流分析可能会对其进行优化,但这种情况很难捕捉并且被认为很少使用。因此,您在所有其他方面都遭受了性能损失。

编辑:为了看到临时文件确实在适当的时候被核了,我建议使用调试器和单步进入函数。或者查看总成列表。对于抽象机,dtor 调用必须发生在下一条指令之前。

但是,优化的代码更自由地使用“好像”规则。如果代码无法区分,它可能会推迟一点,您可能会体验到这种效果。

C++11 注意:在 C++11 中,vector 获得了另一个从右值移动的 op=。这将治愈这种特殊情况,但是所有编译器都需要时间来缩小差距,尤其是所有类都获得了移动的东西。寻找移动的 Pre-11 代码将使用 swap() 代替 =。

于 2013-06-09T10:17:30.620 回答
4

我的猜测是您正在查看操作系统显示分配给进程的内存量,并且您的编译器尚不支持 C++11 的移动分配。

因此,发生的情况可能如下:

  • 在您的函数中,您分配向量(消耗 1GB 内存)。
  • 然后,在您的作业中,复制该向量(消耗另一个 1GB 内存,与第一个块相邻)。
  • 之后,原始向量被破坏。这会释放内存以供程序中的未来分配使用,但是由于该内存位于保留向量的内存之前,并且对于大多数操作系统来说,分配给进程的内存必须是连续的,因此进程无法将其还给操作系统.

因此结果是操作系统为您的进程分配了 2GB,其中 1GB 分配给了向量,1GB 可用于您的程序中的未来分配(但不能分配给另一个进程中的分配)。

于 2013-06-09T10:17:39.650 回答
2

我刚刚在win32、vc7上测试过。

您的代码分配 2Gb。如果改成这样:

int main () {
  vvc v = f();
  while(true);

它只需要 1Gb。

我想原因 - 是向量之间的复制操作。

v = f()- 调用构造函数。您的案例 - 默认 c'tor 并将(运算符=)复制到空对象中。应对需要 2 Gb。

f()(创建向量和返回)的内部操作可以使用RVO,并且没有任何应对和额外分配。

于 2013-06-09T09:58:08.377 回答
1

编辑:有多种方法可以修复某些答案中指出的此代码。但我的意思是理解这段代码。当然有一个临时对象,但它应该在分号处消失,并且应该向系统返回 1GB 内存。该问题旨在询问为什么它没有发生。

在 Windows 上,我使用 gcc 编译了您的测试并运行。我看到 的值Private Bytes在程序启动后开始增加,然后开始减少,最后停止变化。所以在这个操作系统内存被返回给系统。顺便说一句,我利用Process Explorer来获取有关Private Bytes.

我假设您在 Linux 上进行了测试,并且在此操作系统上的内存未返回系统。如果不使用它,如果我是对的,它最终会移动到分页区域。

于 2013-06-09T11:25:59.213 回答
0

我认为返回值将被复制。比临时的 2 个 vvc 副本存在。每个有 2^30 个字符(= 1GB)

据我所知,允许但不需要编译器删除复制操作

我找到了这个链接 http://en.wikipedia.org/wiki/Return_value_optimization

于 2013-06-09T10:02:27.870 回答