Siempre me he preguntado si, en general, declarar una variable desechable antes de un ciclo, en lugar de repetidamente dentro del ciclo, ¿hace alguna diferencia (de rendimiento)? Un ejemplo (bastante inútil) en Java:
a) declaración antes del bucle:
double intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
b) declaración (repetidamente) dentro del bucle:
for(int i=0; i < 1000; i++){
double intermediateResult = i;
System.out.println(intermediateResult);
}
¿Cuál es mejor, una o b ?
Sospecho que la declaración variable repetida (ejemplo b ) crea más sobrecarga en teoría , pero que los compiladores son lo suficientemente inteligentes como para que no importe. El ejemplo b tiene la ventaja de ser más compacto y limitar el alcance de la variable al lugar donde se usa. Aún así, tiendo a codificar según el ejemplo a .
Editar: Estoy especialmente interesado en el caso de Java.
java
performance
loops
variables
initialization
Rabarberski
fuente
fuente
Respuestas:
¿Cuál es mejor, a o b ?
Desde una perspectiva de rendimiento, tendrías que medirlo. (Y en mi opinión, si puedes medir la diferencia, el compilador no es muy bueno).
Desde una perspectiva de mantenimiento, b es mejor. Declare e inicialice las variables en el mismo lugar, en el ámbito más estrecho posible. No deje un hueco entre la declaración y la inicialización, y no contamine los espacios de nombres que no necesita.
fuente
Bueno, ejecuté sus ejemplos A y B 20 veces cada uno, haciendo un bucle de 100 millones de veces. (JVM - 1.5.0)
A: tiempo de ejecución promedio: 0,074 segundos
B: tiempo de ejecución promedio: 0,067 segundos
Para mi sorpresa, B fue un poco más rápido. Tan rápido como las computadoras son ahora es difícil decir si podría medir esto con precisión. También lo codificaría como A pero diría que realmente no importa.
fuente
Depende del idioma y el uso exacto. Por ejemplo, en C # 1 no hizo ninguna diferencia. En C # 2, si la variable local es capturada por un método anónimo (o expresión lambda en C # 3), puede hacer una diferencia muy significativa.
Ejemplo:
Salida:
La diferencia es que todas las acciones capturan la misma
outer
variable, pero cada una tiene su propiainner
variable separada .fuente
Outer
9?Lo siguiente es lo que escribí y compilé en .NET.
Esto es lo que obtengo de .NET Reflector cuando CIL se vuelve a representar en el código.
Entonces ambos se ven exactamente iguales después de la compilación. En los idiomas administrados, el código se convierte en código CL / byte y en el momento de la ejecución se convierte en lenguaje máquina. Entonces, en lenguaje de máquina, es posible que ni siquiera se cree un doble en la pila. Puede ser solo un registro ya que el código refleja que es una variable temporal para la
WriteLine
función. Hay un conjunto completo de reglas de optimización solo para bucles. Por lo tanto, el chico promedio no debería preocuparse por eso, especialmente en los idiomas administrados. Hay casos en los que puede optimizar el código de administración, por ejemplo, si tiene que concatenar una gran cantidad de cadenas usando solostring a; a+=anotherstring[i]
vs usandoStringBuilder
. Hay una gran diferencia en el rendimiento entre ambos. Hay muchos casos en los que el compilador no puede optimizar su código, porque no puede averiguar qué se pretende en un ámbito mayor. Pero puede optimizar las cosas básicas para usted.fuente
Este es un problema en VB.NET. El resultado de Visual Basic no reiniciará la variable en este ejemplo:
Esto imprimirá 0 la primera vez (¡las variables de Visual Basic tienen valores predeterminados cuando se declaran!) Pero
i
cada vez después de eso.Sin embargo, si agrega un
= 0
, obtendrá lo que podría esperar:fuente
Hice una prueba simple:
vs
Compilé estos códigos con gcc - 5.2.0. Y luego desarme el main () de estos dos códigos y ese es el resultado:
1º:
vs
2º
Que son exactamente el mismo resultado. ¿No es una prueba de que los dos códigos producen la misma cosa?
fuente
Depende del lenguaje: IIRC C # optimiza esto, por lo que no hay ninguna diferencia, pero JavaScript (por ejemplo) hará todo el shebang de asignación de memoria cada vez.
fuente
Siempre usaría A (en lugar de depender del compilador) y también podría volver a escribir en:
Esto todavía se restringe
intermediateResult
al alcance del bucle, pero no se redeclara durante cada iteración.fuente
En mi opinión, b es la mejor estructura. En a, el último valor de intermediarioResultado se queda después de que finaliza su ciclo.
Editar: Esto no hace mucha diferencia con los tipos de valor, pero los tipos de referencia pueden ser algo pesados. Personalmente, me gusta que las variables sean desreferenciadas lo antes posible para la limpieza, y b lo hace por usted,
fuente
sticks around after your loop is finished
- aunque esto no importa en un lenguaje como Python, donde los nombres enlazados permanecen hasta que finaliza la función.my
palabra clave), C # y Java para nombrar 5 que he usado.Sospecho que algunos compiladores podrían optimizar ambos para ser el mismo código, pero ciertamente no todos. Entonces diría que estás mejor con lo primero. La única razón para esto último es si desea asegurarse de que la variable declarada se use solo dentro de su ciclo.
fuente
Como regla general, declaro mis variables en el ámbito más interno posible. Por lo tanto, si no está utilizando el resultado intermedio fuera del bucle, entonces iría con B.
fuente
Un compañero de trabajo prefiere la primera forma, diciéndole que es una optimización, prefiriendo reutilizar una declaración.
Prefiero el segundo (¡y trato de persuadir a mi compañero de trabajo! ;-)), después de leer eso:
De todos modos, cae en la categoría de optimización prematura que se basa en la calidad del compilador y / o JVM.
fuente
Hay una diferencia en C # si está utilizando la variable en un lambda, etc. Pero, en general, el compilador básicamente hará lo mismo, suponiendo que la variable solo se use dentro del ciclo.
Dado que son básicamente lo mismo: tenga en cuenta que la versión b hace que sea mucho más obvio para los lectores que la variable no se puede usar, y no se puede, después del ciclo. Además, la versión b se refactoriza mucho más fácilmente. Es más difícil extraer el cuerpo del bucle en su propio método en la versión a. Además, la versión b le asegura que no existe ningún efecto secundario en dicha refactorización.
Por lo tanto, la versión a me molesta sin fin, porque no tiene ningún beneficio y hace que sea mucho más difícil razonar sobre el código ...
fuente
Bueno, siempre puedes hacer un alcance para eso:
De esta manera, solo declaras la variable una vez, y morirá cuando abandones el ciclo.
fuente
Siempre he pensado que si declaras tus variables dentro de tu ciclo, estás desperdiciando memoria. Si tienes algo como esto:
Entonces, no solo es necesario crear el objeto para cada iteración, sino que debe haber una nueva referencia asignada para cada objeto. Parece que si el recolector de basura es lento, entonces tendrá un montón de referencias colgantes que deben limpiarse.
Sin embargo, si tienes esto:
Entonces solo está creando una referencia única y asignándole un nuevo objeto cada vez. Claro, puede tomar un poco más de tiempo para que se salga del alcance, pero solo hay una referencia pendiente para tratar.
fuente
Creo que depende del compilador y es difícil dar una respuesta general.
fuente
Mi práctica es la siguiente:
si el tipo de variable es simple (int, double, ...) Prefiero la variante b (dentro).
Motivo: reducción del alcance de la variable.
si el tipo de variable no es simple (algún tipo de
class
ostruct
) prefiero la variante a (fuera).Motivo: reducción del número de llamadas ctor-dtor.
fuente
Desde una perspectiva de rendimiento, el exterior es (mucho) mejor.
Ejecuté ambas funciones mil millones de veces cada una. outside () tomó 65 milisegundos. inside () tardó 1.5 segundos.
fuente
Probé para JS con el Nodo 4.0.0 si alguien está interesado. La declaración fuera del ciclo resultó en una mejora de rendimiento de ~ .5 ms en promedio sobre 1000 pruebas con 100 millones de iteraciones de ciclo por prueba. Así que voy a decir adelante y escribirlo de la manera más legible / mantenible que es B, en mi opinión. Pondría mi código en un violín, pero usé el módulo Node de rendimiento ahora. Aquí está el código:
fuente
A) es una apuesta segura que B) ......... Imagínese si está inicializando la estructura en bucle en lugar de 'int' o 'float', ¿entonces qué?
me gusta
¡Sin duda tendrá problemas de pérdida de memoria! Por lo tanto, creo que 'A' es una apuesta más segura, mientras que 'B' es vulnerable a la acumulación de memoria, especialmente al trabajar con bibliotecas de código cerrado.
fuente
Es una pregunta interesante. Según mi experiencia, hay una pregunta fundamental a tener en cuenta al debatir este asunto para obtener un código:
¿Hay alguna razón por la cual la variable deba ser global?
Tiene sentido declarar la variable solo una vez, globalmente, en lugar de muchas veces localmente, porque es mejor para organizar el código y requiere menos líneas de código. Sin embargo, si solo necesita declararse localmente dentro de un método, lo inicializaría en ese método para que quede claro que la variable es exclusivamente relevante para ese método. Tenga cuidado de no llamar a esta variable fuera del método en el que se inicializa si elige la última opción: su código no sabrá de qué está hablando e informará un error.
Además, como nota al margen, no duplique nombres de variables locales entre diferentes métodos, incluso si sus propósitos son casi idénticos; solo se vuelve confuso.
fuente
esta es la mejor forma
1) de esta manera se declara una vez ambas variables, y no cada una por ciclo. 2) la tarea es más grasa que cualquier otra opción. 3) Entonces, la regla de la mejor práctica es cualquier declaración fuera de la iteración para.
fuente
Probé lo mismo en Go, y comparé la salida del compilador
go tool compile -S
con go 1.9.4Diferencia cero, según la salida del ensamblador.
fuente
Tuve esta misma pregunta durante mucho tiempo. Así que probé un código aún más simple.
Conclusión: Para tales casos, NO hay diferencia de rendimiento.
Caja de bucle exterior
Caja de bucle interior
Revisé el archivo compilado en el descompilador de IntelliJ y para ambos casos, obtuve el mismo
Test.class
También desarme el código para ambos casos usando el método dado en esta respuesta . Solo mostraré las partes relevantes para la respuesta
Caja de bucle exterior
Caja de bucle interior
Si presta mucha atención, solo los
Slot
asignadosi
yintermediateResult
enLocalVariableTable
se intercambian como un producto de su orden de aparición. La misma diferencia en la ranura se refleja en otras líneas de código.intermediateResult
sigue siendo una variable local en ambos casos, por lo que no hay diferencia en el tiempo de acceso.PRIMA
Los compiladores optimizan muchísimo, eche un vistazo a lo que sucede en este caso.
Caso de trabajo cero
Cero trabajo descompilado
fuente
Incluso si sé que mi compilador es lo suficientemente inteligente, no me gustaría confiar en él y utilizaré la variante a).
La variante b) tiene sentido para mí solo si necesita desesperadamente hacer el resultado intermedio no esté disponible después del cuerpo del bucle. Pero no puedo imaginar una situación tan desesperada, de todos modos ...
EDITAR: Jon Skeet hizo un muy buen punto, mostrando que la declaración de variables dentro de un ciclo puede hacer una diferencia semántica real.
fuente