Optimierung mal anders

Aus der Kategorie, „Spaß am Gerät, den man lieber nicht hätte“: Amoklaufende Optimierung im GCC.

Man lese folgenden Code, der alternierend ein LCD mit Zahlen, Buchstaben und „nichts“ füllt:

while (1) {
	for (unsigned char k=0;k<4;k++)  {
		LCD_setCursorPos(k,0);
		LCD_write("1234567890123456");
	}
	delay(250);
	LCD_clear();
	delay(250);
	for (unsigned char k=0;k<4;k++)  {
		LCD_setCursorPos(k,0);
		LCD_write("abcdefghijklmnop");
	}
	delay(250);
}

What could possibly go wrong ❓

Gut, wir kennen ja Murphy: Alles, was schief gehen kann, geht schief. Und wenn schon der Code kaum Fehlerpotential hat, muss zwangsläufig der Compiler seinen Beitrag dazu leisten ❗

Genau das hat er dann auch getan… erstmal äußerte sich das ganze dann darin, dass wider erwarten nur Zahlen, leere Seite und wieder Zahlen kamen. Und dabei die Seite mit den Zahlen irgendwie „zu lang“ zu sehen war.
Was war also los?
Wie in Blick in das praktischerweise im Makefile angeforderte Extended Listing verrät, ist avr-gcc der Meinung, die beiden Schleifen würden das Gleiche machen, und kombiniert die in eine Schleife. Durch Ausprobieren anderer Optimierungsstufen zwischen -Os und -O1 kann man dann gcc immerhin überreden, nicht mehr beides in einer Schleife zu erledigen, sondern die zweite komplett zu ignorieren (toll!)… auch nicht ganz das Wahre. -O0 erzeugt Warnungen in <util/delay.h>, fällt also leider aus.

Die einzige mir bekannte Lösung ist das verlegen der beiden Schleifen in separate Methoden:

void test1(void)
{
	for (unsigned char k=0;k<4;k++)  {
		LCD_setCursorPos(k,0);
		LCD_write("1234567890123456");
	}
	delay(250);
}
 
void test2(void)
{
	for (unsigned char k=0;k<4;k++)  {
		LCD_setCursorPos(k,0);
		LCD_write("abcdefghijklmnop");
	}
	delay(250);
}
while (1) {
	test1();
	LCD_clear();
	delay(250);
	test2();
}

Gut, man muss eingestehen, der gcc hier ist schon etwas (avr-gcc (WinAVR 20080610) 4.3.0) älter. Das ist in einigen Kompatibilitätsproblemen mit anderen Projekten geschuldet, die sich auf ein bestimmtes Verhalten verlassen (hey, ich hab das nicht erfunden, ok?)
Kann also sein, dass das mittlerweile nicht mehr so ist. Aber trotzdem toll, dass so ein Bug es tatsächlich in eine Produktiv-Version schafft. Ist das kein Testcase? Naja, gcc halt…

2 thoughts on “Optimierung mal anders

  1. Wie schaut es aus, wenn Du die Schleifenvariablen als volatile kennzeichnest? schaut es aus, wenn du eine Schleife in eine Funktion auslagerst und als Parameter den anzuzeigenden String übergibst?

    Und welche Warnung brachte er in der delay.h? Aber gut: Irgendwo hab ich da noch ne eigene Implementierung für den AVR ATmega128 rumliegen, müsste man nur paar Konstanten anpassen für andere Controller.

    1. Die Warnung war, dass bei -O0 nicht für genaues funktionieren garantiert wird. Warum das so ist hab ich nicht weiter erforscht, macht aber doch irgendwo Sinn, wenn man bedenkt dass delay immer auf Instruktionen zählen hinausläuft.

      Weitere Experimente hatte ich dann auch nicht gemacht, weil der Code nur mal schnell ein LCD an den Draht bringen sollte, um selbiges zu testen. Hat mir gereicht, dass es irgendwie funktioniert.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.