把它放在堆上还是栈上?

3

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

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 是在使用“堆”吗? - Lightness Races in Orbit
2
它在糟糕地使用堆。以一种几乎没有堆分配优势的方式。 - bdonlan
1
@bdonlan 这就是重点,唯一的优势在于我不使用堆栈空间,因为缓冲区是一个局部变量。所以我开始放置 char buffer[10000...],每个后续的零都让我感到担忧。 - tomasz
1
@tg:只是不要称它为堆。称其为动态分配内存更为恰当,这与其实际类型(自动/静态/动态/线程)相符。 - Martin York
@Steve:即使是我,也会选择自己的战斗。 :) - Lightness Races in Orbit
显示剩余5条评论
4个回答

4
这两个函数完全不同。第二个函数对于所有调用都使用相同的缓冲区,而第一个函数使用新的缓冲区,这意味着第二个函数不是线程安全的。
风格可怕。如果write_fancy_things只使用了X个字节,并且X在编译时是未知的,则动态分配X个字节。不要在堆栈上分配一些希望的最大大小,也不要使用更大的希望的静态缓冲区。使用正确类型的向量,将其调整为适当的大小,然后使用该缓冲区。

+1. 使用静态指针动态分配静态大小的缓冲区毫无意义。 - Fred Foo
也许我在伪代码中过于简化了一些事情... write_fancy_things即将说,我将写入1到MAX_SIZE字节之间的某些内容,所有值都是可能的,所有值都会被使用,但主要是使用小值。关于不支持线程安全的好点子,但这里真的不需要考虑线程安全问题。 - tomasz
如果主要是小的分配,为什么不让write_fancy_things进行分配呢?而且MAX_SIZE有多大? - bdonlan

2

将数据放在堆栈上不会受到惩罚,因为您只是减少了堆栈指针。

可以使用操作系统实用程序修改堆栈大小,因此我不会担心大约1MB的大尺寸。

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


1
如果你开始担心大小,就用堆,否则用栈。
总之。

0
如果因为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的堆栈,这不是一种滥用吗? - tomasz
@tg:一个选择是将问题上交 - 记录库调用需要100kb缓冲区,如果用户愿意,则让用户提供它,并在其他情况下动态分配它。然后,任何能够通过在堆栈上传递缓冲区来测量其应用程序的整体性能改进的用户都会感到高兴。如果实际使用的空间量是不可预测的,由MAX_SIZE限制,但通常要小得多,则您可能不想使用vector,因为它非常缓慢地对缓冲区进行零初始化,而是使用scoped_array或类似的东西。 - Steve Jessop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接