Programmierprinzipien in Bezug auf Software- (Rechen-) Effizienz und die Verwendung von Variablen

8

Ich bin ein klassisch ausgebildeter Psychologe und kein Programmierer. Manchmal entgehen mir die fortgeschritteneren Aspekte der Programmierung, insbesondere in Bezug auf die Programmeffizienz und / oder bestimmte Best Practices, in diesem Fall in Bezug auf die Verwendung von Variablen.

Hier ist ein Pseudocode:

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

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

Meine Frage lautet also:
Ist es mehr oder weniger effizient (oder gleich), Daten einmal in einer Variablen zu speichern und darauf zu verweisen, im Gegensatz zu wiederholten Aufrufen derselben Funktion?

IE, ist dieser Code effizienter?

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

Wenn ja, ist dieser Effizienzgewinn oder -verlust im Allgemeinen sprachübergreifend gleich oder variiert er je nach Sprache? Gibt es Schwellenwerte, bei denen es besser wird, eine Variable zu benennen, als einen wiederholten Funktionsaufruf zu verwenden oder umgekehrt? Welche Aspekte können die Effizienz verändern (z. B. gibt es einen Unterschied, ob es sich um eine Mitgliedsfunktion einer Klasse handelt oder um eine reguläre Funktion im globalen Bereich)? usw.

Wenn möglich, möchte ich speziell wissen, wie ein solcher Begriff für C ++ / MFC-Dialoge gilt, wie er beim Schreiben von Code in diesem Framework entstanden ist.

// 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));
Stoicfury
quelle

Antworten:

10

Zur Beantwortung Ihrer Frage: Es ist in der Regel mehr effizient zum Speichern von Daten in einer Variablen und Referenz dass. Zusätzliche lokale Variablen sind billig.

Angenommen, Sie haben das Codefragment, beginnend mit einem einfachen Beispiel:

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

Wenn Sie sich den obigen Code ansehen und so tun, als wären Sie die CPU, die ihn Schritt für Schritt ausführt, würde die CPU die Multiplikation zweimal mit demselben Wert von durchführen x. Das ist fast immer weniger effizient als die einmalige Multiplikation:

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

Heutzutage haben moderne Compiler fast immer eine Optimierung, die als gemeinsame Eliminierung von Unterausdrücken bezeichnet wird und den gleichen Effekt beim Extrahieren hat x2wie im obigen Beispiel. Sie müssen sich also oft keine Sorgen machen, weil der Compiler Ihren Rücken hat.

Die Verwendung einer Variablen, wie x2sie die Komplexität der folgenden Zeilen erheblich verringern könnte, ist jedoch aus Gründen der Lesbarkeit nichts Falsches daran, eine solche Variable einzuführen.

Bei Ihrem MFC-Code rufen Sie wiederholt an pLISTBOX->GetItemData(SelectedIndex). Da dies ein Funktionsaufruf ist, der das Betriebssystem aufruft, um weitere Daten abzurufen, kann der Compiler diesbezüglich keine gemeinsame Beseitigung von Unterausdrücken durchführen. Stattdessen würde ich eine neue Variable einführen, sodass Sie den Aufruf nur einmal ausführen müssen:

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
quelle
1
Vielleicht möchten Sie hinzufügen, dass CSE nur im ursprünglichen Beispiel funktioniert, wenn die aufgerufene Funktion ( GetSomeInformation) rein ist und der Compiler sich dieser Tatsache bewusst ist. Andernfalls muss der Compiler es dreimal aufrufen, um sicherzustellen, dass die Nebenwirkungen wie erwartet auftreten.
tdammers
1
+1 für das Zitieren des Optimierers. Das OP muss verstehen, dass der Quellcode lediglich eine Empfehlung für den Compiler zum Erstellen von Objektcode ist.
Ross Patterson
3
  1. Ja, der zweite Code ist in den meisten Sprachen geringfügig effizienter als der erste Code.
  2. Es macht wahrscheinlich keinen praktischen Unterschied.
  3. Wahrscheinlich macht einen Unterschied in der Lesbarkeit machen, so mit dem saubereren Code gehen.

Außerdem wird ein guter Compiler (einschließlich Javas JIT) wiederholte Methodenaufrufe einbinden und wiederholt verwendete Werte zwischenspeichern, sodass die beiden Beispiele wahrscheinlich eine vergleichbare Leistung erzielen würden.

Beachten Sie die folgenden Faustregeln:

Lass es funktionieren, mach es richtig, mach es schnell ... in dieser Reihenfolge. - Kent Beck

Die erste Regel der Optimierung : Nicht.

Die zweite Optimierungsregel : Noch nicht.

Die dritte Optimierungsregel : Profil vor der Optimierung

Vorzeitige Optimierung ist die Wurzel allen Übels. - Donald Knuth

In den meisten Fällen werden> 90% der Zeit Ihres Programms in <10% des Codes verbracht, und ohne Profilierung Ihres Codes haben Sie keine Ahnung, welche 10% das sind. Machen Sie sich also erst Sorgen, wenn Sie Feedback erhalten. Es ist viel wertvoller, die Programmiererzeit zu optimieren als die Laufzeit .

Alex Chaffee
quelle
3

Es ist normalerweise effizienter, einen berechneten Wert in einer lokalen Variablen zu speichern, vorausgesetzt, Ihr Compiler hat dies nicht bereits für Sie optimiert. Tatsächlich wird dies manchmal in eine Funktion selbst eingebaut, in einer Technik, die als Memoisierung bezeichnet wird .

Meistens ist der Wirkungsgrad jedoch klein genug, um als vernachlässigbar angesehen zu werden. Die Lesbarkeit sollte in der Regel nach der Richtigkeit Ihr Hauptanliegen sein.

Die Verwendung einer lokalen Variablen macht sie jedoch häufig lesbarer und bietet Ihnen das Beste aus beiden Welten. Anhand Ihres Beispiels:

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

Dies ist für einen menschlichen Leser viel klarer. Sie müssen nicht nur diesen langen Funktionsaufruf nicht in jeder Zeile lesen, es ist auch ausdrücklich klar, dass alle Widgets genau denselben Text erhalten.

Karl Bielefeldt
quelle
2

Variablen sind im Wesentlichen kostenlos, aber der Funktionsaufruf hat unbekannte Kosten. Gehen Sie also immer mit der Variablen.

DeadMG
quelle
1

Ein allgemeiner Grundsatz I folgen ist , dass , wenn ich nicht weiß , wie eine Funktion implementiert ist, es könnte teuer sein , und wenn ich weiß , dass ich nur den Wert aus dieser Funktion einmal brauchen, ich kann es nur lokal speichern. Ihr Codebeispiel würde dann werden:

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