在std容器上执行一系列操作时,最大限度地减少峰值内存使用

Minimizing peak memory usage while performing a series of operations on std containers

本文关键字:内存 最大限度 std 执行 操作 一系列      更新时间:2023-10-16

我有一个生成最终结果和一些中间结果的函数"管道"。我正在寻找一种方法,通过清理中间结果,一旦他们不再需要减少峰值内存。

如果我不关心中间结果的内存,这就是它的样子。B、C、D、E和F都是std容器,每个容器都有超过一百万的对象。

void A::getF(F &f) {
  B b;
  C c;
  computeBAndC(b, c);
  D d;
  computeD(b, c, d);
  // b and c no longer needed
  E e;
  computeE(d, e);
  // d no longer needed
  computeF(e, f);
}

这些是我想到的方法:

。一旦不再需要,使用newdelete进行清理。但是,对std容器使用new和delete有意义吗?

void A::getF(F &f) {
  B *b = new B;
  C *c = new C;
  computeBAndC(b, c);
  D *d = new D;
  computeD(b, c, d);
  // b and c no longer needed
  delete b;
  delete c;
  E e;
  computeE(d, e);
  // d no longer needed
  delete d;
  computeF(e, f);
}

B。使用std容器的swap函数在使用后清除其内存

void A::getF(F &f) {
  B b;
  C c;
  computeBAndC(b, c);
  D d;
  computeD(b, c, d);
  // b and c no longer needed
  B().swap(b);
  C().swap(c);
  E e;
  computeE(d, e);
  // d no longer needed
  D().swap(d);
  computeF(e, f);
}

C。使用块。

void A::computeF(F &f) {
  E e;
  {
    D d;
    {
      B b;
      C c;
      computeBAndC(b, c);
      computeD(b, c, d);
      // b and c no longer needed
    }
    computeE(d, e);
    // d no longer needed
  }
  computeF(e, f);
}

D。重新构造,以便在函数作用域结束时删除中间结果:

void A::getF(F &f) {
  E e;
  getE(e);
  computeF(e, f);
}
void A::getE(E &e) {
  D d;
  getD(d);
  computeE(d, e);
}
void A::getD(D &d) {
  B b;
  C c;
  computeBAndC(b, c);
  computeD(b, c, d);
}

这些方法的优缺点是什么?这些真的会降低内存使用峰值吗?有没有更好的办法?

老实说,我会选择选项D,因为考虑到您的一些变量之间存在一些联系,将这些代码提取到函数中不仅可以解决您的内存问题,而且还可以更好地记录您使用代码本身的意图(我相信您可以在代码中提出良好的自解释名称)。唯一的问题当然是性能,如果你使用一个更好的方法来函数签名(假设你不控制compute函数:即

)
E A::computeE(const D& d) {
    E e;
    computeE(d, e);
    return E;
}

我从你的评论中了解到你只能使用没有移动语义的c++ 98。然而,编译器长期以来一直能够执行命名返回值优化-在您的情况下,我会尝试测试您的编译器使用NRVO(和复制省略)的能力,如果它是好的-使用我概述的更自然的(IMO)签名-否则我会使用您的选项d

如果您在这些容器中存储指向对象的指针,那么删除它们(或者只是让它们超出作用域)不会释放对象正在使用的内存,只会释放容器本身的内存。您还必须删除容器中的每个对象。

否则,在STD容器上使用new和delete是完全有意义的。这也意味着在堆上而不是堆栈上为它们分配内存,这可能是明智的,因为虽然堆栈有利于快速分配,但如果在堆栈上转储太多,可能会耗尽内存。您可能还会遇到分页问题。

然而,要真正解决你的问题,更多地了解你在做什么会有所帮助。你真的需要使用性病容器吗?你能用数组代替吗?容器中的对象对每个容器来说是唯一的吗,还是它们是被重新安排到不同容器中的同一堆对象?有没有办法把操作分解成更小的数据集,而不是像那样在一系列容器中运行?