Hallo allerseits, ich möchte im Programm große Zahlen dividieren. 40 Bit / 40 Bit würde ausreichen. Das Programm ist in AVRGCC geschrieben. uint32_t Zaehlerwert; Zaehlerwert = 10000000 / Zaehlerwert; Program: 1390 bytes (67.9% Full) <<-- sehr schlank (.text + .data + .bootloader) Data: 23 bytes (18.0% Full) (.data + .bss + .noinit) funktioniert sehr gut und vergrößert das Programm nur geringfügig. __________________________________________________ Wenn ich aber umstelle auf 64 bit, bläht sich der Code extrem auf: uint64_t Zaehlerwert; Zaehlerwert = 10000000 / Zaehlerwert; Program: 5670 bytes (276.9% Full) <<<-- das geht gar nicht !!! (.text + .data + .bootloader) Data: 283 bytes (221.1% Full) (.data + .bss + .noinit) Kann mir bitte jemand nennen, ob man nur beim Conpilieren eine andere Bibliothek einbinden muß, wie es mal für Float der Fall war, um die Programmgröße in vernünftigem Maß zu halten? Oder muß beim Datentyp uint64_t das Programm so groß werden? siehe: Beitrag "LCD float ausgeben" Beitrag "uint64_t verwendet und gleich wird 5kb mehr Ram gebraucht" Ich habe früher mal eine 40-bit-Division in Assembler geschrieben für den kleinen Controller, die nicht sonderlich viel Platz brauchte. Es gibt auch hier noch eine Abhandlung, die das Thema streift. Es soll mit einem Tiny2313 ein reziproker Frequenzzähler realisiert werden. Beitrag "Reziproker Frequenzzähler+ Optimierte 64bit uint Routinen" Die Anwendung der mitgelieferten uint64_ops.h erschließt sich mir nicht ohne weiteres... mit freundlichem Gruß
Christian S. schrieb: > Wenn ich aber umstelle auf 64 bit, bläht sich der Code extrem auf: Dann benutzt Du vermutlich noch den alten WINAVR 2010. Ich hatte dafür mal eine optimierte Lib geschrieben: http://www.avrfreaks.net/forum/c-util-efficient-64-bit-support?name=PNphpBB2&file=viewtopic&t=113673 Bei neueren AVR-GCC wurde die dann übernommen.
Christian S. schrieb: > Es soll > mit einem Tiny2313 ein reziproker Frequenzzähler realisiert werden. Zum einen kannst Du problemlos auf einen ATtiny4313 umsteigen (4K Flash) und sofern Deine Referenzfrequenz nicht mehr als 6-7 gültige Stellen hergibt viel besser mit float rechnen. Andernfalls nimm eine IAR-Kiskstart Version (4K limit) und rechne double. Auch das geht sehr schnell und paßt in einen ATtiny4313. Als Anregung: http://www.mino-elektronik.de/fmeter/fm_software.htm#bsp2
1 | #include <stdint.h> |
2 | |
3 | uint64_t Zaehlerwert; |
4 | |
5 | void foo(void) |
6 | {
|
7 | Zaehlerwert = 10000000 / Zaehlerwert; |
8 | }
|
9 | |
10 | int main(void) |
11 | {
|
12 | return 0; |
13 | }
|
Compiliert mit -Os für den ATtiny2313: GCC 4.7.2:
1 | $ avr-size foo.elf |
2 | text data bss dec hex filename |
3 | 320 0 8 328 148 foo.elf |
GCC 4.9.3 bringt das gleiche Ergebnis. Davon abgesehen, für rechenintensivere Dinge würde ich eher einen ATmega nehmen, der in Hardware multiplizieren kann.
Jörg W. schrieb: > GCC 4.7.2: > $ avr-size foo.elf > text data bss dec hex filename > 320 0 8 328 148 foo.elf > > GCC 4.9.3 bringt das gleiche Ergebnis. Kann das sein dass der Compiler die foo() wegoptimiert hat? 320 Bytes ist für eine Softwaredivision einer 64Bit Zahl etwas zugut.
Christian S. schrieb: > Ich habe früher mal eine 40-bit-Division in Assembler geschrieben für > den kleinen Controller, die nicht sonderlich viel Platz brauchte. Dann nimm doch die her ... Du meintest ja schon, 40Bit wären genug
Matthias x. schrieb: > Kann das sein dass der Compiler die foo() wegoptimiert hat? 320 Bytes > ist für eine Softwaredivision einer 64Bit Zahl etwas zugut. Vermutlich ... Der Compiler weiß genau, dass mit dem Ergebnis nichts mehr passiert :-)
Matthias x. schrieb: > Kann das sein dass der Compiler die foo() wegoptimiert hat? 320 Bytes > ist für eine Softwaredivision einer 64Bit Zahl etwas zugut. Nö:
1 | … |
2 | 00000044 T foo |
3 | … |
4 | 000000c2 T __udivdi3 |
5 | 000000c4 T __udivdi3_umoddi3 |
6 | 000000d8 T __udivmod64 |
7 | 000000be T __umoddi3 |
8 | … |
Ohne LTO kann er das auch nicht wegoptimieren, da es ja eine globale Option ist, d. h. beim Compilieren von foo.c weiß er nicht, ob sie noch jemand anders rufen könnte.
Matthias x. schrieb: > 320 Bytes ist für eine Softwaredivision einer 64Bit Zahl etwas zugut. Einiges der 64-Bit Arithmetik wurde für GCC 4.7 überarbeitet bzw. komplett neu geschrieben, siehe https://gcc.gnu.org/PR49313 Mit Peters Implementierung hat das übrigens nichts zu tun. Die 64-Bit Division macht einen Deal zwischen Codegröße und Laufzeit: Bei "großen" Devices (solchen mit JMP, also >= 16KiB Flash) kann das dazu führen, dass eine 64-Bit Division merklich schneller arbeitet als eine 32-Bit Division mit den gleichen Werten. Wer sich für die konkrete Imlpementierung interessiert: http://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/avr/lib1funcs.S?revision=219188&view=markup#l1741
Johann L. schrieb: > Mit Peters Implementierung hat das übrigens nichts zu tun. Meine Version (9.11.2011) benennt die temporären Register t7 ... t0. Deine Version (21.11.2011) benennt die temporären Register c7 ... c0. Ansonsten ist der Code (1030 ;; The very Division + Remainder Routine) völlig identisch, sogar das T-Bit als Flag für die Restausgabe ist gleich. Ist aber bestimmt alles nur reiner Zufall, wie auch die zeitliche Nähe.
:
Bearbeitet durch User
Hallo, Danke erst mal für die Hinweise und Zuschriften. Ich werde das alles noch durchsehen. MfG
Peter D. schrieb: > Johann L. schrieb: >> Mit Peters Implementierung hat das übrigens nichts zu tun. > > Meine Version (9.11.2011) benennt die temporären Register t7 ... t0. > Deine Version (21.11.2011) benennt die temporären Register c7 ... c0. > Ansonsten ist der Code (1030 ;; The very Division + Remainder Routine) > völlig identisch, sogar das T-Bit als Flag für die Restausgabe ist > gleich. Ich wollte hier keinen Streit vom Zaun brechen. Generell kopiere ich keinen Code, den ich irgendwo im Netz finde, in GCC. Zum einen genügt der Code i.d.R. weder den Lizenzbedingungen (GPL), und andererseits entspricht er selten den Coding-Rules oder dem ABI (libgcc verwendet z.B. nie R2...R7). Ausnahme ist der Code von Sean D'Épagnier für Fixed-Point, der aber auch massiv angepasst wurde. Die Nähe des (Binär)Codes liegt darin begründet, dass es nicht wirklich viele unterschiedliche Implementierungen für eine 64-Bit Division gibt: Bitweise Schieben, Vergleichen und abhängig davon das Ergebnis bitweise aufbauen. Und die Übergabe- und Return-Register, call-saved und call-clobbered Register sind vom ABI vorgegeben. Zum Vergleich hab ich die beiden Versionen mal angehängt; viel Ähnlichkeit sehe ich da nicht. > Ist aber bestimmt alles nur reiner Zufall, wie auch die zeitliche Nähe. Die zeitliche Nähe ergab sich auch daraus, dass damals auf AVRfreaks ziemlich viel über die ineffiziente 64-Bit Arithmetik genölt wurde. PR49131 ist übrigens von Juni 2011 und nennt bereits explizit die libgcc-Funktionen in der GCC-Nomenklatur: __muldi3, __[u]divdi3, __[u]moddi3. Zumindest kam das Thema in 11-2011 auch hier auf, und ich hab da unterschiedliche Versionen implementiert und auch gebenchmarkt: Beitrag "Re: Stack Überschreiber beim Rechnen mit uint64_t" Meine Arbeitsversion dort anbei sieht auch schon fast so aus wie das, was jetzt in der libgcc ist: Beitrag "Re: Stack Überschreiber beim Rechnen mit uint64_t" Und nein, ich arbeitete an avr-gcc weder haupberuflich noch nebenberuflich oder freiberuflich. Von daher war das alles recht zäh und langwierig und im Ausmaß sehr beschränk. Gelciher gilt auch für meine Verwendung von avr-gcc. Und ich fänd's toll, wenn mehr Leute am GCC mitarbeiten würden. Erfahrene Entwickler gibt's ja viele, und eine Toolchain ist zentraler Teil der Softwareentwicklung -- zumindest wenn man in C/C++ schreibt.
...hier noch die 64-Bit Multiplikation. Ohne MUL bleibt da nicht viel Spielraum für die Kür, aber mit MUL ist die Implementierung schon was aufwändiger.
Hallo, nochmals Danke für die Ausführungen. Meine Lösung gestaltet sich im Upgrade des AVR-GCC. Einen Assembler-Code-Teil in den C-Code einzufügen, gelingt mir bislang noch nicht. Bestimmt ist das ganz einfach, wenn man weiß, wie es geht :-) Diese Lernschwelle mußte ich also nicht überwinden, obwohl das interessant und fortschritlich wäre. Bei Atmel kann man die neue Toolchain "avr8-gnu-toolchain-installer-3.5.0.85-win32.any.x86.exe" erhalten, die den AVR-GCC enthält. Diesen entpackt man in ein beliebiges Verzeichnis. Damit AVR-Studio diesen auch benutzt, muß man im AVR-Studio in Project - Project options - custom options - external tools vom Pfad, der bisher zu winavr20100110 zeigte, zum Pfad des neuen Verzeichnisses umstellen. Die Zeile bei Make kann bleiben, da in der neuen Toolchain kein Make enthalten ist. Hier beispielsweise hat schon mal jemand auf andere Weise das neue GCC eingebunden: Beitrag "Re: WinAVR 2010 / AVR-GCC und Atmega48PA" Hier mal ein paar Größenangaben. Realisiert wurde ein Programm, das einen normalen Frequenzzähler und einen Fequenzzähler mittels Periodendauermessung umschaltbar auf einfache Weise mit Ausgabe aufs LCD realisiert: (mal wieder ein Fall für die gut abgelagerten Exemplare, die wie am ersten Tag funktionieren) AVR Memory Usage ---------------- Device: at90s2313 mit neuem GCC compiler (avr8-gnu-toolchain) Program: 1442 bytes (70.4% Full) <<-- ist größer, nicht kleiner!!! (.text + .data + .bootloader) Zaehlerwert = 10000000 / Zaehlerwert; Data: 23 bytes (18.0% Full) nochmals mit der alten Version (Winavr mit AVR GNU Compiler Collection (GCC) 4.3.3 ) und 32-Bit-Division zum Vergleich getestet: Program: 1390 bytes (67.9% Full) (.text + .data + .bootloader) Data: 23 bytes (18.0% Full) (.data + .bss + .noinit) ___________________________________________ mit neuem GCC compiler (avr8-gnu-toolchain 3.5.0.85) und 64-Bit-Division: AVR Memory Usage ---------------- Device: at90s2313 Program: 1630 bytes (79.6% Full) <<--- sehr brauchbare Größe (.text + .data + .bootloader) Data: 27 bytes (21.1% Full) (.data + .bss + .noinit) mit neuem GCC compiler (avr8-gnu-toolchain), aber ohne die Zeile mit der Division: Program: 1442 bytes (71.4% Full) <<-- 188 bytes benötigt die Zeile mit der Divison (.text + .data + .bootloader) Data: 27 bytes (21.1% Full) (.data + .bss + .noinit) _________________________________________ mit "zaehlerwert = 10000000000ULL / Zaehlerwert;" Program: 1584 bytes (77.3% Full) (.text + .data + .bootloader) Data: 23 bytes (18.0% Full) (.data + .bss + .noinit) Es bleibt also noch Platz für eine Mittelwertbildung und sonstige Späße wie UART-Ausgabe... mit freundlichem Gruß
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.