Code optimieren – Die richtige Stelle finden

19. Februar 2009, 8:44 Uhr | Elan Tanzer
Diesen Artikel anhören

Fortsetzung des Artikels von Teil 3

Code optimieren – Die richtige Stelle finden

Da diese Schritte die Geschwindigkeit der Funktion verbesserten und Verzögerungen reduzierten, jedoch die Größe der Funktionen kaum änderten, entscheidet sich George, die ganze Anwendung im nächsten Schritt nicht nur mit --Ospace zu optimieren, sondern auch mit --thumb (Kompilieren in platzsparendem 16-Bit-Thumb-Code statt im schnelleren 32-Bit-ARM-Code). Als Nächstes führt George die Leistungsoptimierung für die zehn wichtigsten Funktionen durch, indem er mit dem »#pragma arm«-Befehl die Funktionen in ARM-Code anstelle von Thumb-Code kompiliert und mit dem »#pragma Otime«-Befehl die Funktionen auf Leistung optimiert. Da diese Schritte nur ein paar Minuten in Anspruch nehmen, kann George den Rest der Stunde für weitere Optimierungen verwenden.

passend zum Thema

Systeminterne Funktionen und »restrict«

Da George die neuesten Features des Compilers kennt, vermutet er, dass er durch Verwenden systeminterner Saturation-Funktionen die Anzahl der Anweisungen für Berechnungen verringern kann. In diesem Fall verwendet er die »_usat«-Funktion, die es ermöglicht, dass die Werte nicht mehr zur Berechnung in den Stapel geladen werden müssen. Sie weist den Prozessor an, bestimmte Werte bei Überschreiten eines angegebenen Grenzwerts auf diesen zurückzusetzen. Die xvid-Anwendung verwendet 16 Millionen Farben und enthält eine Berechnung in der Funktion »yv12_to_rgb555_c«, die sicherstellt, dass die Werte für jede Farbe nie kleiner als 0 oder größer als 255 sind. Durch Ersetzen der Berechnungen (Kasten »Werte in den Stack laden«) in der Funktion »yv12_to_rgb555_c« durch die systeminternen Saturation-Funktionen (Kasten » Systeminterne Funktionen übernehmen «) kann George noch einmal die Anwendungszeit verkürzen, ohne die Größe zu beeinträchtigen.

Nachdem George nun den Aufwand für Berechnungen reduziert hat, baut er weiteren Overhead ab, indem er das unnötige Neuladen von Werten nach dem Speichern verhindert. Eines der Schlüsselwörter in C99 heißt »restrict«. Es informiert den Compiler darüber, dass zwei verschiedene Zeiger auf zwei unterschiedliche, sich nicht überlagernde Bereiche im Speicher verweisen. Diese Zeiger werden als Nicht-Aliasing-Zeiger bezeichnet (Bild 2).

Taucht ein restrict auf, so weiß der Compiler, dass eine Speicherung durch einen Zeiger nicht die Werte beeinträchtigt, die durch einen anderen Zeiger geladen wurden. George weiß, dass seine Zeiger kein Aliasing verursachen, und deshalb fügt er den Zeigerdeklarationen das Schlüsselwort __restrict (C99-Erweiterung an C89) hinzu (Kasten »restrict«). Danach erstellt er die Anwendung erneut, führt sie aus und vergleicht die Ergebnisse. Dieses Schlüsselwort sollte natürlich nur zum Einsatz kommen, wenn sich die Zeiger sicher nicht überlagern.

Durch die verschiedenen Compileroptimierungen, systemeigenen Funktionen und Schlüsselwörter konnte George die Anwendung so optimieren, dass sie um 30% schneller und um 26% kleiner wurde. (mc)

Elan Tanzer
ist Technical Market Engineer in der System Design Division von

ARM
Telefon 089/92 86 15 0
www.arm.com

Siehe auch:

Unit-Tests mit Open-Source-Werkzeugen

Auf den Spuren der Fehler

job_07_Bild02__af_04.jpg
Bild 2: Die Zeiger p und q sind Aliase; u und v sind keine Aliase

Code-Listings:

#pragma

193 // I_FinishUpdate
194 //
195 #pragma push
196 #pragma ARM
197 #pragma --Otime
198 #pragma --O3
199 void I_FinishUpdate (void)
...
226 lcdupdate = 1;
227 while (lcdupdate);
228 }
229 #pragma pop
...

Systeminterne Funktionen übernehmen

*(uint16_t *) (x_ptr ) =
    ((__usat(r[0],8) << 7) & 0x7c00) |
    ((__usat(g[0],8) << 2) & 0x03e0) |
    ((__usat(b[0],8) >> 3) & 0x001f);

Werte in den Stack laden

*(uint16_t *) (x_ptr ) =
    (((0>(((255)<(r[0])?(255):(r[0])))?0
    :(((255)<(r[0])?(255):(r[0]))))<<7)&
    0x7c00)|
    (((0>(((255)<(g[0])?(255):(g[0])))?0
    :(((255)<(g[0])?(255):(g[0]))))<<2)&
    0x03e0)|
    (((0>(((255)<(b[0])?(255):(b[0])))?0
    :(((255)<(b[0])?(255):(b[0]))))>>3)&
    0x001f);

restrict

void yv12_to_rgb555_c (uint8_t *
__restrict x_ptr, int x_stride, uint8_t
* __restrict y_ptr,
    uint8_t * __restrict u_ptr, uint8_t
* __restrict v_ptr, int y_stride,
    int uv_stride, int width,
    int height, int vflip)


  1. Code optimieren – Die richtige Stelle finden
  2. Auswählen und Optimieren des Algorithmus
  3. Echte Verwendungsszenarien
  4. Code optimieren – Die richtige Stelle finden

Jetzt kostenfreie Newsletter bestellen!