为什么分配堆内存比分配栈内存快得多?

Why is allocating heap-memory much faster than allocating stack-memory?

我尝试在堆和栈内存中为 10^7 个整数分配 space 以查看哪个更快。显然在堆内存中分配要快得多,但我不明白原因。

#include <bits/stdc++.h>
#include <chrono>

using namespace std;
using namespace std::chrono;

int main()
{
  high_resolution_clock::time_point t1 = high_resolution_clock::now();

  int *p = new int[1e7];

  high_resolution_clock::time_point t2 = high_resolution_clock::now();
  auto duration = duration_cast<microseconds>( t2 - t1 ).count();
  cout << duration / 1e6 << "\n"; // 5e-06



  t1 = high_resolution_clock::now();

  vector<int> v(1e7);

  t2 = high_resolution_clock::now();
  duration = duration_cast<microseconds>( t2 - t1 ).count();
  cout << duration / 1e6 << "\n"; // 0.112284

  return 0;
}

我只是一个初学者,但我把我所理解的主要用于测试自己。

int *p = new int[1e7];

您正在为堆上的 1000 万个整数分配连续的内存。

vector<int> v(1e7);

您正在为 vector<int> 对象分配堆栈内存。在该对象的成员中,有一个指向堆上 int[1e7] 的指针,该指针也已分配。此外,其中的所有值都使用 int() 的值(带 0)进行了初始化。参见 constructor (2) of std::vector

new int[1e7] 为 1e7 int 值分配 space 并且不初始化它们。

vector<int> v(1e7); 在堆栈上创建一个 vector<int> 对象,该对象的构造函数在堆上为 1e7 int 值分配 space。它 将每个 int 值初始化为 0。

速度上的差异是因为初始化。

为了比较堆栈分配的速度,您需要在堆栈上分配一个数组:

int data[1e7];

但请注意:这很有可能会失败,因为堆栈不够大,无法容纳那么大的数组。

其他回答指出vector constructor中至少有一个"hidden"初始化

但是你的例子还有另一个问题:也许它甚至没有衡量你认为它做了什么。在 C++ 中对未优化的代码进行基准测试几乎毫无意义,并且很难正确计时优化代码。

让我们看一下您的(为了便于阅读而修改的)示例,该示例由 Clang 以 -O3 优化级别编译:godbolt link.

double test1() {
  high_resolution_clock::time_point t1 = high_resolution_clock::now();

  int *p = new int[1e7];

  high_resolution_clock::time_point t2 = high_resolution_clock::now();
  auto duration = duration_cast<microseconds>( t2 - t1 ).count();
  return duration / 1e6; // 5e-06
}

编译为:

test1():                              # @test1()
        push    rbx
        call    std::chrono::_V2::system_clock::now()
        mov     rbx, rax
        call    std::chrono::_V2::system_clock::now()
        sub     rax, rbx
        movabs  rcx, 2361183241434822607
        imul    rcx
        mov     rax, rdx
        shr     rax, 63
        sar     rdx, 7
        add     rdx, rax
        cvtsi2sd        xmm0, rdx
        divsd   xmm0, qword ptr [rip + .LCPI0_0]
        pop     rbx
        ret
.LCPI1_0:
        .quad   4696837146684686336     # double 1.0E+6

第一部分甚至没有调用 operator new!编译器查看了您的程序并意识到您从未使用过分配的数组,因此它从生成的可执行文件中删除了分配。

因此,当使用这样的设置进行编译时,程序的第一部分根本不会在堆上分配数组,从而使测量变得毫无意义。

我建议阅读基准测试并使用专门的微基准框架来进行此类测试。查看 Google Benchmark (and online QuickBench) 及其文档。

我想指出,堆栈分配在 运行 时绝对不需要时间;所有的工作都由编译器完成。 无论优化如何,比较都没有意义。