Princípios de programação em relação à eficiência (computacional) do software e ao uso de variáveis

8

Sou psicólogo com formação clássica, não sou programador, portanto, às vezes, os aspectos mais avançados da programação me escapam, especialmente em relação à eficiência do programa e / ou a certas práticas recomendadas, neste caso no que diz respeito ao uso de variáveis.

Aqui está algum pseudo-código:

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

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

Portanto, minha pergunta é:
É mais ou menos eficiente (ou o mesmo) armazenar dados em uma variável uma vez e referenciar isso em oposição a chamadas repetidas para a mesma função?

IE, esse código é mais eficiente?

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

Em caso afirmativo, esse ganho ou perda de eficiência geralmente é o mesmo entre os idiomas ou varia de acordo com o idioma? Existem limites nos quais se torna melhor nomear uma variável em vez de usar uma chamada de função repetida ou vice-versa? Que aspectos podem mudar sua eficiência (por exemplo, existe uma diferença se é uma função de membro de uma classe versus uma função regular no escopo global)? etc.

Se possível, gostaria de saber especificamente como essa noção se aplica às caixas de diálogo C ++ / MFC, como surgiu quando eu estava escrevendo algum código nessa estrutura.

// 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));
fúria estoica
fonte

Respostas:

10

Para responder à sua pergunta: Geralmente é mais eficiente armazenar dados em uma variável e referenciá-la. Variáveis ​​locais adicionais são baratas.

Começando com um exemplo simples, suponha que você tenha o fragmento de código:

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

Se você olhar para o código acima e fingir que é a CPU executando passo a passo, a CPU faria a multiplicação duas vezes com o mesmo valor de x. Isso quase sempre é menos eficiente do que fazer a multiplicação uma vez:

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

Agora, os compiladores modernos quase sempre têm uma otimização chamada eliminação de subexpressão comum , que terá o mesmo efeito de extração x2que no exemplo acima. Portanto, muitas vezes você não precisa se preocupar com isso, porque o compilador está à sua disposição.

Dito isto, o uso de uma variável como x2pode reduzir substancialmente a complexidade das linhas a seguir; portanto, não há nada errado em introduzir uma variável como essa por motivos de legibilidade.

No caso do seu código MFC, você está chamando repetidamente pLISTBOX->GetItemData(SelectedIndex). Como essa é uma chamada de função que faz uma chamada ao sistema operacional para obter mais dados, o compilador não pode fazer a eliminação de subexpressão comum. Em vez disso, eu introduziria uma nova variável para que você precise fazer a chamada apenas uma 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
fonte
1
Pode-se acrescentar que o CSE só funciona no exemplo original se a função chamada ( GetSomeInformation) for pura e o compilador estiver ciente desse fato. Caso contrário, o compilador precisará chamá-lo três vezes para garantir que os efeitos colaterais ocorram conforme o esperado.
tdammers
1
+1 por citar o otimizador. O OP precisa entender que o código fonte é apenas um conselho ao compilador sobre como criar código de objeto.
Ross Patterson
3
  1. Sim, o segundo código é marginalmente mais eficiente que o primeiro, na maioria dos idiomas.
  2. Provavelmente não faz nenhuma diferença prática.
  3. Provavelmente faz a diferença na legibilidade, então vá com o código mais limpo.

Além disso, um bom compilador (incluindo o JIT do Java) alinhará chamadas de método repetidas e armazenará em cache valores usados ​​repetidamente, de modo que os dois exemplos provavelmente acabariam tendo um desempenho comparável.

Consulte as seguintes regras práticas:

Faça funcionar, faça certo, faça rápido ... nessa ordem. - Kent Beck

A primeira regra de otimização : não.

A segunda regra de otimização : não ... ainda.

A terceira regra de otimização : perfil antes de otimizar

Otimização prematura é a raiz de todo o mal. - Donald Knuth

Na maioria dos casos,> 90% do tempo do seu programa será gasto em <10% do código e, sem criar um perfil do seu código, você não tem idéia de quais 10% são. Portanto, nem se preocupe com isso até receber feedback; é muito mais valioso otimizar o tempo do programador do que o tempo de execução .

Alex Chaffee
fonte
3

Geralmente é mais eficiente armazenar um valor calculado em uma variável local, supondo que seu compilador ainda não otimize isso para você. De fato, às vezes isso é incorporado à própria função, em uma técnica chamada memoização .

No entanto, na maioria das vezes, o ganho de eficiência é pequeno o suficiente para ser considerado insignificante. A legibilidade geralmente deve ser sua principal preocupação após a correção.

Dito isto, o uso de uma variável local também costuma torná-lo mais legível, oferecendo o melhor dos dois mundos. Usando seu exemplo:

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

Isso é muito mais claro para um leitor humano. Não apenas você não precisa ler essa chamada de função longa em todas as linhas, mas é explicitamente claro que todos os widgets estão recebendo exatamente o mesmo texto.

Karl Bielefeldt
fonte
2

As variáveis ​​são essencialmente gratuitas, mas a chamada de função tem um custo desconhecido, portanto, sempre vá com a variável.

DeadMG
fonte
1

Um princípio geral que sigo é que, se eu não souber como uma função é implementada, pode ser caro e se eu souber que só preciso do valor dessa função uma vez, apenas a armazeno localmente. Seu exemplo de código se tornaria:

// 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
fonte