sentencia - ¿Hay alguna sobrecarga para declarar una variable dentro de un bucle?(C++)
que es if else en programacion (13)
Esta pregunta ya tiene una respuesta aquí:
- ¿Diferencia entre declarar variables antes o en bucle? 24 respuestas
Me pregunto si habrá una pérdida de velocidad o eficiencia si haces algo como esto:
int i = 0;
while(i < 100)
{
int var = 4;
i++;
}
que declara int var
cien veces. Me parece que habría, pero no estoy seguro. sería más práctico / más rápido hacer esto en su lugar:
int i = 0;
int var;
while(i < 100)
{
var = 4;
i++;
}
o son los mismos, en cuanto a velocidad y eficiencia?
Actualmente, es mejor declararlo dentro del ciclo, a menos que sea una constante, ya que el compilador podrá optimizar mejor el código (reduciendo el alcance de la variable).
EDITAR: Esta respuesta es en su mayoría obsoleta ahora. Con el auge de los compiladores postclásicos, los casos en que el compilador no puede resolverlo son cada vez menos frecuentes. Todavía puedo construirlos, pero la mayoría de la gente clasificaría la construcción como un código incorrecto.
Ambos bucles tienen la misma eficacia. Ambos tomarán una cantidad infinita de tiempo :) Puede ser una buena idea incrementar i dentro de los bucles.
Ambos son lo mismo, y así es cómo se puede averiguar, al observar lo que hace el compilador (incluso sin la optimización establecida en alto):
Mira lo que el compilador (gcc 4.0) hace con tus ejemplos simples:
1.c:
main(){ int var; while(int i < 100) { var = 4; } }
gcc -S 1.c
1.s:
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
2.c
main() { while(int i < 100) { int var = 4; } }
gcc -S 2.c
2.s:
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
A partir de estos, puede ver dos cosas: en primer lugar, el código es el mismo en ambos.
En segundo lugar, el almacenamiento para var se asigna fuera del ciclo:
subl $24, %esp
Y, finalmente, lo único que hay en el ciclo es la verificación de asignación y condición:
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
Que es lo más eficiente que puede ser sin eliminar por completo el bucle.
Con solo dos variables, el compilador probablemente asignará un registro para ambos. Estos registros están ahí de todos modos, así que esto no lleva tiempo. Hay 2 instrucciones de registro de registro y una de registro en cualquiera de los casos.
Creo que a la mayoría de las respuestas le falta un punto importante para considerar cuál es: "¿Está claro?" Y, obviamente, según toda la discusión, el hecho es; no, no es. Sugeriría que en la mayoría de los códigos de bucle la eficiencia es prácticamente nula (a menos que calcule para un módulo de aterrizaje de Marte), realmente la única pregunta es qué parece más sensato, legible y fácil de mantener, en este caso recomiendo declarar la variable al frente y fuera del bucle - esto simplemente lo hace más claro. Entonces las personas como usted y yo no nos molestaríamos en perder tiempo revisando en línea para ver si es válido o no.
El espacio de pila para variables locales generalmente se asigna en el alcance de la función. Entonces, no ocurre ningún ajuste de puntero de pila dentro del ciclo, simplemente asignando 4 a var
. Por lo tanto, estos dos fragmentos tienen la misma sobrecarga.
La única forma de estar seguro es cronometrarlos. Pero la diferencia, si es que hay una, será microscópica, por lo que necesitarás un gran ciclo de sincronización.
Más al punto, el primero es un mejor estilo porque inicializa la variable var, mientras que el otro lo deja sin inicializar. Esto y la pauta de que uno debe definir las variables lo más cerca posible de su punto de uso, significa que la primera forma normalmente debería ser preferida.
La mayoría de los compiladores modernos optimizarán esto para usted. Dicho esto, utilizaría su primer ejemplo, ya que me parece más legible.
Para tipos primitivos y tipos de POD, no hace diferencia. El compilador asignará el espacio de la pila para la variable al comienzo de la función y lo desasignará cuando la función regrese en ambos casos.
Para los tipos de clase no POD que tienen constructores no triviales, MARCARÁ la diferencia; en ese caso, colocar la variable fuera del ciclo solo llamará al constructor y al destructor una vez y al operador de asignación cada iteración, mientras que lo coloca dentro del loop llamará al constructor y al destructor para cada iteración del ciclo. Dependiendo de lo que hagan el constructor de la clase, el destructor y el operador de asignación, esto puede ser conveniente o no.
Para un tipo incorporado probablemente no habrá diferencia entre los 2 estilos (probablemente hasta el código generado).
Sin embargo, si la variable es una clase con un constructor / destructor no trivial, podría haber una gran diferencia en el costo del tiempo de ejecución. Por lo general, enfoco la variable dentro del ciclo (para mantener el alcance lo más pequeño posible), pero si resulta tener un impacto de rendimiento, buscaría mover la variable de clase fuera del alcance del ciclo. Sin embargo, hacer eso necesita un análisis adicional ya que la semántica de la ruta de los odos puede cambiar, por lo que esto solo se puede hacer si las estadísticas lo permiten.
Una clase RAII podría necesitar este comportamiento. Por ejemplo, una clase que gestiona la vida útil de acceso a archivos puede necesitar crearse y destruirse en cada iteración de bucle para gestionar el acceso al archivo correctamente.
Supongamos que tiene una clase LockMgr
que adquiere una sección crítica cuando se construye y la libera cuando se destruye:
while (i< 100) {
LockMgr lock( myCriticalSection); // acquires a critical section at start of
// each loop iteration
// do stuff...
} // critical section is released at end of each loop iteration
es bastante diferente de:
LockMgr lock( myCriticalSection);
while (i< 100) {
// do stuff...
}
Una vez realicé algunas pruebas de rendimiento y, para mi sorpresa, ¡descubrí que el caso 1 era realmente más rápido! Supongo que esto puede deberse a que declarar la variable dentro del ciclo reduce su alcance, por lo que se libera antes. Sin embargo, eso fue hace mucho tiempo, en un compilador muy antiguo. Estoy seguro de que los compiladores modernos hacen un mejor trabajo optimizando las diferencias, pero aún no está mal mantener el alcance variable lo más corto posible.
eso no es cierto, hay gastos generales, pero su descuido puede sobrecargarse.
Aunque probablemente terminen en el mismo lugar en la pila, aún así lo asignan. Asignará la ubicación de memoria en la pila para ese int y luego la liberará al final de}. No en sentido libre, en sentido, moverá sp (stack pointer) por 1. Y en tu caso, considerando que solo tiene una variable local, simplemente equiparará fp (frame pointer) y sp
La respuesta breve sería: NO CUIDES DE CUALQUIER MANERA FUNCIONA CASI LO MISMO.
Pero intenta leer más sobre cómo se organiza la pila. Mi escuela de pregrado tuvo muy buenas conferencias sobre eso. Si quieres leer más verifica aquí http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html
#include <stdio.h>
int main()
{
for(int i = 0; i < 10; i++)
{
int test;
if(i == 0)
test = 100;
printf("%d/n", test);
}
}
El código anterior siempre imprime 100 10 veces, lo que significa que el bucle interno de la variable local solo se asigna una vez por cada llamada de función.