这个问题可能听起来相当简单,但这是我与另一位开发人员的争论。

我小心地在我可以的地方进行堆栈分配,而不是堆分配。他和我说话,看着我的肩膀,并评论说,这是没有必要的,因为他们的表现是一样的。

在我的印象中,增加堆栈是一个常数时间,而堆分配的性能取决于当前堆的复杂性,包括分配(找到合适大小的孔)和反分配(缩小孔以减少碎片,如果我没有弄错的话,许多标准库实现在删除过程中需要时间来做这件事)。

在我看来,这可能非常依赖于编译器。在这个项目中,我特别使用了一个用于PPC架构的Metrowerks编译器。对这种组合的深入了解将是最有帮助的,但一般来说,对于GCC和msvc++,情况如何?堆分配不如堆栈分配高效吗?没有区别吗?还是差异如此之小以至于变成了毫无意义的微观优化。


当前回答

我不认为堆栈分配和堆分配通常是可以互换的。我也希望它们的性能都足以用于一般用途。

我强烈推荐小件物品,哪种更适合分配范围。对于较大的项,堆可能是必要的。

在有多个线程的32位操作系统上,堆栈通常是相当有限的(尽管通常至少是几mb),因为需要分割地址空间,迟早一个线程堆栈会碰到另一个线程堆栈。在单线程系统(至少是Linux glibc单线程)上,限制要小得多,因为堆栈可以不断增长。

在64位操作系统上,有足够的地址空间使线程堆栈相当大。

其他回答

堆栈要快得多。它在大多数架构上只使用一条指令,在大多数情况下,例如在x86上:

sub esp, 0x10

(将堆栈指针向下移动0x10个字节,从而“分配”这些字节供变量使用。)

当然,堆栈的大小是非常非常有限的,因为你很快就会发现你是否过度使用堆栈分配或尝试进行递归:-)

同样,没有什么理由去优化那些不需要它的代码的性能,比如通过分析来证明。“过早的优化”通常会导致比它本身价值更多的问题。

我的经验法则:如果我知道我将在编译时需要一些数据,并且它的大小在几百字节以下,我就会对它进行堆栈分配。否则我进行堆分配。

通常,堆栈分配只是由堆栈指针寄存器中的减法组成。这比搜索堆快多了。

Sometimes stack allocation requires adding a page(s) of virtual memory. Adding a new page of zeroed memory doesn't require reading a page from disk, so usually this is still going to be tons faster than searching a heap (especially if part of the heap was paged out too). In a rare situation, and you could construct such an example, enough space just happens to be available in part of the heap which is already in RAM, but allocating a new page for the stack has to wait for some other page to get written out to disk. In that rare situation, the heap is faster.

这不仅仅是堆栈分配更快。您还可以在使用堆栈变量方面获得很多好处。它们有更好的参考位置。最后,折价也便宜得多。

class Foo {
public:
    Foo(int a) {

    }
}
int func() {
    int a1, a2;
    std::cin >> a1;
    std::cin >> a2;

    Foo f1(a1);
    __asm push a1;
    __asm lea ecx, [this];
    __asm call Foo::Foo(int);

    Foo* f2 = new Foo(a2);
    __asm push sizeof(Foo);
    __asm call operator new;//there's a lot instruction here(depends on system)
    __asm push a2;
    __asm call Foo::Foo(int);

    delete f2;
}

It would be like this in asm. When you're in func, the f1 and pointer f2 has been allocated on stack (automated storage). And by the way, Foo f1(a1) has no instruction effects on stack pointer (esp),It has been allocated, if func wants get the member f1, it's instruction is something like this: lea ecx [ebp+f1], call Foo::SomeFunc(). Another thing the stack allocate may make someone think the memory is something like FIFO, the FIFO just happened when you go into some function, if you are in the function and allocate something like int i = 0, there no push happened.

自然,堆栈分配更快。使用堆分配,分配器必须在某处找到空闲内存。使用堆栈分配,编译器只需要给你的函数一个更大的堆栈框架就可以完成,这意味着分配完全不需要花费时间。(我假设您没有使用alloca或任何东西来动态分配堆栈空间,但即使这样,它也非常快。)

但是,您必须警惕隐藏的动态分配。例如:

void some_func()
{
    std::vector<int> my_vector(0x1000);
    // Do stuff with the vector...
}

您可能认为这会在堆栈上分配4 KiB,但您错了。它在堆栈上分配vector实例,但该vector实例又在堆上分配它的4 KiB,因为vector总是在堆上分配它的内部数组(至少除非您指定了一个自定义分配器,这里我不会深入讨论)。如果您希望使用类似stl的容器在堆栈上进行分配,则可能需要std::array或boost::static_vector(由外部boost库提供)。