海量数据集中的内存优化

Memory optimization in huge data set

本文关键字:内存 优化 集中 数据集 数据      更新时间:2023-10-16

总之,我已经实现了一些功能,并且喜欢问一些基本的问题,因为我对C++没有很好的基础知识。我希望你们都能告诉我什么是我可以向你们学习的好方法。(请注意,这不是家庭作业,我周围没有任何专家可以问这个问题(

我所做的是;我从一个文件中读取输入的x、y、z、点数据(大约3GB的数据集(,然后为每个点计算一个值,并存储在向量中(结果(。然后,它将在下一个循环中使用。然后,这个向量将不再使用,我需要获得内存,因为它包含巨大的数据集。我想我可以通过两种方式做到这一点。(1( 只需初始化一个矢量,然后擦除它(参见代码-1(。(2( 通过分配一个动态内存,然后再取消分配(参见代码-2(。我听说这种取消分配是低效的,因为再次取消分配会消耗内存,或者可能我误解了。

Q1(我想知道在内存和效率方面优化的方式是什么。

Q2(另外,我想知道通过引用返回函数是否是给出输出的好方法。(请看代码-3(

代码-1

int main(){
    //read input data (my_data)
    vector<double) result;
    for (vector<Position3D>::iterator it=my_data.begin(); it!=my_data.end(); it++){
         // do some stuff and calculate a "double" value (say value)
         //using each point coordinate 
         result.push_back(value);
    // do some other stuff
    //loop over result and use each value for some other stuff
    for (int i=0; i<result.size(); i++){
        //do some stuff
    }
    //result will not be used anymore and thus erase data
    result.clear()

代码-2

int main(){
    //read input data
    vector<double) *result = new vector<double>;
    for (vector<Position3D>::iterator it=my_data.begin(); it!=my_data.end(); it++){
         // do some stuff and calculate a "double" value (say value)
         //using each point coordinate 
         result->push_back(value);
    // do some other stuff
    //loop over result and use each value for some other stuff
    for (int i=0; i<result->size(); i++){
        //do some stuff
    }
    //de-allocate memory
    delete result;
    result = 0;
}

代码03

vector<Position3D>& vector<Position3D>::ReturnLabel(VoxelGrid grid, int segment) const
{
  vector<Position3D> *points_at_grid_cutting = new vector<Position3D>;
  vector<Position3D>::iterator  point;
  for (point=begin(); point!=end(); point++) {
       //do some stuff         
  }
  return (*points_at_grid_cutting);
}

对于如此庞大的数据集,我完全避免使用std容器,而是使用内存映射文件。

如果您更喜欢使用std::vector,请使用vector::clear()vector::swap(std::vector())释放分配的内存。

erase不会释放用于矢量的内存。它减少了大小,但没有减少容量,因此向量仍然有足够的内存用于所有这些双打。

使内存再次可用的最佳方法是像您的代码-1一样,但让向量超出范围:

int main() {
    {
        vector<double> result;
        // populate result
        // use results for something
    }
    // do something else - the memory for the vector has been freed
}

如果不能做到这一点,清除矢量并释放内存的惯用方法是:

vector<double>().swap(result);

这创建了一个空的临时向量,然后它与result交换该向量的内容(因此result是空的,具有小容量,而临时向量具有所有数据和大容量(。最后,它破坏了临时的,带走了大的缓冲区

关于code03:通过引用返回动态分配的对象不是一种好的风格,因为它不会给调用者提供太多提醒,提醒他们有责任释放它。通常最好的做法是按值返回局部变量:

vector<Position3D> ReturnLabel(VoxelGrid grid, int segment) const
{
  vector<Position3D> points_at_grid_cutting;
  // do whatever to populate the vector
  return points_at_grid_cutting;
}

原因是,如果调用方使用对该函数的调用作为其自身向量的初始化,则会出现一种名为"命名返回值优化"的功能,并确保尽管您按值返回,但不会复制值。

不实现NRVO的编译器是一个糟糕的编译器,可能会出现其他各种令人惊讶的性能故障,但在某些情况下,NRVO不适用——最重要的是,当值由调用方分配给变量而不是在初始化中使用时。有三种修复方法:

1( C++11引入了移动语义,它基本上是通过确保临时的赋值是廉价的来进行排序的。

2( 在C++03中,调用者可以玩一个叫做"交换优化"的把戏。代替:

vector<Position3D> foo;
// some other use of foo
foo = ReturnLabel();

写入:

vector<Position3D> foo;
// some other use of foo
ReturnLabel().swap(foo);

3( 您编写了一个具有更复杂签名的函数,例如通过非常量引用获取vector并将值填充其中,或者将OutputIterator作为模板参数。后者还为调用者提供了更大的灵活性,因为他们不需要使用vector来存储结果,他们可以使用其他容器,甚至可以一次处理一个,而不需要一次存储整个批次。

您的代码似乎第一个循环中计算的值只在第二个循环中上下文无关地使用。换言之,一旦在第一个循环中计算出双值,就可以立即对其执行操作,而不需要一次存储所有值。

如果是这样的话,您应该以这种方式实现它。无需担心大量分配、存储或其他任何问题。更好的缓存性能。幸福

vector<double) result;
    for (vector<Position3D>::iterator it=my_data.begin(); it!=my_data.end(); it++){
         // do some stuff and calculate a "double" value (say value)
         //using each point coordinate 
         result.push_back(value);

如果"结果"向量最终将有数千个值,这将导致许多重定位。最好是用足够大的存储容量初始化它,或者使用保留功能:

vector<double) result (someSuitableNumber,0.0);

这将减少重新分配的次数,并可能进一步优化您的代码。

我也会写:vector<Position3D>& vector<Position3D>::ReturnLabel(VoxelGrid grid, int segment) const

像这样:

void vector<Position3D>::ReturnLabel(VoxelGrid grid, int segment, vector<Position3D> & myVec_out) const //myVec_out is populated inside func

您返回引用的想法是正确的,因为您希望避免复制。

` C++中的析构函数不能失败,所以解除分配不会分配内存,因为内存不能在无抛出保证的情况下分配。

另外:与其多次循环,不如以集成的方式进行操作,即不加载整个数据集,然后缩减整个数据集。只需逐个读入点,然后直接应用缩减,即代替

load_my_data()
for_each (p : my_data)
    result.push_back(p)
for_each (p : result)
    reduction.push_back (reduce (p))

只做

file f ("file")
while (f)
    Point p = read_point (f)
    reduction.push_back (reduce (p))

如果你不需要存储这些减少,只需按顺序输出即可

file f ("file")
while (f)
    Point p = read_point (f)
    cout << reduce (p)

code-1工作良好,与code-2几乎相同,没有主要优点或缺点。

code03其他人应该回答这个问题,但我相信在这种情况下,指针和引用之间的差异是微不足道的,不过我更喜欢指针。

话虽如此,我认为你可能从错误的角度来进行优化。你真的需要所有的点来计算第一个循环中一个点的输出吗?或者,你可以重写你的算法,只读取一个点,像在第一个循环中那样计算值,然后按照你想要的方式立即使用它吗?也许不是用单个点,而是用一批点。这可能会减少你的内存需求,只需少量增加处理时间。