In this portal I have met people who think that the scope of the variables should be the minimum essential while others think that it is best to extend their life to the maximum possible.
What advantages does each of these two options provide?
In this portal I have met people who think that the scope of the variables should be the minimum essential while others think that it is best to extend their life to the maximum possible.
What advantages does each of these two options provide?
General norm
As a general rule, it is advisable to reduce the scope of variables as much as possible. This statement is supported by the fact that sharing a variable for different uses often makes the code more difficult to read and maintain.
If, on top of that, we declare the variables as global, the matter becomes even more complicated and it's not worth mentioning if, apart from global variables, we start using threads.
Scope vs performance (native types)
A detail that is usually alleged in defense of extending the life of variables is the issue of performance. It is defended that declaring the variable only once prevents it from having to be created and destroyed, thus improving the performance of the application.
This is not technically correct, at least not with modern compilers. To demonstrate this we will use an example like the following:
The functions are marked as
extern
because I don't care about their content or that the compiler converts them toinline
. I do it like this to make the examples simpler.If we compile this code in release mode and analyze the resulting assembler (for this you can use this tool ) we get a sequence like the following (example with gcc 6.2 compiled with -O3):
The code is basically structured as follows:
num
it is worth 0 it skips directly to the end of the function.As you can see, the compiler is making use of the registers
ebx
for the first loop andebp
for the second. Instead of creating the variable on the stack, it is making use of the processor's internal registers. The cost of creating the variable is, in this case, 0.Now we go with a second version. In this case, the scope of the variable will be reduced to the scope of the loop itself:
The resulting assembler is the following:
If both sequences are compared, we see that they are exactly the same. The exact same registers are used
ebx
andebp
for each loop then creating the variables is a free process .What happens then if we have nested loops?
Given the following code:
The resulting assembler is the following:
We see that the processor registers are used again. In this case you are using
ebx
andr12d
, but they are still processor registers, so the cost of creating the variablej
on each iteration of the first loop is 0.It is then clear that when using native types there is no difference between sharing variables or not, so the supposed benefits of extending the scope of variables is, in this case, a false myth.
Scope vs performance (structures and classes)
For this example we are going to create a wrapper that encapsulates an integer and implements the minimum functions necessary for the code to compile:
what happens in this case? Let's see:
Surprisingly the code is practically the same. The compiler is able to extract the integer from the wrapper and generate code just as efficient as in the previous cases.
Now we only need to check what happens in the case of more complex classes. An example using
std::string
outside of the loop:And its output:
Now the code is a little more complex to read because code belonging to the constructor and destructor of the class is being called
string
.If we now move the creation of
string
the into the loop:We are left with the following:
The most noticeable change is that the build instructions have been moved:
and destruction of the string:
Inside the loop.
In this case, you could see a decrease in performance in the case of declaring the variables inside the loop... but wait... we are talking about a string with a fixed value. What would happen if the value of the string is changed on each iteration?
Let's first see what happens if we declare the variable outside of the loop:
Which results in:
And now we are going to leave the string inside the loop:
The result is as follows:
Para empezar vemos que si dejamos el
string
fuera del bucle el compilador realiza tres llamadas al destructor (al final de.L13
,.L4
y.L9
) mientras que si declaramos la cadena dentro del bucle únicamente se llama al destructor en dos ocasiones (final de.L9
y.L1
). Además vemos que, en el caso de declarar la variable fuera del bucle, se realizan dos llamadas al constructor:Llamada al constructor por defecto
Y al constructor copia:
Luego podemos ver que el supuesto beneficio de alargar la vida de las clases realmente no tiene por qué ser tan beneficioso. En el ejemplo propuesto declarar la clase fuera del bucle origina un código más largo y más lento que si intentamos reducir el ámbito de las variables al mínimo.
Conclusión
Se podrían presentar ejemplos en los que sacar las variables fuera del bucle daría como resultado un código más rápido. Aquí únicamente pretendía demostrar que afirmar categóricamente que eso de ampliar el scope de las variables es beneficioso es un mito. Unas veces será beneficioso y otras no.
Entonces, ¿cuándo hay que optar por una solución u otra? Mi recomendación es, en este caso, intentar reducir por costumbre el ámbito al mínimo. La necesidad de ampliar la vida de las variables es algo que debería surgir de forma natural si el algoritmo no cumple con los requisitos de velocidad (requisitos que pocas veces existen) y únicamente cuando un profiler te diga que el cambio es beneficioso para tus intereses.
Lo anterior lo comento porque es ciencia cierta que cualquier programador tiene unas aptitudes para encontrar cuellos de botella propias de un hámster, sobretodo en lenguajes orientados a objetos y con código de cierta complejidad. Soy consciente de que a todos nos pasa que en un momento dado desechamos una idea porque automáticamente intentamos medir mentalmente su rendimiento y deducimos que será demasiado pobre... dejemos que sea un análisis real el que nos confirme nuestras sospechas en vez de desechar buenas ideas basándonos en teorías efímeras afectadas por nuestro humor y nuestro cansancio.
Nota final: Si este hilo tiene buena aceptación consideraré marcar la respuesta como wiki de comunidad. Aun así me gustaría que más gente aportarse su punto de vista.