这个问题听起来可能相当基本的但这是我必须用另一个开发人员处理的争论。

我已小心地堆栈分配位置可以而不是堆分配它们的事情。他是与我交谈,在我的肩上观看并注释它没有必要因为它们是明智的做法相同的性能。

我总是被认为,增长堆栈是恒定的时间,并且堆分配性能依赖于当前 (查找适当大小的一个洞) 的分配和解除分配 (折叠孔以减少碎片,因为许多标准库实现需要花时间来执行此操作删除过程中,如果我不被误认为) 堆的复杂性。

作为东西很可能是非常相关的编译器这震撼我的地方。该项目特别是我在使用Metrowerks编译器PPC的体系结构。这种组合的见解将是最有帮助的但是一般情况下,对于 GCC,和 MSVC + + 中,什么是这种情况呢?作为高性能堆栈分配不是堆分配?有没有什么不同吗?也因此每分钟变得毫无意义的微优化的区别是。

2008-10-02 06:06:28
问题评论:

为什么不只是替换空 e;对于像 int j = i;它应确保堆栈分配未发生。

我知道这是相当古老,但最好查看演示不同类型的分配有些 C/c + + 代码段。

牛 orker 是未知,但他是很危险的因为他使他并太不了解有关的事情的权威声明更重要。尽可能快地从您的团队的这类人的消费税。

请注意,堆通常大于堆栈。如果您分配大量的数据,您实际需要将其放在堆上,否则将从操作系统的堆栈大小。

回答:

堆栈分配要快得多因为所有它确实是移动堆栈指针。使用内存池,您可以获得相当的堆分配性能,但附带轻微增加的复杂性和自己头痛。

同时,堆和堆栈不只性能的一个因素;它还将告知您大量对象的预期寿命。

和更重要的是,堆栈始终热,您得到的内存更多可能是比任何远堆分配的内存缓存中

一些 (主要是嵌入的我知道的) 体系结构中,堆栈可能存储在速度上模具内存 (例如 SRAM)。这可以带来巨大的不同 !

堆栈是因为实际上,堆栈。无法释放堆栈除非它是在它的上面所使用的内存块。没有管理,推入或弹出的事情上。另一方面,托管的堆内存是︰ 它要求内核内存区块、 也许将它们拆分、 合并 thems、 重用它们并将其释放。堆栈是真的意味着快速、 简短的分配。

@Pacerier 因为堆栈是堆比很小。如果您想要分配大数组,您更好地分配在堆上。如果您尝试分配堆栈上的一个大数组它将为您提供堆栈溢出。尝试,例如在 c + +: int t [100000000];请尝试例如 t [10000000] = 10;并且然后 cout << t [10000000];它会使您的堆栈溢出或只是将不起作用,不会显示任何内容。但是,如果您分配在堆上的数组︰ int * t = 新 int [100000000];并执行相同的操作之后,它会起作用,因为堆有这样一个大数组的大小。

他们分配中最明显的原因是在堆栈上的对象超出范围块退出时的 @Pacerier。

堆栈是快得多。它实际上只使用了大多数的体系结构,在大多数情况下,例如在 x86 上一条指令︰

sub esp, 0x10

(将堆栈指针向下移动由 0x10 字节,从而""这些字节用于分配的变量。)

当然,堆栈的大小是非常有限的因为将快速了解到,是否过度使用堆栈分配或尝试:-) 做无限递归

另外,还有一点原因来优化性能的代码可验证并不需要它,例如,通过分析说明了。"过早优化"往往会让更多的问题不是得不偿失。

我的经验法则︰ 如果我知道我将需要一些数据在编译时,和它位于几百字节的大小,我堆栈的分配它。否则我堆的分配它。

一条指令,并且通常由堆栈上的所有对象共享。

所做的好,尤其是关于可验证需要它的点的点。我不断地吃惊性能有关的人的问题放错位置。

"取消分配"也是非常简单,通过leave单个指令。

请记住成本,尤其是对于第一次扩展堆栈"隐藏"。这样做可能会导致页面错误,它们需要执行一些操作来分配内存 (或从交换,在最坏的情况下加载该) 内核的上下文切换。

真诚地,它是常用来编写一个程序来比较性能︰

#include <ctime>
#include <iostream>

namespace {
    class empty { }; // even empty classes take up 1 byte of space, minimum
}

int main()
{
    std::clock_t start = std::clock();
    for (int i = 0; i < 100000; ++i)
        empty e;
    std::clock_t duration = std::clock() - start;
    std::cout << "stack allocation took " << duration << " clock ticks
";
    start = std::clock();
    for (int i = 0; i < 100000; ++i) {
        empty* e = new empty;
        delete e;
    };
    duration = std::clock() - start;
    std::cout << "heap allocation took " << duration << " clock ticks
";
}

它说愚蠢的一致性是一些思想的教条很明显优化编译器,这是很多程序员心里的 hobgoblins。本文使用底部的答案,但人们显然不能打扰阅读,到目前为止,所以我在这里要避免的问题,我已回答移动它。

优化的编译器可能会注意到,此代码不执行任何操作,并可能对其优化全部消失。它是优化程序的工作,像这样,做东西,反击优化程序是愚弄人民监督。

我建议使用已关闭,因为没有好的方法来让每个优化程序当前正在使用或将在未来成为使用中的优化编译此代码。

优化程序开启,然后报告有关打击它的人应受到公共的嘲笑。

如果我太纳秒精度有关,我岂不使用std::clock()如果我想要发布的结果作为博士论文,我会使关于此,大交易,可能会比较 GCC,Tendra/Ten15、 LLVM、 Watcom、 高、 Visual C++、 数字火星,ICC 和其他编译器。由于它是,堆分配采用数百次长比堆栈分配并看不到任何有关进一步的调查问题任何有用的操作。

优化程序拥有摆脱我正在测试的代码的一个使命。我看不到任何理由告诉优化器运行,然后尝试欺骗实际上并没有优化优化程序。但如果我看到认识的价值,我可以执行下列一项或多项操作︰

  1. 数据成员添加到empty,以及访问该数据成员在循环;但如果我只读取数据成员优化程序可以执行常量合并和删除循环;如果我只写入的数据成员,优化程序可以跳过除循环的最后一次迭代。此外,这个问题不是"堆栈分配和堆分配与数据访问和数据访问"。

  2. 声明evolatilevolatile经常编译时错误(PDF)。

  3. 采用循环内的e地址 (和可能将其分配给一个变量声明为extern并且在另一个文件中定义)。但即使在这种情况下,编译器可能发现 — 堆栈上至少- e始终在相同的内存地址将分配,然后执行上面的常量合并类似 (1)。获取循环,该循环的所有迭代,但实际上绝不会分配对象。

不明确的该测试有缺陷在于它测量分配和解除分配,并且原始问题没有问到解除分配。当然在堆栈上分配的变量在其范围的末尾将自动释放,因此无法调用delete将 (1) 倾斜 (堆栈释放纳入有关堆栈分配的编号,这样才公平测量堆释放) 的数字和 (2) 导致非常坏的内存泄漏,除非我们将为新指针的引用,调用delete后我们必须将我们的时间测量。

我的计算机上使用 g + + 3.4.4 在 Windows 上,获得"0 时钟滴答"堆栈和堆分配任何小于 100000 分配,和甚至然后得到"0 时钟计时周期"堆栈分配和"15 时钟滴答"的堆分配。当测量 10000000 分配时,堆栈分配采用 31 时钟计时周期并堆分配采用 1562年时钟计时周期。


是的优化编译器可能 elide 创建的空的对象。如果我理解正确,它可能甚至 elide 整个第一个循环。当我增加 10000000 堆栈分配到迭代花费 31 时钟计时周期以及堆分配了 1562年时钟计时周期。我认为它是肯定地说,不让知道 g + + 优化可执行文件,g + + 没有不 elide 构造函数。


在年中因为我写了这首选项在堆栈溢出已张贴与优化生成的性能。一般情况下,我认为这是正确的。但是,我仍然认为这是愚蠢要求编译器实际上不希望代码优化时优化代码。作为非常类似于支付额外费用为 valet 停车但拒绝呈递键它震撼我的地方。在此特定情况下,我不想运行优化的程序。

用稍加修改的版本的准则 (以满足原始程序未分配堆栈上的内容每次通过循环时的有效点),并编译但不优化但链接来释放库 (来解决我们不希望包括任何减速造成链接的调试库的有效点)︰

#include <cstdio>
#include <chrono>

namespace {
    void on_stack()
    {
        int i;
    }

    void on_heap()
    {
        int* i = new int;
        delete i;
    }
}

int main()
{
    auto begin = std::chrono::system_clock::now();
    for (int i = 0; i < 1000000000; ++i)
        on_stack();
    auto end = std::chrono::system_clock::now();

    std::printf("on_stack took %f seconds
", std::chrono::duration<double>(end - begin).count());

    begin = std::chrono::system_clock::now();
    for (int i = 0; i < 1000000000; ++i)
        on_heap();
    end = std::chrono::system_clock::now();

    std::printf("on_heap took %f seconds
", std::chrono::duration<double>(end - begin).count());
    return 0;
}

将显示︰

on_stack took 2.070003 seconds
on_heap took 57.980081 seconds

在我的系统与命令编译时行cl foo.cc /Od /MT /EHsc.

您可能不同意我的方法来获取非优化的生成。这没什么关系︰ 尽情修改基准测试时所需。当我打开优化时,得到︰

on_stack took 0.000000 seconds
on_heap took 51.608723 seconds

不是因为堆栈分配实际上瞬间完成,但任何半不错的编译器,可以注意到, on_stack并不会很有用,可以优化掉。GCC Linux 便携式计算机还注意到, on_heap并不会很有用,并优化它消失也︰

on_stack took 0.000003 seconds
on_heap took 0.000002 seconds

此外,应添加"校准"循环初现端倪的主函数,内容以使您了解每个循环周期得到,并且调整以确保您的示例的其他循环了多长时间运行的一定量的时间,而不是固定的常量您正在使用。

我很高兴还增加了每个选项循环运行,再加上指示 g + + 不来优化?) 的时间产生显著的效果。所以现在我们有了真实的事实,说堆栈是速度更快。感谢您的努力 !

是要除去类似下面的优化程序的职责。是否有合理的理由打开优化程序,然后阻止它实际优化?我已编辑以使事情更清晰的答案︰ 如果您喜欢反击优化程序,一定要学习如何如何的聪明的编译器编写器。

我是很晚,但它也是非常值得一提的是这里的堆分配请求通过内核的内存,因此极力命中的性能取决于内核的效率。Linux 中使用此代码 (Linux 3.10.7-gentoo #2 SMP 星期三 9 月 4 18:58:21 MDT 2013 x86_64)、 修改 HR 计时器,并在每个循环使用 100 万个迭代生成这样的业绩:stack allocation took 0.15354 seconds, heap allocation took 0.834044 seconds-O0集,使 Linux 堆分配仅慢上约我特定的计算机上的 5.5 倍。

在 windows 但不优化 (调试版本) 上,它将使用调试堆比非调试堆得多慢。我认为它根本是"技巧"优化到是个好主意。编译器编写器智能,但编译器并非 AI 的。

值得注意的是,我学到了有关堆栈和堆分配 Xbox 360 Xenon 处理器,还可应用于其他多核系统,所分配在堆上会导致临界区输入暂停所有其他内核,以便分配不发生冲突。因此,在紧密循环中,堆栈分配是因为它无法停止去获得固定大小数组的方式。

这可能是另一个要考虑是否您使用的编程的多核/multiproc,堆栈分配将仅可由您指定了作用域的功能,运行的核心,它将不会影响任何其他核心/Cpu 的速度。

这是最多核机器,而不仅仅是 Xenon 的则返回 true。即使单元格已做它,因为您可能的 PPU 核心上运行两个硬件线程。

这就是 (尤其是差) 的堆分配器实现的效果。更好的堆分配器不需要获取对锁的每个分配。

您可以编写的非常性能特殊堆分配的特定大小的对象。但是,常规的堆分配器不是特别是性能。

此外我同意Torbjörn Gyllebring对象的预期寿命有关。好问题 !

它有时被称为楼板分配。

请输入您的翻译

Which is faster: Stack allocation or Heap allocation

确认取消