Principios de programación con respecto a la eficiencia del software (computacional) y el uso de variables

8

Soy un psicólogo con formación clásica, no un programador, por lo que a veces los aspectos más avanzados de la programación se me escapan, en particular con respecto a la eficiencia del programa y / o ciertas mejores prácticas, en este caso con respecto al uso de variables.

Aquí hay un pseudocódigo:

var a;
var b;
var c;
function GetSomeInformation() {
    returns "XYZ";
}

a = GetSomeInformation();
b = GetSomeInformation();
c = GetSomeInformation();

Entonces mi pregunta es:
¿es más o menos eficiente (o lo mismo) almacenar datos en una variable una vez y hacer referencia a eso en lugar de llamadas repetidas a la misma función?

IE, ¿es este código más eficiente?

var results = GetSomeInformation();
a = results;
b = results;
c = results;

Si es así, ¿esta ganancia o pérdida de eficiencia es generalmente la misma en todos los idiomas, o varía según el idioma? ¿Hay umbrales en los que es mejor nombrar una variable en lugar de usar una llamada de función repetida o viceversa? ¿Qué aspectos podrían cambiar su eficiencia (p. Ej., ¿Hay alguna diferencia si es una función miembro de una clase o una función regular en el ámbito global)? etc.

Si es posible, me gustaría saber específicamente cómo se aplica dicha noción a los diálogos de C ++ / MFC, ya que surgió cuando estaba escribiendo algún código en ese marco.

// define pointers to the items in my form
CListBox *pLISTBOX = (CListBox*) GetDlgItem(LISTBOX);
CStatic *pSTATIC_A = (CStatic*) GetDlgItem(STATIC_A);
CStatic *pSTATIC_B = (CStatic*) GetDlgItem(STATIC_B);
CEdit *pEDIT_BOX_A = (CEdit*) GetDlgItem(EDIT_BOX_A);
CEdit *pEDIT_BOX_B = (CEdit*) GetDlgItem(EDIT_BOX_B);
int SelectedIndex = pLISTBOX->GetCurSel();
pSTATIC_A->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pSTATIC_B->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pEDIT_BOX_A->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pEDIT_BOX_B->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
furia estoica
fuente

Respuestas:

10

Para responder a su pregunta: generalmente es más eficiente almacenar datos en una variable y hacer referencia a eso. Las variables locales adicionales son baratas.

Comenzando con un ejemplo simple, suponga que tiene el fragmento de código:

x = 5;
y = x*x + 1;
z = x*x + 2;

Si observa el código anterior y finge que es la CPU ejecutándolo paso a paso, la CPU haría la multiplicación dos veces con el mismo valor de x. Eso casi siempre es menos eficiente que hacer la multiplicación una vez:

x = 5;
x2 = x*x;
y = x2 + 1;
z = x2 + 2;

Ahora, los compiladores modernos casi siempre tienen una optimización llamada eliminación de subexpresión común , que tendrá el mismo efecto de extracción x2que en el ejemplo anterior. Por lo tanto, a menudo no tiene que preocuparse por eso, porque el compilador lo respalda.

Dicho esto, el uso de una variable como x2podría reducir sustancialmente la complejidad de las siguientes líneas, por lo que no hay nada de malo en introducir una variable como esa por razones de legibilidad.

En el caso de su código MFC, está llamando repetidamente pLISTBOX->GetItemData(SelectedIndex). Dado que esta es una llamada de función que hace una llamada al sistema operativo para obtener más datos, el compilador no puede hacer una eliminación de subexpresión común al respecto. En cambio, introduciría una nueva variable para que solo tenga que hacer la llamada una vez:

int SelectedIndex = pLISTBOX->GetCurSel();
const char *data = pLISTBOX->GetItemData(SelectedIndex);
pSTATIC_A->SetWindowText(data);
pSTATIC_B->SetWindowText(data);
pEDIT_BOX_A->SetWindowText(data);
pEDIT_BOX_B->SetWindowText(data);
Greg Hewgill
fuente
1
Es posible que desee agregar que CSE solo funciona en el ejemplo original si la función llamada ( GetSomeInformation) es pura y el compilador es consciente de este hecho. De lo contrario, el compilador tiene que llamarlo tres veces para asegurarse de que los efectos secundarios ocurran como se esperaba.
tdammers
1
+1 por citar el optimizador. El OP debe comprender que el código fuente es simplemente un consejo para el compilador sobre cómo crear código objeto.
Ross Patterson
3
  1. Sí, el segundo código es marginalmente más eficiente que el primer código, en la mayoría de los idiomas.
  2. Probablemente no haga ninguna diferencia práctica.
  3. Probablemente hace hacer una diferencia en la lectura, por lo que ir con el código más limpio.

Además, un buen compilador (incluido el JIT de Java) incorporará llamadas a métodos repetidos y almacenará en caché los valores utilizados repetidamente, por lo que los dos ejemplos probablemente terminarían teniendo un rendimiento comparable.

Consulte las siguientes reglas generales:

Haz que funcione, hazlo bien, hazlo rápido ... en ese orden. - Kent Beck

La primera regla de optimización : no.

La segunda regla de optimización : no ... todavía.

La tercera regla de optimización : perfil antes de optimizar

La optimización prematura es la fuente de todos los males. - Donald Knuth

En la mayoría de los casos, se gastará> 90% del tiempo de su programa en <10% del código, y sin perfilar su código, no tiene idea de qué 10% es ese. Así que ni se preocupe por eso hasta que reciba comentarios; Es mucho más valioso optimizar el tiempo del programador que el tiempo de ejecución .

Alex Chaffee
fuente
3

Por lo general, es más eficiente almacenar un valor calculado en una variable local, suponiendo que su compilador no lo haya optimizado para usted. De hecho, a veces esto está integrado en una función en sí misma, en una técnica llamada memorización .

Sin embargo, la mayoría de las veces, la ganancia de eficiencia es lo suficientemente pequeña como para considerarse insignificante. La legibilidad suele ser su principal preocupación después de la corrección.

Dicho esto, el uso de una variable local también suele hacerlo más legible, ofreciéndote lo mejor de ambos mundos. Usando tu ejemplo:

const char* text = pLISTBOX->GetItemData(SelectedIndex);
pSTATIC_A->SetWindowText(text);
pSTATIC_B->SetWindowText(text);
pEDIT_BOX_A->SetWindowText(text);
pEDIT_BOX_B->SetWindowText(text);

Esto es mucho más claro para un lector humano. No solo no tiene que leer esa llamada de función larga en cada línea, está explícitamente claro que todos los widgets reciben exactamente el mismo texto.

Karl Bielefeldt
fuente
2

Las variables son esencialmente gratuitas, pero la llamada a la función tiene un costo desconocido, así que siempre vaya con la variable.

DeadMG
fuente
1

Un principio general que sigo es que si no cómo se implementa una función, puede ser costoso y si que solo necesito el valor de esa función una vez, simplemente lo almaceno localmente. Su ejemplo de código se convertiría en:

// define pointers to the items in my form
//...
int SelectedIndex = pLISTBOX->GetCurSel();
String text = pLISTBOX->GetItemData(SelectedIndex); //Ok, so maybe String isn't a valid type here but you get the idea...
pSTATIC_A->SetWindowText(text);
pSTATIC_B->SetWindowText(text);
pEDIT_BOX_A->SetWindowText(text);
pEDIT_BOX_B->SetWindowText(text);
FrustratedWithFormsDesigner
fuente