Hallo alle zusammen,
ich habe mal eine Frage zum GCC Version 4.8.2.
Mein Projekt soll auf einem LPC1114 von NXP laufen. Dieser ist laut
Datenblatt etwas begrenzt, was die Flashgröße angeht.
Ziel ist es in dem Projekt, mathematische Berechnungsfunktionen,
gegenüber zu stellen und die Rechenleistung und Codegröße zu evaluieren.
1.) tabellarische Approximation
2.) Berechnung mittels Festkommaarithmetik
3.) float Berechnung
Der LPC1114 besitz keine Floating Point Unit. Deshalb wird hier auch der
halbe eabi Code eingebunden.
Nun zur Frage. Zu statischen Codeanalyse verwende ich Lint.
/* definition of coeffs */
float C0 = -100.530000;
float C1 = 1.134300;
float C2 = -1.235646;
float C3 = 12.023300E-3;
...
result = (C0 + ((val * (C1 + val * (C2 + val .... )) /
(1 + val * (C6 + val * (C7 + ....)
Compiliert wird das ganze mit der Option O2.
Da die Koeffizienten außerhalb des Moduls nicht verwendet werden und
sich nicht ändern fordert Lint hier z.B.
static const float C0 = -100.530000
Gebe ich Lint hier nach, wächst die Codegröße, da nun komischerweise die
__eabi_fsub noch zusätzlich verwendet wird. Ohne "static const" begnügt
er sich mit __eabi_fmul und __eabi_fadd.
Wie zwinge ich den GCC nun ausschließlich die __eabi_fadd + __eabi_fmul
zu verwenden. Und wie kann man gleichzeitig Lint ruhig stellen ohne die
Warnungen zu unterdrücken.
Hat jemand eine Idee.
Grüße René
René B. schrieb:> Gebe ich Lint hier nach, wächst die Codegröße, da nun komischerweise die> __eabi_fsub noch zusätzlich verwendet wird.
Ist nachvollziehbar.
Der Compiler kann mit static noch einen Tick aggresiver compilieren.
Da es allerdings in den meisten Fällen so sein wird, dass in einem
Programm eben nicht nur Additionen, sondern auch Subtraktionen vorkommen
werden, ist das für die Praxis nun wirklich kein Problem, ob der
Compiler
1
a + b
über eine Addition, oder aber weil er den Wert für b kennt und weiß dass
der negativ ist, dann eben
1
a - 5
mittels Subtraktion erledigt.
Nichts gegen Codeanalysen. Aber sie sollten schon der real vorkommenden
Situation entsprechen. Sonst kann man die erhaltenen Zahlen gleich in
die Tonne treten. Diese speziell konstruierten praxisfremden Tests
führen nur dazu, dass zb Grafikkartenhersteller die in bestimmten Tests
benutzten Funktionen extrem optimieren um bei den Tests gut
abzuschneiden.
Hi,
vielen Dank für die Antworten.
Mir ist schon klar, dass, mit aktiven Optimierungen, man es dem Compiler
überlässt, die "für ihn" günstigsten Funktionen zu wählen.
Weiterhin glaube ich, dass statische Codeanalysen schon recht sinnvoll
sind, jedoch auch mit Vorsicht zu genießen sind.
Ich habe allerdings lediglich nach einen Weg gesucht, um für diesen
speziellen Fall die Optimierungen auszuhebeln können. Da das eine kB,
welches die __eabi_fsub belegt, am Ende recht schmerzhaft sein kann.
Ich werde mal das GCC Manual studieren. Eventuell kann man ihn durch
geschickte Nutzung von Compiler-Schaltern definitiv dazu zwingen das
eine oder andere Verfahren anzuwenden. Am Ende sollte es der MCU, ja
egal sein, ob man wie oben beschrieben
(A - B) oder
(a + (-B)
rechnet.
So,
ich konnte das Problem lösen.
Die CompilerFlags -ffast-math oder -funsafe-math-optimizations führen
dazu das auch unter O1, O2, Os und O3 das Polynom ausschließlich mit
__eabi_fadd und __eabi_fmul berechnet wird.
Damit habe ich wieder das kB frei, welches durch die __eabi_fsub belegt
wurde.
Weiterhin meckert Lint nicht mehr, da die Koeffizienten nun als "static
const" angelegt werden können.
René B. schrieb:> Weiterhin meckert Lint nicht mehr, da die Koeffizienten nun als "static> const" angelegt werden können.
Da sollte eigentlich auch dein "Gefühl" meckern, denn "const int" kann
in C kaum/nicht optimiert werden, d.h. für die mathematische Operation
muss die Zahl immer explizit von einer bestimmten Speicherstelle geladen
werden; bei Integer-Literalen (d.h. auch bei "#define"), "static const",
und "const" in C++, kann der Compiler evtl. die Konstante in den
Maschinencode optimieren. Dass der Compiler "static const" optimiert ist
auch nur pure Freundlichkeit, "per Standard optimierbar" ist außer
Integer-Literalen nur "const" in C++.
Dr. Sommer schrieb:> Da sollte eigentlich auch dein "Gefühl" meckern, denn "const int" kann> in C kaum/nicht optimiert werden, d.h. für die mathematische Operation> muss die Zahl immer explizit von einer bestimmten Speicherstelle geladen> werden;
Aus welcher Stelle im C-Standard schliesst du das?
Edit:
Ganz im Gegenteil gibt es in der Draft-Version vom C99 Standard die
Fussnote 112
1
112) The implementation may place a const object that is not volatile
2
in a read-only region of storage. Moreover, the implementation need
3
not allocate storage for such an object if its address is never used.
aus der ich schliesse, dass es dem Compiler frei gestellt ist, ein const
int komplett aus dem Speicher zu verbannen, wenn das möglich ist.
Weiters sagt der Standard
"undefined behaviour" ist im Standard oft ein Hinweis darauf, dass man
es hier mit einer möglichen Optimierung zu tun hat. Im konkreten Fall
hat man es.
1
....
2
3
constinti=5;
4
5
*(int*)&i=7;
6
7
printf("%d",i);
es ist nicht garantiert, dass die Ausgabe auf 7 lautet.
Durch den "Attempt to modify" ist man im "undefined land" Es kann 7
rauskommen, muss aber nicht.
> 112) The implementation may place a const object that is not volatile> in a read-only region of storage.
Das bringt gar nichts...
Karl Heinz schrieb:> Moreover, the implementation need> not allocate storage for such an object if its address is never used.
Okay, das schon eher. Aber dazu müsste der Compiler eben das für den
gesamten Programmcode feststellen, und das kann zB der GCC erst seit
kurzem mit LTO (oder eben bei Verwendung von "static" und Analyse der
einen Translation Unit).
Und das geht immer noch nicht:
Dr. Sommer schrieb:> Okay, das schon eher. Aber dazu müsste der Compiler eben das für den> gesamten Programmcode feststellen
Nein, das muss er nicht.
Denn du darfst den Wert nicht verändern, sonst landest du bei
undefined behviour.
Selbst wenn du mittels
1
constinti=5;
2
3
printf("%p\n",&i);
dir die Adresse von i ausgeben lässt, und damit die Adresse des const
int benutzt, hast du immer noch die Restriktion, dass der Wert an dieser
Adresse konstant ist und für dich nicht änderbar ist. Selbst dann, kann
ein Compiler
1
constinti=5;
2
3
printf("%p\n",&i);
4
printf("%d\n",i);
zu
1
constinti=5;
2
3
printf("%p\n",&i);
4
printf("%d\n",5);
optimieren, ohne gegen den Standard zu verstossen.
das ist aber eine andere Sache, und hat nichts damit zu tun, ob ein
C-Compiler den Wert eines const int an Stellen einsetzen darf, an denen
es erlaubt ist.
Es gibt einen Unterschied zwischen C und C++ was const betrifft. Die
Nichtverwendbarkeit in Variablendeklarationen ist eine davon. Aber das
hat nichts mit der Verwendung und möglichen Optimierungen in Ausdrücken
zu tun.
Karl Heinz schrieb:> Nein, das muss er nicht.>> Denn du darfst den Wert nicht verändern, sonst landest du bei> undefined behviour.
Davon habe ich überhaupt gar nicht gesprochen. Ich meinte, damit der
Compiler keinen extra-Speicher an einer adressierbaren Stelle für die
"Konstante" (ob RO- oder RW- Speicher ist egal) anlegt muss er das
gesamte Programm analysieren:
test1.c:
1
constintX=5;
2
voidtest1(){
3
printf("%p\n",&X);
4
}
test2.c:
1
externconstintX;
2
voidtest2(){
3
printf("%d\n",X);
4
}
Wenn der Compiler die test2.c kompiliert weiß er nicht ob die test1.c
vielleicht die Adresse von X nimmt, und muss daher für X Speicher
anlegen (egal ob RO oder RW).
Karl Heinz schrieb:> Die> Nichtverwendbarkeit in Variablendeklarationen ist eine davon.
Einer der Gründe warum C schlecht ist...
Die Regelung mit der Adresse betrifft nur den Teilaspekt, ob der
Compiler für einen const int Speicher reservieren muss oder nicht. Es
schränkt ihn aber nicht darin ein, die Ersetzungsoptimierung zu machen,
wenn er den Wert des const int kennt. Ich hab die Fussnote deshalb
angeführt, weil aus ihr klar hervorgeht, dass sich der Standard bewusst
ist, dass ein const int auch komplett aus dem Speicher rausfliegen kann,
was im krassen Gegensatz zu deiner Aussage " muss die Zahl immer
explizit von einer bestimmten Speicherstelle geladen werden" steht. Wenn
es etwas im Speicher nicht gibt, kann es auch nicht von dort geladen
werden.
Bitte nicht vergessen, dass der LPC1114 ein Cortex M0 ist.
Dieser unterstützt in Assembler nicht die Verwendung von Literals
268: 4920 ldr r1, [pc, #128] ; (2ec <proc+0xa4>)
26a: 1c20 adds r0, r4, #0
26c: f000 fd30 bl cd0 <__aeabi_fmul>
270: 491f ldr r1, [pc, #124] ; (2f0 <proc+0xa8>)
272: f000 f965 bl 540 <__aeabi_fadd>
276: 1c21 adds r1, r4, #0
278: f000 fd2a bl cd0 <__aeabi_fmul>
2e0: 41942920 .word 0x41942920
2e4: 43c33d95 .word 0x43c33d95
2e8: 00000352 .word 0x00000352
Die Koeffizienten für die Berechnung werden immer aus dem Speicher
(Flash/RAM) geladen.
Als Nachtrag für mein Problem. Die Berechnung befindet sich in einer
Funktion während die Koeffizienten global für das C-File sind.
Dr. Sommer schrieb:> Davon habe ich überhaupt gar nicht gesprochen.
Dann muss ich dir eine Themenverfehlung attestieren.
Das Thema des Threads lautet: kann der Compiler den Zahlenwert eines
"const x" in mathematishcen Ausdrücken zur Compilerzeit ersetzen - ja
oder nein.
Ja. Das kann er, wenn er den Wert kennt
Und zwar auch dann, wenn irgendwo anders im Programm der Compiler
gezwungen wird, tatsächlich Speicher für den const int bereit zu halten.
> test1.c:>
1
constintX=5;
2
>voidtest1(){
3
>printf("%p\n",&X);
4
>}
> test2.c:>
1
externconstintX;
2
>voidtest2(){
3
>printf("%d\n",X);
4
>}
>> Wenn der Compiler die test2.c kompiliert weiß er nicht ob die test1.c> vielleicht die Adresse von X nimmt, und muss daher für X Speicher> anlegen (egal ob RO oder RW).
Du redest hier von etwas ganz anderem. Denn der Compiler kann in test2.c
den Wert gar nicht einsetzen, weil er ihn gar nicht kennt. Damit ist die
ganze Voraussetzung für eine mögliche Optimierung in test2.c schon mal
gar nicht gegeben.
René B. schrieb:> Wie zwinge ich den GCC nun ausschließlich die __eabi_fadd + __eabi_fmul> zu verwenden. Und wie kann man gleichzeitig Lint ruhig stellen ohne die> Warnungen zu unterdrücken.>> Hat jemand eine Idee.>> Grüße René
Da das Symbol __eabi_fadd, __eabi_fsub usw. aus der libgcc kommt gar
nicht.
Die einzige Möglichkeit besteht darin einen gcc zu nehmen wo diese
libgcc nicht vorkommt.
Siehe
http://www.linuxfromscratch.org/lfs/view/stable/chapter05/gcc-pass1.html
Hans Ulli Kroll schrieb:> Da das Symbol __eabi_fadd, __eabi_fsub usw. aus der libgcc kommt gar> nicht.>> Die einzige Möglichkeit besteht darin einen gcc zu nehmen wo diese> libgcc nicht vorkommt.
Also ist die Verwendung der Compiler Flags -ffast-math oder
-funsafe-math-optimizations keine Garantie, dass das gewünschte
Verhalten unter allen Bedingungen erfüllt wird?
Quasi ein Wechsel auf eine neuere Compiler Version könnte hier schon
wieder andere Ergebnisse bringen.
Leider sind diese beiden Flags, hinsichtlich ihrer Compilat-Ergebnisse,
nicht weiter dokumentiert.
Nicht schön aber selten: eigene Funktion fmad() oder so machen in der
nur die gewünschten Operationen vorkommen. Da der Compiler nicht weiß
welche Operanden er bekommt,darf er auch nicht 'optimieren'.
>> Es gibt einen Unterschied zwischen C und C++ was const betrifft. Die> Nichtverwendbarkeit in Variablendeklarationen ist eine davon.
Soweit ich weiß, funktioniert obiges Konstrukt weder in C noch in C++.
Dafür wurde mit C++11 der 'constexpr'-Qualifier eingeführt.
Johann L. schrieb:> Doch, natürlich sind die dokumentiert:>> http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-ffast_002dmath-919
-ffast-math
Sets -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only,
-fno-rounding-math, -fno-signaling-nans and -fcx-limited-range.
This option causes the preprocessor macro _FAST_MATH_ to be defined.
This option is not turned on by any -O option besides -Ofast since it
can result in incorrect output for programs that depend on an exact
implementation of IEEE or ISO rules/specifications for math functions.
It may, however, yield faster code for programs that do not require the
guarantees of these specifications.
Unter Dokumentation verstehe ich nicht zwangsweise den Hinweis das die
Code-Execution beschleunigt wird und man diese Options lieber
deaktiviert lässt. Aber um das zu verstehen muss man wohl in die
Internas des Compiler-Baus einsteigen.
Trotzdem Danke für die vielen Posts. Ich werde wohl, um sicher zu gehen,
eine eigene math lib erzeugen und nur die gewünschten Funktionen
einbetten. Dann gehe ich sicher das keine anderen benutzt werden.