c++ - ¿Por qué la asignación de memoria de montón es mucho más rápida que la asignación de memoria de pila?
performance heap-memory (3)
He intentado asignar espacio para 10 ^ 7 enteros en la memoria de pila y pila para ver cuál es más rápido. Obviamente, la asignación en la memoria de almacenamiento dinámico fue mucho más rápida, pero no entiendo la razón.
#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;
}
Otras respuestas señalaron que hay al menos una inicialización "oculta" en el constructor de vectores.
Pero su ejemplo tiene otro problema: tal vez ni siquiera mide lo que cree que hace. La evaluación comparativa del código no optimizado en C ++ no tiene sentido y el código optimizado correctamente es difícil.
Echemos un vistazo a su ejemplo (modificado para facilitar la lectura) compilado por Clang con un nivel de optimización -O3 : enlace godbolt .
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
}
compilado a:
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
¡La primera parte ni siquiera llama al operador nuevo! El compilador vio a través de su programa y se dio cuenta de que nunca utilizó la matriz asignada, por lo que eliminó la asignación del ejecutable resultante.
Entonces, la primera parte de su programa no asigna la matriz en el montón cuando se compila con tales configuraciones, lo que hace que las mediciones no tengan sentido.
Recomiendo leer sobre benchmarking y usar marcos de micro benchmark especializados para hacer tales pruebas. Eche un vistazo a Google Benchmark (y QuickBench línea) y su documentación.
Solo soy un principiante, pero déjame dar lo que entiendo principalmente para ponerme a prueba.
En
int *p = new int[1e7];
está asignando memoria consecutiva para 10 millones de enteros en el montón.
En
vector<int> v(1e7);
está asignando en la memoria de pila para un objeto
vector<int>
.
Entre los miembros de ese objeto hay un puntero a un
int[1e7]
en el montón, que también se asigna.
Además, todos los valores en él se inicializan con el valor de
int()
(con 0s).
Ver
constructor (2) de
std::vector
.
new int[1e7]
asigna espacio para los valores 1e7
int
y no los inicializa.
vector<int> v(1e7);
crea un objeto
vector<int>
en la pila, y el constructor para ese objeto asigna espacio para 1e7 valores
int
en el montón.
También
inicializa cada valor
int
a 0.
La diferencia en velocidad se debe a la inicialización.
Para comparar la velocidad de la asignación de la pila, debe asignar una matriz en la pila:
int data[1e7];
pero tenga cuidado: hay una buena posibilidad de que esto falle porque la pila no es lo suficientemente grande para una matriz tan grande.