Me parece que todo lo que se puede hacer con una pila se puede hacer con el montón, pero no todo lo que se puede hacer con el montón se puede hacer con la pila. ¿Es eso correcto? Entonces, por simplicidad, e incluso si perdemos una pequeña cantidad de rendimiento con ciertas cargas de trabajo, ¿no podría ser mejor ir con un solo estándar (es decir, el montón)?
Piense en la compensación entre modularidad y rendimiento. Sé que esa no es la mejor manera de describir este escenario, pero en general parece que la simplicidad de comprensión y diseño podría ser una mejor opción, incluso si existe la posibilidad de un mejor rendimiento.
Respuestas:
Los montones son malos en la asignación rápida de memoria y la desasignación. Si desea obtener muchas pequeñas cantidades de memoria durante un tiempo limitado, un montón no es su mejor opción. Una pila, con su algoritmo de asignación / desasignación súper simple, sobresale naturalmente en esto (aún más si está integrado en el hardware), por lo que la gente lo usa para cosas como pasar argumentos a funciones y almacenar variables locales, la mayoría La desventaja importante es que tiene un espacio limitado, por lo que mantener objetos grandes en él o tratar de usarlo para objetos de larga duración son malas ideas.
Deshacerse de la pila por completo en aras de simplificar un lenguaje de programación es la manera incorrecta de la OMI: un mejor enfoque sería abstraer las diferencias, dejar que el compilador descubra qué tipo de almacenamiento usar, mientras el programador reúne más alto- construcciones de nivel que están más cerca de la forma en que los humanos piensan, y de hecho, los lenguajes de alto nivel como C #, Java, Python, etc. hacen exactamente esto. Ofrecen una sintaxis casi idéntica para los objetos asignados al montón y las primitivas asignadas a la pila ('tipos de referencia' frente a 'tipos de valor' en la jerga de .NET), ya sea completamente transparente o con algunas diferencias funcionales que debe comprender para usar el lenguaje correctamente (pero en realidad no tiene que saber cómo funcionan internamente una pila y un montón).
fuente
En pocas palabras, una pila no es un poco de rendimiento. Es cientos o miles de veces más rápido que el montón. Además, la mayoría de las máquinas modernas tienen soporte de hardware para la pila (como x86) y esa funcionalidad de hardware para, por ejemplo, la pila de llamadas no se puede eliminar.
fuente
No
El área de pila en C ++ es increíblemente rápida en comparación. Me atrevo a decir que ningún desarrollador experimentado de C ++ estaría abierto a deshabilitar esa funcionalidad.
Con C ++, tiene elección y control. Los diseñadores no estaban particularmente inclinados a introducir características que agregaran tiempo o espacio de ejecución significativo.
Ejerciendo esa elección
Si desea crear una biblioteca o programa que requiera que cada objeto se asigne dinámicamente, puede hacerlo con C ++. Se ejecutaría relativamente lento, pero entonces podría tener esa 'modularidad'. Para el resto de nosotros, la modularidad siempre es opcional, instálela según sea necesario porque ambas son necesarias para implementaciones buenas / rápidas.
Alternativas
Hay otros idiomas que requieren que el almacenamiento para cada objeto se cree en el montón; es bastante lento, de modo que compromete los diseños (programas del mundo real) de una manera peor que tener que aprender ambos (IMO).
Ambos son importantes, y C ++ le brinda el poder de usar ambos de manera efectiva para cada escenario dado. Dicho esto, el lenguaje C ++ puede no ser ideal para su diseño, si estos factores en su OP son importantes para usted (por ejemplo, lea en lenguajes de nivel superior).
fuente
En realidad, ¡el rendimiento es probable que sea considerable!
Como otros han señalado, las pilas son una estructura extremadamente eficiente para administrar datos que obedecen las reglas LIFO (último en entrar, primero en salir). La asignación / liberación de memoria en la pila suele ser solo un cambio en un registro en la CPU. Cambiar un registro es casi siempre una de las operaciones más rápidas que puede realizar un procesador.
El montón suele ser una estructura de datos bastante compleja y la asignación / liberación de memoria requerirá muchas instrucciones para hacer toda la contabilidad asociada. Peor aún, en implementaciones comunes, cada llamada para trabajar con el montón tiene el potencial de resultar en una llamada al sistema operativo. ¡Las llamadas al sistema operativo requieren mucho tiempo! El programa generalmente tiene que cambiar del modo de usuario al modo kernel, y cada vez que esto sucede, el sistema operativo puede decidir que otros programas tienen necesidades más urgentes y que su programa tendrá que esperar.
fuente
Simula usó el montón para todo. Poner todo en el montón siempre induce un nivel más de indirección para las variables locales, y ejerce una presión adicional sobre el recolector de basura (debe tener en cuenta que los recolectores de basura realmente apestaron en ese entonces). Eso es en parte por qué Bjarne inventó C ++.
fuente
Las pilas son extremadamente eficientes para los datos LIFO, como los metadatos asociados con las llamadas a funciones, por ejemplo. La pila también aprovecha las características de diseño inherentes de la CPU. Dado que el rendimiento en este nivel es fundamental para casi todo lo demás en un proceso, tomar ese "pequeño" golpe a ese nivel se propagará ampliamente. Además, el sistema operativo puede mover la memoria del montón, lo que sería mortal para las pilas. Si bien se puede implementar una pila en el montón, requiere una sobrecarga que afectará literalmente cada parte del proceso al nivel más granular.
fuente
"eficiente" en términos de escribir código tal vez, pero ciertamente no en términos de la eficiencia de su software. Las asignaciones de pila son esencialmente gratuitas (solo se necesitan unas pocas instrucciones de la máquina para mover el puntero de la pila y reservar espacio en la pila para las variables locales).
Dado que la asignación de la pila casi no lleva tiempo, una asignación incluso en un montón muy eficiente será 100k (si no 1M +) de veces más lenta.
Ahora imagine cuántas variables locales y otras estructuras de datos utiliza una aplicación típica. Cada pequeña "i" que usas como contador de bucles se asigna un millón de veces más lento.
Claro, si el hardware es lo suficientemente rápido, puede escribir una aplicación que solo use el montón. Pero ahora imagina qué tipo de aplicación podrías escribir si aprovechas el montón y utilizas el mismo hardware.
fuente
Es posible que le interese "La recolección de basura es rápida, pero una pila es más rápida".
http://dspace.mit.edu/bitstream/handle/1721.1/6622/AIM-1462.ps.Z
Si lo leí correctamente, estos chicos modificaron un compilador de C para asignar "cuadros de pila" en el montón, y luego usar la recolección de basura para desasignar los cuadros en lugar de hacer estallar la pila.
Los "marcos de pila" asignados a la pila superan decisivamente a los "marcos de pila" asignados al montón.
fuente
¿Cómo va a funcionar la pila de llamadas en un montón? Esencialmente, tendría que asignar una pila en el montón en cada programa, entonces, ¿por qué no hacer que el hardware OS + lo haga por usted?
Si quieres que las cosas sean realmente simples y eficientes, solo dale al usuario su porción de memoria y deja que se ocupe de eso. Por supuesto, nadie quiere implementar todo por sí mismo y es por eso que tenemos una pila y un montón.
fuente
Se requieren tanto la pila como el montón. Se utilizan en diferentes situaciones, por ejemplo:
Básicamente, los mecanismos no se pueden comparar en absoluto porque muchos detalles son diferentes. Lo único común con ellos es que ambos manejan la memoria de alguna manera.
fuente
Las computadoras modernas tienen varias capas de memoria caché además de un sistema de memoria principal grande pero lento. Uno puede hacer docenas de accesos a la memoria caché más rápida en el tiempo requerido para leer o escribir un byte desde el sistema de memoria principal. Por lo tanto, acceder a una ubicación mil veces es mucho más rápido que acceder a 1,000 (o incluso 100) ubicaciones independientes una vez cada una. Debido a que la mayoría de las aplicaciones asignan y desasignan repetidamente pequeñas cantidades de memoria cerca de la parte superior de la pila, las ubicaciones en la parte superior de la pila se usan y reutilizan una cantidad enorme, de modo que la gran mayoría (99% + en una aplicación típica) de los accesos a la pila se pueden manejar usando memoria caché.
Por el contrario, si una aplicación creara y abandonara repetidamente objetos de almacenamiento dinámico para almacenar información de continuación, cada versión de cada objeto de pila que se haya creado debería escribirse en la memoria principal. Incluso si la gran mayoría de estos objetos fueran completamente inútiles para cuando la CPU quisiera reciclar las páginas de caché en las que comenzaron, la CPU no tendría forma de saberlo. En consecuencia, la CPU tendría que perder mucho tiempo realizando grabaciones lentas de memoria de información inútil. No es exactamente una receta para la velocidad.
Otra cosa a considerar es que en muchos casos es útil saber que una referencia de objeto pasada a una rutina no se usará una vez que la rutina salga. Si se pasan parámetros y variables locales a través de la pila, y si la inspección del código de la rutina revela que no persiste una copia de la referencia pasada, entonces el código que llama a la rutina puede estar seguro de que si no hay una referencia externa a la el objeto existía antes de la llamada, ninguno existirá después. Por el contrario, si los parámetros se pasaran a través de objetos de montón, los conceptos como "después de que una rutina regrese" se vuelven algo más nebulosos, ya que si el código guardara una copia de la continuación, sería posible que la rutina "regrese" más de una vez después de un Llamada única.
fuente