Forum: Compiler & IDEs make - Build mit unterschiedlichen Compiler-Flags


von Harald T. (marsbotschafter)


Lesenswert?

Ich habe folgende Aufgabe, bei der Ihr mir vielleicht helfen könnt, sie 
zu lösen:
Ich habe ein umfangreiches embedded-C++ Projekt, welches mit dem GCC für 
einen ATmega128 gebaut wird. Nahezu alle CPP-Files sollen mit der 
Optimierungsstufe "s" gebaut werden, um das Paket in die 128kByte zu 
bekommen.
Nun gibt es aber einige Klassen (also einige CPP-Files) die in 
Optimierungsstufe "2" oder "3" gebaut werden müssen, weil sie sehr 
performant sein müssen. Nun ist es kein Problem diese Files im makefile 
in eine eigene Liste zu bringen. Allerdings ist mir noch nicht klar, wie 
ich make beibringen kann, beim Build (also beim compilieren von .cpp in 
.o) diese Liste mit anderen Compiler-Flags zu bauen.
Kann mir hier jemand weiter helfen?
Danke!

von Oliver (Gast)


Lesenswert?

gcc ab Version 4.4 kennt pragmas, mit denen sich der Optimierungslevel 
im Source code definieren lässt.

http://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-Pragmas.html#Function-Specific-Option-Pragmas

Allerdings würde mich mal interessieren, wie groß nachweislich der 
Geschwindigkeitsunterschied zwischen -Os und -O2 bzw. -O3 wirklich ist. 
Denn einer der Eigenschaften der Riskprozessoren ist, daß kürzer in den 
allermeisten Fällen auch schneller ist. Das weiß der gcc aber nicht 
immer.

Oliver

von holger (Gast)


Lesenswert?

>Denn einer der Eigenschaften der Riskprozessoren ist, daß kürzer in den
>allermeisten Fällen auch schneller ist.

Nö. Weniger Programmcode heisst noch lange nicht
das das Programm schneller ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wie gesagt, erst ein Profiling kann zeigen, ob O2 oder O3 sinnvoll sind. 
Neben den Futschle-an-100-Schalter-gleichzeitig-rum-Optionen wie Os, O2, 
O3 gibt's ja noch zig Schräubchen zum Feinjustieren.

Wie auch immer; im Makefile geht auch
1
foo.o bar.o: CFLAGS += -O2
Das -O bzw -f[no-]eine-option, die am weitesten rechts steht, 
überschreibt die weiter links — zumindest für die meisten Optionen.

Es stört also nicht, wenn CFLAGS im Ende aussieht wie
1
... -mcall-prologues -Os -mno-call-prologues -O2 ...

Den Code mit optimize pragmas/attribute verunstalten ist 
Geschmackssache.

von Konrad S. (maybee)


Lesenswert?

Wenn das Object-File x.o mit zusätzlichen Flags für C++ kompiliert 
werden soll, dann schreibst du in dein Makefile sowas rein:

x.o : CXXFLAGS += -O3

CXXFLAGS wirkt nur auf C++-Kompilierung.

von holger (Gast)


Lesenswert?

>Nun gibt es aber einige Klassen (also einige CPP-Files) die in
>Optimierungsstufe "2" oder "3" gebaut werden müssen, weil sie sehr
>performant sein müssen.

Wenn man an den Optimierungsstufen rumfummeln
muss um sie performant zu machen, dann ist da schon der
Hund begraben. 99% eines Codes muss gar nicht performant
sein. Such dein 1% wo der meiste Umsatz gemacht wird und
optimiere da. Zur Not auch mit Assembler. Wenn es um
Hardwaremodule geht kann man sich oft deren Eigenschaften
zu Nutze machen.

Ohne Quellcode Beispiele für die besonders performanten
Stellen kann man da aber auch nicht helfen.

von (prx) A. K. (prx)


Lesenswert?

Oliver schrieb:

> Denn einer der Eigenschaften der Riskprozessoren ist, daß kürzer in den
> allermeisten Fällen auch schneller ist.

Auf Highend-Prozessoren lässt sich diese Regel nicht anwenden. Im 
Gegenteil, etliche lohnende Laufzeitoptimierungen sind recht 
platzraubend.

Bei Lowend-RISCs wie AVRs ist aggressiv laufzeitoptimierter Code zwar 
oft nicht viel schneller, aber wenn beispielsweise Laufzeitfunktionen 
für Mul/Div ins Spiel kommen, dann kann der Compiler sie für kürzer 
halten als simple Shifts. Für die Laufzeit ist das indes eine 
Katastrophe.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. schrieb:

> ...wenn beispielsweise Laufzeitfunktionen für Mul/Div ins Spiel kommen,
> dann kann der Compiler sie für kürzer halten als simple Shifts.
> Für die Laufzeit ist das indes eine Katastrophe.

So wir's zB in avr-gcc gemacht: Wenn auf Größe optimiert wird, werden zB 
mitunter Shift-Loops genommen wo für Speed der Shift offen codiert wird; 
ähnlich für Multiplikation vs. Libcall oder Division durch Konstante vs. 
Multiplikation.

An einigen Stellen hab ich die Kosten billiger gemacht als sie wirklich 
sind, um nicht einen ewig-langsamen Libcall zu erzeugen wo eine Version, 
die eine Instruktion länger ist, mit einem 
Arithmetik-Taschenspielertrick aufwarten kann und wesentlich schneller 
ist.

Problem dabei ist, daß Os/O2 eigentlich kompromisslos sind und daß es 
keine Möglichkeit gibt, zwischen diesen Polen abzuwägen — weder für den 
Anwender noch innerhalb von GCC.

Weiteres Problem bei AVR ist, daß es extrem kleine Hardware gibt mit nur 
ein paar k Programmspeicher wo jedes Byte zählt.

Bei der 64-Bit Arithmetik hab ich versuch abzuwägen, d.h. je nachdem für 
welches Derivat man übersetzt, bekommt man unterschiedliche 
schnelle/große Implemenmtierung der Division. Dies ist jedoch unabhängig 
vom Optimierungsgrad.

von Oliver (Gast)


Lesenswert?

Johann L. schrieb:
> Weiteres Problem bei AVR ist, daß es extrem kleine Hardware gibt mit nur
> ein paar k Programmspeicher wo jedes Byte zählt.

Und gerade bei den Mini-Progrämmchen ist die Wahrscheinlichkeit gegeben, 
daß ein libcall anstelle von ausformuliertem Code zwar beim Aufruf 
kürzer ist, dafür aber die lib-Funktionen reinzieht, die sonst nicht 
erforderlich gewesen wäre.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Oliver schrieb:
> Johann L. schrieb:
>> Weiteres Problem bei AVR ist, daß es extrem kleine Hardware gibt mit nur
>> ein paar k Programmspeicher wo jedes Byte zählt.
>
> Und gerade bei den Mini-Progrämmchen ist die Wahrscheinlichkeit gegeben,
> daß ein libcall anstelle von ausformuliertem Code zwar beim Aufruf
> kürzer ist, dafür aber die lib-Funktionen reinzieht, die sonst nicht
> erforderlich gewesen wäre.

Ja, das ist ein Problem im GCC und vielleicht auch in anderen Compilern, 
wenn globales Wissen nicht vorhanden ist oder nicht verwendet wird. 
C-Paradigma war und ist ja modulweise zu übersetzen und alles erst zur 
Linkzeit zusammenzuführen.

Momentan werden die Kosten auf Instruktionsebene bestimmt unter der 
Annahme, daß sich die Vorteile aufsummieren nach dem Motto "alles wird 
gut".

Eine andere, globale Kostenberechnung setzt zum einen voraus, daß die 
Kosten überhaupt erst bestimmbar sind (was im Rahmen von LTO prinzipiell 
der Fall ist) und daß diese Kosten im Rahmen eines exakteren 
Kostenmodells Anwendung finden. In GCC ist letzteres ist auf 
Instruktionsebene nicht der Fall, d.h. Grundvoraussetzung wäre ein neues 
Kostenmodell.

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.