为什么分配堆内存比分配栈内存快得多?
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) 及其文档。
我想指出,堆栈分配在 运行 时绝对不需要时间;所有的工作都由编译器完成。
无论优化如何,比较都没有意义。
我尝试在堆和栈内存中为 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) 及其文档。
我想指出,堆栈分配在 运行 时绝对不需要时间;所有的工作都由编译器完成。 无论优化如何,比较都没有意义。