Allgemeine Optimierungen
Hier einige meiner Lieblingsoptimierungen. Ich habe tatsächlich die Ausführungszeiten verlängert und die Programmgröße reduziert, indem ich diese verwendet habe.
Deklarieren Sie kleine Funktionen als inline
oder Makros
Jeder Aufruf einer Funktion (oder Methode) verursacht Overhead, z. B. das Verschieben von Variablen auf den Stapel. Einige Funktionen können auch bei der Rücksendung einen Overhead verursachen. Eine ineffiziente Funktion oder Methode enthält weniger Anweisungen als der kombinierte Overhead. Dies sind gute Kandidaten für Inlining, sei es als #define
Makros oder als inline
Funktion. (Ja, ich weiß, es inline
ist nur ein Vorschlag, aber in diesem Fall betrachte ich ihn als Erinnerung an den Compiler.)
Entfernen Sie toten und redundanten Code
Wenn der Code nicht verwendet wird oder nicht zum Ergebnis des Programms beiträgt, entfernen Sie ihn.
Vereinfachen Sie das Design von Algorithmen
Ich habe einmal viel Assembler-Code und Ausführungszeit aus einem Programm entfernt, indem ich die berechnete algebraische Gleichung aufgeschrieben und dann den algebraischen Ausdruck vereinfacht habe. Die Implementierung des vereinfachten algebraischen Ausdrucks nahm weniger Platz und Zeit in Anspruch als die ursprüngliche Funktion.
Abwickeln der Schleife
Jede Schleife hat einen Aufwand für die Inkrementierung und Abschlussprüfung. Um eine Schätzung des Leistungsfaktors zu erhalten, zählen Sie die Anzahl der Anweisungen im Overhead (mindestens 3: Inkrementieren, Überprüfen, zum Start der Schleife) und dividieren Sie durch die Anzahl der Anweisungen innerhalb der Schleife. Je niedriger die Zahl, desto besser.
Bearbeiten: Geben Sie ein Beispiel für das Abrollen der Schleife. Vorher:
unsigned int sum = 0;
for (size_t i; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
Nach dem Abrollen:
unsigned int sum = 0;
size_t i = 0;
**const size_t STATEMENTS_PER_LOOP = 8;**
for (i = 0; i < BYTES_TO_CHECKSUM; **i = i / STATEMENTS_PER_LOOP**)
{
sum += *buffer++; // 1
sum += *buffer++; // 2
sum += *buffer++; // 3
sum += *buffer++; // 4
sum += *buffer++; // 5
sum += *buffer++; // 6
sum += *buffer++; // 7
sum += *buffer++; // 8
}
// Handle the remainder:
for (; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
In diesem Vorteil wird ein sekundärer Vorteil erzielt: Es werden mehr Anweisungen ausgeführt, bevor der Prozessor den Anweisungscache neu laden muss.
Ich habe erstaunliche Ergebnisse erzielt, als ich eine Schleife mit 32 Anweisungen abgewickelt habe. Dies war einer der Engpässe, da das Programm eine Prüfsumme für eine 2-GB-Datei berechnen musste. Diese Optimierung in Kombination mit dem Blocklesen verbesserte die Leistung von 1 Stunde auf 5 Minuten. Das Abrollen der Schleife lieferte auch in Assemblersprache eine hervorragende Leistung. Meine memcpy
war viel schneller als die des Compilers memcpy
. - TM
Reduzierung von if
Aussagen
Prozessoren hassen Verzweigungen oder Sprünge, da sie den Prozessor zwingen, seine Anweisungswarteschlange neu zu laden.
Boolesche Arithmetik ( Bearbeitet: Codeformat auf Codefragment angewendet, Beispiel hinzugefügt)
Konvertieren Sie if
Anweisungen in boolesche Zuweisungen. Einige Prozessoren können Anweisungen ohne Verzweigung bedingt ausführen:
bool status = true;
status = status && /* first test */;
status = status && /* second test */;
Der Kurzschluss des logischen UND- Operators ( &&
) verhindert die Ausführung der Tests, wenn dies der Fall status
ist false
.
Beispiel:
struct Reader_Interface
{
virtual bool write(unsigned int value) = 0;
};
struct Rectangle
{
unsigned int origin_x;
unsigned int origin_y;
unsigned int height;
unsigned int width;
bool write(Reader_Interface * p_reader)
{
bool status = false;
if (p_reader)
{
status = p_reader->write(origin_x);
status = status && p_reader->write(origin_y);
status = status && p_reader->write(height);
status = status && p_reader->write(width);
}
return status;
};
Zuordnung von Faktorvariablen außerhalb von Schleifen
Wenn eine Variable im laufenden Betrieb innerhalb einer Schleife erstellt wird, verschieben Sie die Erstellung / Zuordnung vor die Schleife. In den meisten Fällen muss die Variable nicht bei jeder Iteration zugewiesen werden.
Faktor konstante Ausdrücke außerhalb von Schleifen
Wenn eine Berechnung oder ein variabler Wert nicht vom Schleifenindex abhängt, verschieben Sie ihn außerhalb (vor) der Schleife.
E / A in Blöcken
Lesen und Schreiben von Daten in großen Blöcken. Je größer desto besser. Zum Beispiel ist das Lesen von jeweils einem Oktekt weniger effizient als das Lesen von 1024 Oktetten mit einem Lesevorgang.
Beispiel:
static const char Menu_Text[] = "\n"
"1) Print\n"
"2) Insert new customer\n"
"3) Destroy\n"
"4) Launch Nasal Demons\n"
"Enter selection: ";
static const size_t Menu_Text_Length = sizeof(Menu_Text) - sizeof('\0');
//...
std::cout.write(Menu_Text, Menu_Text_Length);
Die Effizienz dieser Technik kann visuell demonstriert werden. :-)
Verwenden Sie printf
family nicht für konstante Daten
Konstante Daten können mit einem Blockschreibvorgang ausgegeben werden. Beim formatierten Schreiben wird Zeit damit verschwendet, den Text nach Formatierungen zu durchsuchen oder Formatierungsbefehle zu verarbeiten. Siehe obiges Codebeispiel.
In den Speicher formatieren und dann schreiben
Formatieren Sie char
mit mehreren in ein Array und verwenden Sie sprintf
dann fwrite
. Dadurch kann das Datenlayout auch in "konstante Abschnitte" und variable Abschnitte unterteilt werden. Denken Sie an Seriendruck .
Deklarieren Sie konstanten Text (String-Literale) als static const
Wenn Variablen ohne das deklariert werden static
, weisen einige Compiler möglicherweise Speicherplatz auf dem Stapel zu und kopieren die Daten aus dem ROM. Dies sind zwei unnötige Operationen. Dies kann mithilfe des static
Präfixes behoben werden .
Schließlich Code wie der Compiler würde
Manchmal kann der Compiler mehrere kleine Anweisungen besser optimieren als eine komplizierte Version. Auch das Schreiben von Code zur Optimierung des Compilers hilft. Wenn der Compiler spezielle Blockübertragungsanweisungen verwenden soll, schreibe ich Code, der die speziellen Anweisungen verwenden sollte.