que pila direcciones diferencia c++ c performance stack heap

c++ - direcciones - ¿Es más rápido acceder a los datos en el montón que desde la pila?



que es heap (5)

Sé que esto parece una pregunta general y he visto muchas preguntas similares (tanto aquí como en la web) pero ninguna de ellas es realmente como mi dilema.

Digamos que tengo este código:

void GetSomeData(char* buffer) { // put some data in buffer } int main() { char buffer[1024]; while(1) { GetSomeData(buffer); // do something with the data } return 0; }

¿Obtendría algún rendimiento si declarara el búfer [1024] globalmente?

Ejecuté algunas pruebas en Unix a través del comando de tiempo y prácticamente no hay diferencias entre los tiempos de ejecución.

Pero no estoy realmente convencido ...

En teoría, ¿debería este cambio hacer una diferencia?


¿Es más rápido acceder a los datos en el montón que desde la pila?

No inherentemente ... en todas las arquitecturas en las que he trabajado, se puede esperar que toda la "memoria" del proceso funcione al mismo conjunto de velocidades, según el nivel del archivo de caché / RAM / intercambio de la CPU que contiene los datos actuales y cualquier retraso en la sincronización a nivel de hardware que las operaciones en esa memoria puedan desencadenar para hacerla visible a otros procesos, incorporar los cambios de otros procesos / CPU (núcleo), etc.

El sistema operativo (que es responsable de las fallas / cambios de página) y la captura de hardware (CPU) en los accesos a las páginas intercambiadas o a las que aún no se ha accedido, ni siquiera rastrearían qué páginas son "apiladas" frente a "montones". .. una página de memoria es una página de memoria. Dicho esto, la dirección virtual de los datos globales se puede calcular y codificar en el momento de la compilación, las direcciones de los datos basados ​​en la pila suelen ser relativos a la pila y el puntero, mientras que la memoria en el montón casi siempre debe accederse utilizando punteros, que podrían ser un poco más lento en algunos sistemas, depende de los modos y ciclos de direccionamiento de la CPU, pero casi siempre es insignificante, ni siquiera vale la pena echarle un vistazo o pensarlo dos veces, a menos que esté escribiendo algo donde las millonésimas de segundo sean enormemente importantes.

De todos modos, en su ejemplo, está contrastando una variable global con una variable de función local (apilar / automática) ... no hay montón involucrado. La memoria del montón proviene de new o malloc / realloc . Para la memoria de almacenamiento dinámico, el problema de rendimiento que se debe tener en cuenta es que la aplicación en sí misma realiza un seguimiento de la cantidad de memoria en uso y las direcciones, los registros de todos los que toman algún tiempo para actualizarse a medida que los punteros a la memoria son distribuidos por new / malloc / realloc , y un poco más de tiempo para actualizar a medida que los punteros se delete d o free d.

Para las variables globales, la asignación de memoria se puede hacer efectivamente en el momento de la compilación, mientras que para las variables basadas en la pila, normalmente hay un puntero de pila que se incrementa por la suma calculada en tiempo de compilación de los tamaños de las variables locales (y algunos datos de mantenimiento) cada vez Se llama una función. Por lo tanto, cuando se llama a main() puede haber algún tiempo para modificar el puntero de la pila, pero probablemente solo se modifique en una cantidad diferente en lugar de no modificarse si no hay un buffer y se modifica si existe, por lo que no hay diferencia en el rendimiento en tiempo de ejecución en absoluto.


Citando la respuesta de Jeff Hill :

La pila es más rápida porque el patrón de acceso hace que sea trivial asignar y desasignar la memoria (un puntero / entero simplemente se incrementa o disminuye), mientras que el montón tiene una contabilidad mucho más compleja involucrada en una asignación o gratis. Además, cada byte en la pila tiende a reutilizarse con mucha frecuencia, lo que significa que tiende a asignarse a la memoria caché del procesador, por lo que es muy rápido. Otro problema de rendimiento para el montón es que el montón, que es principalmente un recurso global, por lo general tiene que ser seguro para múltiples subprocesos, es decir, cada asignación y desasignación debe estar, por lo general, sincronizada con "todos" otros accesos de montón en el programa.


Dar que las variables y las matrices de variables que se declaran en el montón es más lento es solo un hecho. Piensa en ello de esta manera;

Las variables creadas globalmente se asignan una vez y se desasignan una vez que el programa se está cerrando. Para un objeto de pila, su variable debe asignarse en el lugar cada vez que se ejecuta la función, y se debe desasignar al final de la función.

¿Alguna vez ha intentado asignar un puntero a un objeto dentro de una función? Bueno, mejor libérelo / elimínelo antes de que salga la función, o si no, tendrá una pérdida de memoria, ya que no está haciendo esto en un objeto de clase en el que está libre / borrado dentro del deconstructor.

Cuando se trata de acceder a una matriz, todas funcionan igual, primero se asigna un bloque de memoria por elementos de tamaño de (DataType) *. Más tarde se puede acceder por ->

1 2 3 4 5 6 ^ entry point [0] ^ entry point [0]+3


Tu pregunta no tiene realmente una respuesta; Depende de lo que estés haciendo. En términos generales, la mayoría de las máquinas utilizan la misma estructura de "memoria" en todo el proceso, por lo que, independientemente de dónde (la pila, la pila o la memoria global) reside la variable, el tiempo de acceso será idéntico. Por otro lado, la mayoría de las máquinas modernas tienen una estructura de memoria jerárquica, con un canal de memoria, varios niveles de caché, memoria principal y memoria virtual. Dependiendo de lo que haya ocurrido anteriormente en el procesador, el acceso real puede ser a cualquiera de estos (independientemente de si es de pila, de pila o global), y los tiempos de acceso aquí varían enormemente, desde un solo reloj si la memoria es en el lugar correcto en la tubería, a unos 10 milisegundos si el sistema tiene que ir a la memoria virtual en el disco.

En todos los casos, la clave es la localidad. Si un acceso está "cerca" de un acceso anterior, mejorará en gran medida la posibilidad de encontrarlo en una de las ubicaciones más rápidas: caché, por ejemplo. En este sentido, poner objetos más pequeños en la pila puede ser más rápido, porque cuando accede a los argumentos de una función, tiene acceso a la memoria de la pila (con un procesador Intel de 32 bits, al menos --- con procesadores mejor diseñados, es más probable que los argumentos estén en los registros). Pero esto probablemente no será un problema cuando se involucre una matriz.


cuando se asignan buffers en la pila, el alcance de la optimización no es el costo de acceder a la memoria, sino la eliminación de la asignación de memoria dinámica a menudo muy costosa en el montón (la asignación del buffer de pila se puede considerar instantánea, ya que la pila en su conjunto se asigna al inicio del hilo) .