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?
Warum vergleichst du nicht einfach die *.lss Files? Dann siehst du sofort, wo was dazu gekommen ist. Auf Anhieb ist der Effekt nicht nachvollziehbar.
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 schrieb: > bin plötzlich 510 Bytes über der 8K-Grenze! Lies da die ersten 5 Stunden: Beitrag "C versus Assembler->Performance" Da sind einige Tipps vergraben...
@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:
1 | (__extension__({ __asm__ __volatile__ ( "movw r0, %4\n\t" "movw r30, %A3\n\t" "sts %1, %C3\n\t" "sts %0, %2\n\t" "spm\n\t" "clr r1\n\t" : : "i" (((uint16_t) &((*(volatile uint8_t *)((0x37) + 0x20))))), "i" (((uint16_t) &((*(volatile uint8_t *)((0x3B) + 0x20))))), "r" ((uint8_t)((1 << (0)))), "r" ((uint32_t)(address)), "r" ((uint16_t)(data)) : "r0", "r30", "r31" ); })); |
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. ;)
:
Bearbeitet durch Moderator
Die Änderung ist relativ simpel. Früher stand da:
1 | speedCtr=speedTable[state.currIdx]; |
Jetzt ist da draus ein
1 | speedCtr=pgm_read_word(&speedTable[state.currIdx]); |
geworden. speedTable ist
1 | PROGMEM const short speedTable[1900]={... |
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: > speedCtr=pgm_read_word(&speedTable[state.currIdx]); wenn das ganze in einer ISR steht, kommen die ganzen Pop und Pusch dazu.
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.
Vermutlich war davor die nicht benutzte Tabelle auch nicht im Flash. Der GCC ist da sehr gut drin, unbenutztes einfach wegzulassen.
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.
Sortland schrieb: > Die Änderung ist relativ simpel. Früher stand da: > >
1 | speedCtr=speedTable[state.currIdx]; |
> > Jetzt ist da draus ein > >
1 | speedCtr=pgm_read_word(&speedTable[state.currIdx]); |
> > 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.
:
Bearbeitet durch User
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.
:
Bearbeitet durch Moderator
Sortland schrieb: > Wie komme ich an diese Files? Hast du Dr. Gurgel mal kontaktiert? Es gibt knapp 6000 Einträge zu "gcc .lss". Die Files stehen üblicherweise im Verzeichnis "default". Beispiel: ## Build all: $(TARGET) uart861.hex uart861.eep uart861.lss size ## Compile uart861.o: ../uart861.c $(CC) $(INCLUDES) $(CFLAGS) -c $< ##Link $(TARGET): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) $(LINKONLYOBJECTS) $(LIBDIRS) $(LIBS) -o $(TARGET) %.hex: $(TARGET) avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@ %.eep: $(TARGET) -avr-objcopy $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0 %.lss: $(TARGET) avr-objdump -h -S $< > $@ size: ${TARGET} @echo @avr-size -C --mcu=${MCU} ${TARGET}
:
Bearbeitet durch User
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.
1 | const short speedTable[1900]={... |
2 | speedCtr=speedTable[state.currIdx]; |
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.