Forum: Compiler & IDEs link time optimization


von Franz (Gast)


Lesenswert?

Hallo Leute,

ich bin in den letzten Tagen mehrmals über den Begriff "Link Time 
Optimization" hier im Forum gestolpert.
Ich habe mich eingelesen und damit herumexperimentiert.

Konkret bin ich ein Problem angegangen, dass mich schon länger 
beschäftigt, nämlich Pinzugriff über Funktionen. Außerdem störte mich, 
dass man immer die Kombination Port/Pin mit herumschleppt.
PeDas sbit.h ist zwar ein Schritt in die richtige Richtung, aber man 
handelt sich damit eben auch alle "Gefahren" von Makros ein, außerdem 
kann man den Pin damit nicht in Datenstrukturen speichern.

Aber Funktionen haben Overhead, und spätestens über Modulgrenzen hinweg 
kann der Compiler nicht mehr viel optimieren, und schon dauert das 
Setzen eines Bits 30 statt 2 Takte.

AUTOSAR und auch die Arduino-API machens vor: eine Funktion der Art
1
 write_pin(channel_t channel, boolean value);

Hier mal mein Experiment:
main.c:
1
#include "dio/dio.h"
2
3
int main(void)
4
{
5
  write_channel(channel_2, 1);
6
  write_channel(channel_0, 0);
7
8
  while(1);
9
10
return 0;
11
}

dio.h:
1
typedef enum
2
{
3
  channel_0 = 0u,
4
  channel_1,
5
  channel_2,
6
  channel_3,
7
  channel_4,
8
  channel_5
9
}
10
channel_t;
11
12
void write_channel(channel_t channel, boolean value);

dio.c:
1
#include <avr/io.h>
2
#include "dio.h"
3
4
volatile uint8_t *ports[] = { &PORTB, &PORTB, &PORTC, &PORTD};
5
6
void write_channel(channel_t channel, boolean value)
7
{
8
  uint8_t pin;
9
  uint8_t port;
10
11
  pin = channel & 0x07;
12
  port = channel >> 3;
13
14
  if (value)
15
    *ports[port] |= (1 << pin);
16
  else
17
    *ports[port] &= ~(1 << pin);
18
}

Kompiliert mit -Os -flto für den ATmega1284P ergibt sich:
1
000000a4 <main>:
2
  a4:  2a 9a         sbi  0x05, 2  ; 5
3
  a6:  28 98         cbi  0x05, 0  ; 5
4
  a8:  ff cf         rjmp  .-2        ; 0xa8 <main+0x4>

Genau das was ich solange probiert habe (über Makros, inline-Funktionen, 
etc.).

Das gcc Manual sagt, -flto und Debuggen vertragen sich nicht. Ok, damit 
kann man (grad auf nem Controller) leben.

Ansonsten werde ich das mal in meinem aktuellen Projekt zuhause 
ausprobieren.
Ich muss sagen, ich bin gerade bisl euphorisch. Hat es denn einen Grund 
wieso man von der LTO so selten hört?
Gibt es vielleicht irgendwelche Nebeneffekte?
Kann das Ganze irgendwie nach hinten los gehen?

Grüße,
Franz

von Dr. Sommer (Gast)


Lesenswert?

Franz schrieb:
> Genau das was ich solange probiert habe (über Makros, inline-Funktionen,
> etc.).
Glückwunsch ;-)
> Das gcc Manual sagt, -flto und Debuggen vertragen sich nicht. Ok, damit
> kann man (grad auf nem Controller) leben.
Das stimmt, den optimierten Code kann man quasi nur Instruktionenweise 
debuggen, da die Folge der Maschinenbefehle kaum noch mit der Folge der 
C-Statements zu tun hat...
> Ich muss sagen, ich bin gerade bisl euphorisch. Hat es denn einen Grund
> wieso man von der LTO so selten hört?
Ähnlicher Grund warum keiner C++ und alle noch AVR verwenden - "kenn ich 
nicht, mag ich nicht". LTO war in der Vergangenheit wohl auch noch etwas 
buggy.
> Gibt es vielleicht irgendwelche Nebeneffekte?
> Kann das Ganze irgendwie nach hinten los gehen?
Ja, es könnte dir versehentlich die ISR's und die Interrupt-Table 
wegoptimieren. Abhilfe schafft es __attribute__((used)) dranzuschreiben. 
Ansonsten. Ansonsten kostet es ja nichts das zu verwenden, und wenns 
obskure Fehler gibt halt mal testweise ohne -flto compilen.

von Andreas (Gast)


Lesenswert?

Franz schrieb:
> Kompiliert mit -Os -flto für den ATmega1284P ergibt sich:

Funktioniert bei mir nicht. Ich bekomme eine ellenlange Funktion 
write_channel und zwei Aufrufe zu dieser Funktion in main. Gibt es da 
noch einen besonderen Trick?
Mein Compileraufruf:
avr-gcc -o main.elf -flto -Os -mmcu=atmega1284p -DF_CPU=18432000UL dio.c 
main.c
Compilerversion ist 4.7.2

von Andreas (Gast)


Lesenswert?

Habs gefunden. Wenn man mit
avr-gcc -o main.elf -flto -Os -fwhole-program -mmcu=atmega1284p 
-DF_CPU=18432000UL dio.c main.c
compiliert, dann gehts. ;)

von Bastler (Gast)


Lesenswert?

LTO bedeutet der Compiler läuft nochmal zur Link-Zeit und hat damit alle 
Infos um zu erkennen, daß du nur  jeweils ein Portbit ändern wolltest. 
Wenn alles in einer Source (+event.Header) steht, macht er das ohne LTO.
BTW, wer lieber Zeilenweise Debuggern will, der kann mit -O0 übersetzen.
Achtung: letzter Absatz kann Spuren von Ironie enthalten.

von Franz (Gast)


Lesenswert?

Andreas schrieb:
> Habs gefunden. Wenn man mit
> avr-gcc -o main.elf -flto -Os -fwhole-program -mmcu=atmega1284p
> -DF_CPU=18432000UL dio.c main.c
> compiliert, dann gehts. ;)

Hm, warum es mit -fwhole-program bei dir funkt ist mir nicht ganz klar.
Das sollte eigentlich einen anderen Effekt haben.

Der Trick ist, beim Linkeraufruf ebenfalls -0s -flto anzuhängen. Daran 
ists bei mir auch erst gescheitert.

Beim Compileraufruf werden zunächst die Objekte erzeugt. Dabei wird der 
Code allerdings doppelt abgelegt. Einmal auf herkömmliche Art und Weise, 
nämlich als Maschinencode, der mit -Os optimiert wird.
Zusätzlich wird durch -flto nicht-optimierter Bytecode erzeugt.

Beim Linkeraufruf wird dem Linker mittels -flto mitgeteilt, dass er den 
Bytecode aus den Objekten "linken" soll. Das macht er auch, allerdings 
wurde dieser ja noch nicht optimiert! Deswegen muss dem Linker ebenfalls 
ein -Os mitgegeben werden.

Nachteil an dieser Methode hab ich noch keinen gefunden, ich spiel da 
noch bisl herum...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.