【问题标题】:leave it on stack or put in on heap?把它放在堆栈上还是放在堆上?
【发布时间】:2011-10-07 03:52:16
【问题描述】:

函数使用本地缓冲区准备一些大小有限的数据块并将其传递给另一个函数,就像这样:

void foo()
{
  char buffer[MAX_SIZE];
  size_t size = write_fancy_things(buffer);
  bar(buffer, size);
}

但是,根据 MAX_SIZE 的值,您可能会担心会吃掉太多的堆栈,并用类似于以下示例的代码替换代码(但希望更关心内存管理):

void foo()
{
  static char *buffer = new char[MAX_SIZE]; 
  size_t size = write_fancy_things(buffer);
  bar(buffer, size);
}

一般情况下,这两个函数的行为应该相同。但是,在第一个示例中,如果MAX_SIZE 太大,我们更有可能达到堆栈限制。如果您知道函数的使用位置,使用较大的值可能会很好,但有时您不知道。

在第二个示例中,我们正在处理额外的间接寻址,并且缓冲区更容易出现 CPU 缓存未命中,如果foo 位于低延迟关键路径上,并且我们预计准备缓冲区的成本为在大多数情况下非常低。

您认为多大的尺寸会太大而无法堆叠?将大块数据放入堆栈但只使用其中的一小部分是否有任何惩罚?

编辑:write_fancy _things 只是说*我正在将一些数据写入缓冲区的同义词,介于 1 和 MAX_SIZE 字节之间*。您可以将第二个foo 示例视为类方法,将静态指针视为在构造函数中分配的类成员。我可能只是过度简化了一些事情,但不想引入比需要更多的复杂性并专注于堆栈问题。

【问题讨论】:

  • 确定您的new 正在使用“堆”吗?
  • 它正在使用堆。很糟糕。以一种几乎没有任何堆分配优势的方式。
  • @Tomalak 现在我不是;如果不是堆比什么?
  • @bdonlan 这就是重点,唯一的好处是我没有使用堆栈空间,我更喜欢堆栈空间,因为缓冲区是一个局部变量。所以我开始输入char buffer[10000...,后面的每一个零都让我担心。
  • @tg:只是不要称它为堆。称它为动态分配的内存。它符合它的实际类型(自动/静态/动态/线程)。

标签: c++ heap-memory cpu-cache stack-memory


【解决方案1】:

两者甚至不是完全一样的。第二个对所有调用使用相同的缓冲区,第一个使用新的缓冲区,这意味着第二个不是线程安全的。

风格太可怕了。如果 write_fancy_things 只使用 X 个字节,并且 X 在编译时未知,则动态分配 X 个字节。不要在堆栈上分配一些有希望的最大大小,也不要使用更大的有希望的最大大小的静态缓冲区。使用正确类型的向量,将其调整为适当的大小,然后使用该缓冲区。

【讨论】:

  • +1。使用static 指针动态分配静态大小的缓冲区没有任何用处。
  • 好吧,也许我在我的伪代码中过于简化了...... write_fancy_things 正要说,我要写一些介于 1 和 MAX_SIZE 字节之间的东西,所有值都是可能的,都会被命中,但主要会使用一个小的。不是线程安全的好点,但这里真的不是这样。
  • 如果主要是小分配,为什么不让write_fancy_things做分配呢? MAX_SIZE 有多大?
【解决方案2】:

将数据放入堆栈没有任何惩罚,因为您只是在递减堆栈指针。

可以使用 OS 实用程序修改堆栈大小,因此我不必担心高达 1MB 左右的大尺寸。

但我会担心递归调用。它们可能根本不会发生,因为它们会炸毁堆栈。

【讨论】:

    【解决方案3】:

    如果您开始担心大小,请选择堆,否则选择堆栈。

    考虑所有因素。

    【讨论】:

      【解决方案4】:

      如果出现与帧边界冲突的问题(因为 MAX_SIZE 太大了),则转换为 std::vector

      void foo()
      {
        std::vector<char> buffer(MAX_SIZE);
        size_t size = write_fancy_things(&buffer[0]);
        bar(&buffer[0], size);
      }
      

      坦率地说,你的版本是一个非首发。

      【讨论】:

      • 虽然这可能是最安全、最灵活的方法,但速度很慢。正如我所指出的,foo 位于低延迟关键路径上,并且堆栈缓冲区工作得非常好。但是,由于我的代码是库的一部分,将被其他人使用,在使用时不是某种滥用,比方说,在一次调用中使用 100KB 的堆栈吗?
      • @tg:一种选择是将问题放在楼上——记录库调用需要 100kb 缓冲区,如果他们愿意让用户提供,然后动态分配否则。然后,任何可以通过在堆栈上传递缓冲区来衡量其应用程序整体性能改进的用户都会感到高兴。如果实际使用的空间量不可预测,以MAX_SIZE 为界,但通常要小得多,那么您可能不想使用vector,因为它非常缓慢地将缓冲区零初始化,而不是scoped_array 或类似的。
      猜你喜欢
      • 2010-11-06
      • 2015-08-13
      • 2015-07-26
      • 2011-01-04
      • 2014-03-04
      • 1970-01-01
      • 2011-04-08
      • 2016-12-27
      • 1970-01-01
      相关资源
      最近更新 更多