c++ - sobre - ¿Por qué es la asignación en el montón más rápida que la asignación en la pila?
ultimas noticias de anses asignacion universal por hijo 2018 (3)
En lo que respecta a mi conocimiento sobre la gestión de recursos, asignar algo en el montón (operador new
) siempre debe ser más lento que asignar en la pila (almacenamiento automático), ya que la pila es una estructura basada en LIFO, por lo que requiere una contabilidad mínima, y El puntero de la siguiente dirección a asignar es trivial.
Hasta ahora tan bueno. Ahora mira el siguiente código:
/* ...includes... */
using std::cout;
using std::cin;
using std::endl;
int bar() { return 42; }
int main()
{
auto s1 = std::chrono::steady_clock::now();
std::packaged_task<int()> pt1(bar);
auto e1 = std::chrono::steady_clock::now();
auto s2 = std::chrono::steady_clock::now();
auto sh_ptr1 = std::make_shared<std::packaged_task<int()> >(bar);
auto e2 = std::chrono::steady_clock::now();
auto first = std::chrono::duration_cast<std::chrono::nanoseconds>(e1-s1);
auto second = std::chrono::duration_cast<std::chrono::nanoseconds>(e2-s2);
cout << "Regular: " << first.count() << endl
<< "Make shared: " << second.count() << endl;
pt1();
(*sh_ptr1)();
cout << "As you can see, both are working correctly: "
<< pt1.get_future().get() << " & "
<< sh_ptr1->get_future().get() << endl;
return 0;
}
Los resultados parecen contradecir lo explicado anteriormente:
Regular: 6131
Hacer compartir: 843
Como puede ver, ambos funcionan correctamente: 42 y 42
Programa finalizado con código de salida: 0
En la segunda medición, aparte de la llamada del operador new
, el constructor de std::shared_ptr
( auto sh_ptr1
) debe finalizar. Parece que no puedo entender por qué esto es más rápido que la asignación regular.
¿Cuál es la explicación para esto?
El problema es que la primera llamada al constructor de std::packaged_task
es responsable de inicializar una carga de estado por subproceso que luego se atribuye injustamente a pt1
. Este es un problema común de la evaluación comparativa (especialmente de la marca de microbado) y se alivia con el calentamiento; intenta leer ¿Cómo escribo un micro-benchmark correcto en Java?
Si copio su código pero ejecuto ambas partes primero, los resultados son los mismos dentro de los límites de la resolución del reloj del sistema. Esto demuestra otro problema de la marca microbiológica, que debe realizar pequeñas pruebas varias veces para permitir que el tiempo total se mida con precisión.
Con el calentamiento y la ejecución de cada parte 1000 veces, obtengo lo siguiente ( example ):
Regular: 132.986
Make shared: 211.889
La diferencia (aproximadamente 80 ns) concuerda con la regla general de que malloc toma 100 ns por llamada .
Es un problema con su micro-benchmark: si cambia el orden en el que mide el tiempo, obtendría resultados opuestos ( demo ).
Parece que la primera llamada del constructor std::packaged_task
causa un gran éxito. Añadiendo un sin hora
std::packaged_task<int()> ignore(bar);
Antes de medir el tiempo soluciona este problema ( demo ):
Regular: 505
Hacer compartir: 937
He probado tu ejemplo en ideone y obtuve un resultado similar al tuyo:
Regular: 67950
Make shared: 696
Entonces revirtí el orden de las pruebas:
auto s2 = std::chrono::steady_clock::now();
auto sh_ptr1 = std::make_shared<std::packaged_task<int()> >(bar);
auto e2 = std::chrono::steady_clock::now();
auto s1 = std::chrono::steady_clock::now();
std::packaged_task<int()> pt1(bar);
auto e1 = std::chrono::steady_clock::now();
y encontró un resultado opuesto:
Regular: 548
Make shared: 68065
Así que esa no es la diferencia de pila contra pila, sino la diferencia de la primera y la segunda llamada. Tal vez necesites mirar las std::packaged_task
internas de std::packaged_task
.