c++ 中 std::vector 的内存问题

Memory issue with std::vector in c++

我在 C++ 中遇到 std::vector 的内存问题。这是我的代码:

#include <iostream>
#include <vector>
int main () {

  std::vector< std::vector<float> > mesh_points_A;

  int N=10;

  for(int i=0;i<N;i++){ 
    for(int j=0;j<N;j++){ 
      std::vector<float> xyz;
      xyz.push_back(i);
      xyz.push_back(j);
      xyz.push_back(0.3);
      mesh_points_A.push_back(xyz);
    }
  }

  return 0;
}

当我将 N 增加到 10000 或更高时,我 运行 内存不足......但我认为我做的事情完全错误,因为如果我将 python 与numpy 数组这很容易实现...

非常感谢。

编辑:这是原始代码。上面的代码只是为了更好地举例说明问题而进行的简化。问题是,是否有可能在保持 N=10000.

的同时,在不 运行 内存不足的情况下创建许多 Surface 对象(在代码中目前有两个)
// classes example compile with c++ -o Surface Surface.cpp -std=c++11

#include <iostream>
#include <vector>
#include <array>

class Surface {
private:
  std::vector< std::array<float,3> > mesh_points_A; 

public:
  float R;
  float z; // z position if the suface
  int n_A; //number of area mesh points mesh_points_A.size()
  Surface(int nxA, float R , float z); 
};

Surface::Surface(int nxA, float  R,float z)
  : z(z)
  , R(R)
{

  float dxA= 2*R/(nxA*1.0-1.0);
  //determine n_A, 
  n_A=0;
  for(int i=0;i<nxA;i++){ 
    float x = -R+i*dxA;
    for(int j=0;j<nxA;j++){ 
      float y = -R+j*dxA;
      if(x*x+y*y<R*R){
        n_A+=1;
      }
    }
  }
  std::cout<<"Number of area mesh points: "<<n_A<<std::endl;
  mesh_points_A.reserve(n_A);

  for(int i=0;i<nxA;i++){ 
    float x = -R+i*dxA;
    for(int j=0;j<nxA;j++){ 
      float y = -R+j*dxA;
      if(x*x+y*y<R*R){
        std::array<float,3> xyz{ {x,y,z} };
        mesh_points_A.push_back(xyz);       
      }
    }
  }


}



int main () {
  int N= 20000;
  Surface s1(N,0.1,0.0);
  Surface s2(N,0.1,0.1);
  return 0;
}

您的矢量需要连续重新分配更多内存以保持增长。它通过保留一个新的、更大的内存区域并复制旧数据来实现这一点。这取决于实现保留了多少内存,但典型的策略是分配两倍的内存(libstdc++ 这样做)。

这意味着,在最坏的情况下,您的总内存需求可能接近原始内存需求的三倍

假设您的向量当前包含 90,000,000 个元素,它的容量——不幸的是——也是 90,000,0001。为了插入第 90,000,001 个元素,std::vector 现在保留两倍的内存 — 180,000,000,复制所有旧元素,然后破坏旧数组。

因此,即使您“只”需要 100,000,000 个元素,您也不得不短暂地为 270,000,000 个元素分配存储空间。这相当于大约 9.10 GiB,即使您的 100M 向量只需要 3.35 GiB。

可以通过在嵌套初始化循环前面放置以下行来巧妙地避免这种情况:

mesh_points_A.reserve(N * N);

1更实际一点,容量可能是2的幂,例如226 = 67,108,864;调整大小仍然需要 6.75 GiB 的内存。

std::vector 具有根据您的需要动态更改大小的灵活性。一如既往,灵活性是有代价的。通常这个价格很小,很容易被忽略,但在这种情况下,当你使用 std::vector<float>std::array<float,3> 时,差异非常大,因为你有 1 亿个元素。例如,如果我们 运行 此代码:

 std::vector<float> v;
 for( auto f : { 1.0, 2.0, 3.0 } ) v.push_back(f);
 std::cout << sizeof(v) << "-" << v.capacity() << std::endl;
 std::cout << sizeof(std::array<float,3>) << std::endl;

live example

我们可以看到,在这个平台上 std::vector<float> 本身需要 24 个字节加上它为 4 个浮点数动态分配内存 - 16 字节与仅 3 个浮点数 - 12 字节,如果你使用固定大小的结构。所以在你的情况下,区别是:

1 std::vector - ( 24 + 16 ) * 100 000 000 = 4 000 000 000
2 std::array  - 12 * 100 000 000          = 1 200 000 000

2 800 000 000 或大约 2Gb 内存。

但这还不是结束 std::vector 还有另一个代价 - 它必须连续分配所有数据 space。通常在大小达到当前容量时重新分配容量。在这种情况下,这意味着创建此数据所需的内存可以轻松增加一倍以上 - 假设如果容量达到 5000 万并且向量需要重新分配,它将创建另一个内存块,比如 1 亿,同时保持之前的内存(所以你的记忆必须容纳 1.5 亿个元素)并复制它们。而且没有内存碎片问题。

因此推荐的解决方案是将 std::array<float,3> 用于内部数据(或 struct 包含 3 个元素),将 std::deque 作为外部容器,或者如果您必须使用 std::vector 提前为足够的元素分配内存