Ошибка памяти с std :: vector в c ++ - PullRequest
       23

Ошибка памяти с std :: vector в c ++

0 голосов
/ 17 сентября 2018

У меня проблема с памятью с std :: vector в c ++.Вот мой код:

#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;
}

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

std::vector имеет возможность динамически изменять размер по вашему желанию. Как всегда, гибкость имеет цену. Обычно эта цена невелика и может быть легко проигнорирована, хотя в этом случае, когда вы используете std::vector<float> против std::array<float,3>, разница очень значительна, поскольку у вас есть 100 миллионов элементов. Например, если мы запустим этот код:

 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 000 000 или около 2 ГБ памяти.

Но это еще не все std::vector имеет другую цену - он должен размещать все данные в непрерывном пространстве. Обычно это делается перераспределением емкости, когда размер достигает текущего. В этом случае это означает, что потребность в памяти для создания этих данных может быть легко увеличена более чем вдвое - скажем, если емкость достигает 50 миллионов, а вектор нужно перераспределить, это создаст еще один блок памяти, скажем, 100 миллионов при сохранении предыдущего (так Ваша память должна содержать 150 миллионов элементов) и скопировать их. И это без проблемы фрагментации памяти.

Поэтому рекомендуемое решение - иметь std::array<float,3> для внутренних данных (или struct с 3 элементами) и либо std::deque в качестве внешнего контейнера, либо, если вам нужно использовать std::vector, выделите память для достаточного количества элементов заранее.

0 голосов
/ 17 сентября 2018

Ваш вектор должен последовательно перераспределять больше памяти, чтобы продолжать расти. Это достигается за счет резервирования новой, большей области памяти и копирования старых данных. От реализации зависит, сколько зарезервировано больше памяти, но типичная стратегия состоит в том, чтобы выделить вдвое больше памяти (это делает libstdc ++).

Это означает, что в худшем случае ваше общее требование к памяти может быть близко к в три раза по сравнению с вашим необработанным требованием к памяти:

Допустим, ваш вектор в настоящее время содержит 90 000 000 элементов, и его емкость - по счастливой случайности - также составляет 90 000 000 1 . Чтобы вставить 90 000 001-й элемент, std::vector теперь резервирует вдвое больше памяти - 180 000 000, копирует все старые элементы и затем уничтожает старый массив.

Следовательно, даже если вам «только» нужно 100 000 000 элементов, вам пришлось кратко выделить хранилище для 270 000 000 элементов. Это соответствует примерно 9,10 ГиБ, хотя вашему вектору 100М требуется только 3,35 ГиБ.

Этого можно аккуратно избежать, поместив следующую строку перед вложенным циклом инициализации:

mesh_points_A.reserve(N * N);

1 Более реалистично, емкость, вероятно, является степенью двойки, например, 2 26 = 67 108 864; это все еще 6,75 ГБ памяти для изменения размера.

...