¿Hay alguna sobrecarga para declarar una variable dentro de un bucle? (C ++)

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?

El espacio de stack para variables locales generalmente se asigna en el scope de la función. Entonces, no ocurre ningún ajuste de puntero de stack dentro del ciclo, simplemente asignando 4 a var . Por lo tanto, estos dos fragmentos tienen la misma sobrecarga.

Para tipos primitivos y tipos de POD, no hace diferencia. El comstackdor asignará el espacio de la stack 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.

Ambos son lo mismo, y así es cómo se puede averiguar, al observar lo que hace el comstackdor (incluso sin la optimización establecida en alto):

Mira lo que el comstackdor (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.

Actualmente, es mejor declararlo dentro del ciclo, a menos que sea una constante, ya que el comstackdor podrá optimizar mejor el código (reduciendo el scope de la variable).

EDITAR: Esta respuesta es en su mayoría obsoleta ahora. Con el auge de los comstackdores postclásicos, los casos en que el comstackdor 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.

La mayoría de los comstackdores modernos optimizarán esto para usted. Dicho esto, utilizaría su primer ejemplo, ya que me parece más legible.

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 scope lo más pequeño posible), pero si resulta tener un impacto de rendimiento, buscaría mover la variable de clase fuera del scope 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... } 

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.

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 scope, por lo que se libera antes. Sin embargo, eso fue hace mucho tiempo, en un comstackdor muy antiguo. Estoy seguro de que los comstackdores modernos hacen un mejor trabajo optimizando las diferencias, pero aún no está mal mantener el scope variable lo más corto posible.

 #include  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.

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.

Con solo dos variables, el comstackdor 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.

eso no es cierto, hay gastos generales, pero su descuido puede sobrecargarse.

Aunque probablemente terminen en el mismo lugar en la stack, aún así lo asignan. Asignará la ubicación de memoria en la stack 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 stack. 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