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

Memory issue with std::vector in c++

本文关键字:内存 问题 vector std C++      更新时间:2023-10-16

我在 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 数组一起使用,这很容易......

提前非常感谢。

编辑:这是原始代码。上面的代码只是一个简化,以更好地说明问题。问题是,是否可以以某种方式创建许多 Surface 对象(在代码中当前有两个(,而不会耗尽内存,同时保持 N=10000。

// 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;

现场示例

我们可以看到,在这个平台上,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 000000 或大约 2Gb 内存。

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

因此,建议的解决方案是为内部数据(或具有 3 个元素struct(std::array<float,3>std::deque为外部容器,或者如果您必须使用std::vector提前为足够的元素分配内存。