Hi,
ich habe eine Applikation, deren Codegröße bisher knapp unter der
8K-Grenze war (8152 Bytes). Jetzt habe ich an 5 Stellen ein fehlendes
pgm_read_word() eingefügt - und bin plötzlich 510 Bytes über der
8K-Grenze!
Wenn ich mir ansehe, was hinter pgm_read_word() steckt, dann finde ich
allerdings nur ein paar lausige Assemblerbefehle - keinesfalls genug, um
für jeden Aufruf ca. 100 Bytes Code dazuzumogeln.
Hat jemand eine Idee, wo das herkommen könnte? Verursacht
pgm_read_word() auf irgend welchen verschlungenen Pfaden noch
zusätzlichen Code? Oder was sonst könnte das sein?
Danke!
Sortland schrieb:> Jetzt habe ich an 5 Stellen ein fehlendes pgm_read_word() eingefügt -> und bin plötzlich 510 Bytes über der 8K-Grenze!
Was passiert, wenn du das nur an 1 Stelle einfügst? Oder an 10?
Hast du schon die folgenden Optionen in Benutzung?
-mcall-prologues
Die optimiert die Push/Pop's falls du viele Funktionen hast, die nicht
ge-inlined werden
-flto
Auf Compiler und Linker anzuwenden, wobei der Linker dann auch die
-O<dein-opt-Level> bekommen sollte. Wirkt wenn man mehrere Compile-Units
sprich C-Files hat. Dann wird "über alles" optimiert.
Georg G. schrieb:> Auf Anhieb ist der Effekt nicht nachvollziehbar.
Ich würde das - ohne den Code angeschaut zu haben - so
interpretieren:
Der Compiler "sieht" die Anforderung, verursacht durch neue
Funtionsaufrufe, mehr Register zu sichern und wiederherzustellen
als vorher.
Bekanntlich geschieht das Sichern und Wiederherzustellen von
Registerinhalten (bei optimierendem Compiler) abhängig davon
welche Register nun wirklich verwendet werden.
@Sortland (Gast)
>8K-Grenze war (8152 Bytes). Jetzt habe ich an 5 Stellen ein fehlendes>pgm_read_word() eingefügt - und bin plötzlich 510 Bytes über der>8K-Grenze!
Klingt zuviel.
>Hat jemand eine Idee, wo das herkommen könnte? Verursacht>pgm_read_word() auf irgend welchen verschlungenen Pfaden noch>zusätzlichen Code?
Kann sein.
> Oder was sonst könnte das sein?
Möglicherweise wurden vorher ein paar Operationen wegoptimiert, weil sie
nie im Programmablauf genutzt wurden. Mit pgm_read_byte() geht das
anscheinend nicht mehr.
Poste deinen Quelltext als Anhang, dann kann man dir helfen.
Mitlesa schrieb:> Der Compiler "sieht" die Anforderung, verursacht durch neue> Funtionsaufrufe
Das sind gar keine Funktionsaufrufe, das sind ein paar einfache
Inline-Assembler-Anweisungen. Expandiert durch den Präprozessor
(hier für einen ATmega256RFR2) kommt da so ein Kauderwelsch raus
wie der hier:
Die 500 Bytes mehr müssen also woanders herkommen. Schließlich werden
die gelesenen 5 Werte ja wohl nicht nur einfach weggeworfen, sondern
irgendwas passiert mit denen. Vielleicht stand das, was da „passiert“,
ja vorher im Quellcode auch schon da, aber der Compiler konnte den
ganzen Firlefanz wegwerfen und gleich das Endergebnis hinschreiben,
da es dann konstant war? Jetzt sind die Eingabedaten plötzlich für
den Compiler nicht mehr vorhersagbar, und er muss stattdessen die
komplette Rechnung einfügen … nur so als Schuss ins Blaue, ohne den
Code gesehen zu haben.
Edit: Falk hat die gleiche Idee gehabt. ;)
Unverändert im Code: speedCtr wird anschließend in einer ISR
heruntergezählt. state.currIdx wird von außen (USB-Kommunikation)
gesetzt.
Die vorherige Verwendung des Arrays ohne den Zugriff über
pgm_read_word() war schlichtweg ein Bug, hier wurden auch immer
Müllwerte nach speedCtr geschrieben. Den kompletten Code kann ich leider
nicht posten, die Lizenz ist dafür zu restriktiv :-/
Sortland schrieb:> Den kompletten Code kann ich leider nicht posten
Dann musst du einfach mal das Assemblerlisting vorher-nachher
vergleichen. Da siehst du recht schnell, wo die fast 10% "Zugewinn"
herauskommen...
Sortland schrieb:> speedTable ist> PROGMEM const short speedTable[1900]={...
Das sind ja schonmal 3,7kB Flash-Verbrauch, also fast 50%.
Kannst Du dafür keine Formel einsetzen?
Probier doch einfach mal aus, was ich oben geschrieben hab. Das ist zwar
erstmal keine "Codebereinungung", die du ja auch aus Lizenzgründen
komplett selbst machen müsstest, hat aber im TransistorTester-Thread mal
von 110% auf unter 100% Flash gebracht. Die Randbedingungen sind oben
schon beschrieben, aber noch mal kurz:
Viele (nicht-ge-inline-de) Funktionen mit hoher Registerlast und damit
vielen Push's am Anfang profitieren von -mcall_prologues. Ganz einfach
anzuwenden und mit minimalen Overhead (wenige Takte pro Call).
LTO, bei vielen Compilation-Units (C-Files). Dazu müssen Compiler und
Linker-Aufruf mit -flto Versehen werden und beide die selbe
Optimierungsstufe bekommen, z.B. -Os.
Peter II schrieb:> Sortland schrieb:>> speedCtr=pgm_read_word(&speedTable[state.currIdx]);>> wenn das ganze in einer ISR steht, kommen die ganzen Pop und Pusch dazu.
Sollte eigentlich nicht sein.
pgm_read_word wird per inline aufgelöst.
Wenn das zum Function-call-Register-saving führt, muss Johann wieder
ran.
Karl H. schrieb:> Sollte eigentlich nicht sein.> pgm_read_word wird per inline aufgelöst.>> Wenn das zum Function-call-Register-saving führt, muss Johann wieder> ran.
ok, hatte nicht nachgeschaut. Dachte das ist eine normale Funktion aus
der lib.
> Wenn das zum Function-call-Register-saving führt, muss Johann wieder
ran.
Wenn die ISR die Register bisher nicht gebraucht hatte, die dieser
inline-Code nun nutzt (z.B. Z-Reg für LPM), dann braucht's die
zusätzlichen Push/Pop's. Nur 500 Byte werden das nicht so schnell
werden.
Carl D. schrieb:>> Wenn das zum Function-call-Register-saving führt, muss Johann wieder> ran.>> Wenn die ISR die Register bisher nicht gebraucht hatte, die dieser> inline-Code nun nutzt (z.B. Z-Reg für LPM), dann braucht's die> zusätzlichen Push/Pop's.
Klar brauchts es die. Aber nicht die Push/Pop Orgie aller Register,
die ein Funktionsaufruf in einer ISR nach siech zieht.
> Nur 500 Byte werden das nicht so schnell> werden.
Noch nicht mal, wenn alle Register gesichert werden.
Ich denke auch, dass der gcc da vorher Dinge wegoptimiert hat, was jetzt
nicht mehr geht.
Karl H. schrieb:> Ich denke auch, dass der gcc da vorher Dinge wegoptimiert hat, was jetzt> nicht mehr geht.
Genau drum mein Vorschlag, die *.lss Files zu vergleichen. Das dauert 5
Minuten und ist zielführender als viele Mutmassungen und Spekulationen.
Aber das muss der TO machen, der Code ist ja geheim.
>> geworden. speedTable ist
Probier mal, ob du stattdessen (wie im Parallelthread vorgeschlagen)
__flash nutzen kannst. U. U. kann der Compiler das dann besser
optimieren.
Jörg W. schrieb:> Probier mal, ob du stattdessen (wie im Parallelthread vorgeschlagen)> __flash nutzen kannst. U. U. kann der Compiler das dann besser> optimieren.
Interessant.
Frage an Johann: Gibt es da ein Potential?
Wenn ja, dann wäre das ein ziemlich gutes Argument (neben das
Typsicherheit, die __flash mit sich bringt)
Karl H. schrieb:> Frage an Johann: Gibt es da ein Potential?
Mit fällt da nur ein Unterschied ein:
Mit __flash ist es aus Sicht des Compilers ein normaler Array-Zugriff
auf ein konstantes Array. Ist dessen Inhalt bekannt könnte der Compiler
bei konstantem Index den Wert direkt verwenden.
Bei den pgmspace Makros funktioniert das nicht. Der Compiler könnte zwar
redundante Zugriffe bei gleichem Index rausoptimieren, aber der
Zusammenhang zwischen Index und Wert geht verloren.
So, mal Butter bei die Fische:
Wenn ich die Option -flto verwende, bekomme ich eine Fehlermeldung "cc1:
error: LTO support has not been enabled in this configuration"
Ein .lss/.lst-File, mit dem ich irgend welche Codeunterschiede erkennen
könnte, wird nicht erzeugt. Meine Buildumgebung ist ein avr-gcc mit
Makefile. Wie komme ich an diese Files?
pgm_read_word() wird insgesamt drei mal verwendet, zwei mal davon in
meiner ISR.
Sortland schrieb:> Wie komme ich an diese Files?
avr-objdump -dS yourfile.elf > yourfile.lss
U. u. ist es aber einfacher, die Dinger ohne den eingesprenkelten
(und oft durch Inlining reichlich deplatziert wirkenden) Quellcode
zu lesen:
avr-objdump -d yourfile.elf > yourfile.lss
Oft auch recht hilfreich:
avr-nm -S --size-sort yourfile.elf > yourfile.sym
Durch das --size-sort landen die größten Objekte ganz am Ende. Wenn
du die beiden vergleichst (vorher/nachher), solltest du also recht
schnell eine Idee bekommen, an welcher Stelle der Code explodiert ist.
Jörg W. schrieb:> avr-nm -S --size-sort yourfile.elf > yourfile.sym
OK, das ergebnis ist, dass eine Sektion __vector_10 (was auch immer das
ist - meine ISR?) und <main> schlichtweg mehr Code enthalten.
Vergleichen ist eher schwer, da verlassen mich meine Assemblerkenntnisse
und ein Compare-Tool markiert dann fast alles als "changed".
@ Sortland (Gast)
>OK, das ergebnis ist, dass eine Sektion __vector_10 (was auch immer das>ist - meine ISR?)
Ja.
> und <main> schlichtweg mehr Code enthalten.
Mach mal einen Test. Lass mal zum Vergleich das PROGMEM und die pgm_read
Funktionen weg, dann wird es ein normales Array im RAM. Der ist dann
zwar zu klein, aber egal. Damit kann man sehen, ob das Problem damit
zusammen hängt oder nicht. Wenn der Flashverbrauch nur geringfügig
sinkt, liegt das Problem darin, dass in deiner früheren Version
Programmteile wegoptimiert wurden.