Hi, in meine C-Programm müssen ein paar Gleichungen berechnet werden. Siehe Anhang. Wie mache ich das am effektivsten? Mein 8-bit-µC Tiny26 hat nur 2k Programmspeicher welcher fast voll ist. Die oben gezeigten Rechnungen funktionieren einwandfrei. Gibt es eine Möglichkeit auf die Cast-Konvertierung auf unsigned long int (u32) zu verzichten? Grüße, Michael
"Gibt es eine Möglichkeit auf die Cast-Konvertierung auf unsigned long int (u32) zu verzichten?" Das weißt nur Du, da nur Du den Maximalwert für speed kennst. Ansonsten könnte es sich lohnen, die Divisonen auf 2-er Potenzen umzustellen (128, 1024). Peter
Die Variable speed ist 8-Bit groß und kann Werte zwischen 0 und 255 annehmen. "Ansonsten könnte es sich lohnen, die Divisonen auf 2-er Potenzen umzustellen (128, 1024)." Meinst du damit z.B. nicht durch 1000 zu teilen, sondern durch 1024 per 9mal nach rechts schieben? Zähler muss natürlich angepasst werden. Michael
>Meinst du damit z.B. nicht durch 1000 zu teilen, sondern durch 1024 per >9mal nach rechts schieben? Wird er meinen. Falls die Rechnengenauigkeit reicht, könntest du eventuell in Get_Param() *48/1000 durch /21 ersetzt werden, wäre dann also ~47,6/1000. In diesem Fall könnte die komplette Berechnung mit 16 Bit auskommen. Ansonsten müsste generell bei Get_Param() der hintere und bei Get_No_Load_Current() der vordere Teil mit (u16) auskommen. Ob dies aber was bringt weiß ich nicht, eventuell kommt der Compiler selbst schon drauf. Als letztes könnte ein Blick auf den erzeugten Assemblercode noch hinweise auf mögliche Optimierungen bringen. PS: Irgendwie sind 2KB Flash fast immer zu wenig :-D
"PS: Irgendwie sind 2KB Flash fast immer zu wenig :-D" Volle Zustimmung, zumindest beim AVR-GCC. Deshalb nehme ich eigentlich immer den ATMega8, der kann sogar schnell multiplizieren. Ich weiß jetzt nicht, wie das bei den kommerziellen Compilern ist. Durch den 16Bit-Befehlssatz und RISC ist die Kodeeffizienz ja von vornherein nicht so gut, wie auf CICS mit 8Bit-Befehlen, z.B. 8051. Da habe ich in 2kB (AT89C2051) viel mehr machen können und oft auch floating point gemacht. Hab dann aber auch einen kommerziellen Compiler genommen (Keil). Peter
bei 8bit Eingangsgrösse und 8bit Ausgangsgrösse könnte auch eine Tabelle eine sinnvolle Lösung sein, von Excel berechnen lassen. Macht je 256 Byte Tabellengrösse pro Funktion. Schneller als die Berechnung ist es auf jeden Fall und würde deutlich weniger Speicher brauchen.
"Falls die Rechnengenauigkeit reicht, könntest du eventuell in Get_Param() *48/1000 durch /21 ersetzt werden, wäre dann also ~47,6/1000. In diesem Fall könnte die komplette Berechnung mit 16 Bit auskommen." Respekt! Sehr guter Vorschlag! Stolze 68Byte hat das gebracht! :-) "PS: Irgendwie sind 2KB Flash fast immer zu wenig :-D" Ich hatte das gleiche Programm als Prototyp in Assembler in einem Tiny15 mit 1KB Speicher. Der war auch Rand voll... @crazy horse: So hab ichs mit m Tiny15 gemacht. Da hab ich die Parameter (sind Regelparameter) im E² abgelegt. Eine Tabelle im Flash wäre sicher besser. Ich dachte eigentlich eine Gleichung braucht weniger Programmspeicher als eine Tabelle? Wenn ich die Gleichung mal auskommentier, dann sind 50Byte weniger belegt. Eine Tabelle müsste mit 256 Werten auch 256 Byte belegen, oder? So, jetzt hab ich noch 500Byte. Das sind 200 mehr als vor euren Tipps. Vielen Dank! Hoffentlich reicht das aus... Michael
@Michael Anstatt wahllos rumzuoptimieren, geht doch rein pragmatisch vor. Du wirst wohl nicht umhin kommen, Dir erstmal das Assemblerlisting anzusehen und aufzuschreiben, wieviel Code die einzelnen Funktionen Deines Programms belegen. Und dann suchst Du Dir die Teile raus, die auch wirklich viel Code erzeugen. Es macht nämlich überhaupt keinen Sinn, an irgendwas rumzuoptimieren, was nur 1% Einfluß hat. Peter
Das hört sich nach viel Arbeit an g. Gibts eigentlich ein kleines Tool, welches mir die Größe eines Programmteils anzeigt? Für Windows gibts z.B. Treesize welches die Größe von Ordner ausgibt.
[avr-]size müsste das können. Ich habe nur leider keine Ahnung mit welchen Parametern...
Kommando zurück: $ avr-nm -t d -S <xyz.[a|elf|o]> Zeigt alle Symbole und die Zugehörige größe Dezimal an.
>Gibts eigentlich ein kleines Tool, welches mir die Größe eines >Programmteils anzeigt? Erzeuge das Assemblerlisting mit make dateiname.s Dann kannst du dir ansehen wie groß jede einzelne Funktion wird. Aber Achtung: 1. ist die Größenangabe in Word und nicht Byte (also mal zwei nehmen) 2. Unterprogramme z.B. von der avr-libc werden nicht berücksichtigt.
ich schätze, Du kommst nicht drum'rum Dir anzusehen, welchen Asm Code Dir Dein Compiler erzeugt, wenn Du richtig optimieren möchtes. Wie Peter aber schon andeutete: kennst Du die 10-90 Regel? 10% Code erzeugen 90% Ausführungszeit (Lokalität). Du solltest also wissen, wo es sich lohnt. Ich kann allerdings nicht sagen, ob der avr-gcc profiling unterstützt, was eine konkrete Möglichkeit mit gprof wäre. Du kannst ihm aber etwas auf die Sprünge helfen, indem Du es dem Compiler etwas einfacher machst:
1 | u8 Get_No_Load_Current(void) |
2 | {
|
3 | u32 inl = 237; // kein copy constructor |
4 | inl *= speed; |
5 | inl /= 100; |
6 | ....
|
7 | }
|
Je nach Wertebereich Deiner Vars kommst Du so auch ohne unit32_t aus, was alles etwas weniger bytes/register benötigt (also worst-case Rechnung machen). Wie ich sehe, kannst Du auch speed ausklammern, es wird hier 3x multipliziert, d.h. klever per Hand umformen, Konstanten zusammenfassen. Da Du C machst, kannst Du auch vieles dem Präprozessor machen lassen - gerade die Konstanten Mult.ausdrücke: #define C1 (4711 * 2) wird von ihm ausgerechnet - brutal geht auch: const uint8_t c1 = 4711 * 2; // ggf. static machen benötigt aber wohl 2 Byte mehr Speicher. Viele Grüße Olaf
Oha, jetzt wirds interessant... Zuerst einmal, ich verwende den IAR Kick-Start-Compiler welcher beim STK500 mitgeliefert wird. Das Tool [avr-]size kommt mit gcc mit oder lässt sich als "Plugin" nachinstallieren? ODer ist es ein eigenständiges Programm? Der IAR sagt mir den Speicherbedarf für jede *.c Datei, welche er kompiliert. Das ist schonmal hilfreich. Denn die Datei, in welcher die Gleichungen sind ist nach main() am größten. Ich denk, dass ich trotzdem nicht um den Assemblercode herum komme. Sicher auch ganz lehrreich. Die 10-90 Regel kenn ich als 20-80 Regel. Vielen Dank für eure tolle Tipps!!! Grüße, Michael
Naja, 20/80 oder 10/90, ist ja fast das gleiche... Der entscheidende Punkt ist: In einem kleinem Teil des Systems werden die meisten Resourcen benutzt. Ob das jetzt bei einem Programm Speicher oder Zeit ist, bei einem elektrischen Gerät der Energieverbrauch oder bei einem mechanischem Gerät die Fehlerquelle, oder bei einem anderem technischen Gerät die Kosten, spielt keine Rolle. Fast immer gibt es in einem System irgendwelche signifikaten Konzentrationen von irgendetwas.
>Ich denk, dass ich trotzdem nicht um den Assemblercode herum komme. >Sicher auch ganz lehrreich. Die 10-90 Regel kenn ich als 20-80 Regel. Es lohnt sich wirklich, nicht unbedingt das konkrete verstehen, aber man kann ungefähr erkennen, was der Compiler macht (besonders interessant bei C++ expression und meta templates lib zB. tvmet, blitz++). Die 10-90 oder 20-80 oder 80-20 bzw. 90-10 Regel - da ist sich die Literatur nicht so ganz einig (Ich behaupte mal ganz kess, es sind 8,96% zu 91,04% ;-) aber die Kernaussage bleibt ja erhalten wie bereits hier gesagt. Was auch helfen kann, u.U. besser als den Compiler auf min. Size achten zu lassen (switch -s): inlining; d.h. Du musst wissen, wo es sich lohnt (hier hilft auch der asm code). Persöhnlich (ich kann nur vom gcc sprechen), versaut die -s Option mehr als sie nutzt. Oft schreibt man functions nur wegen der Lesbarkeit des sources und der Compiler nimmt das etwas zu ernst. Der C99 Standard gibt noch das keyword inline her, es ist allerdings ein Hinweis an den CC es doch bitte zu inlinen, ob er es macht, ist 'ne andere Sache. Die neueren (>3.1) gcc kann man dagegen mit __attribute ((always_inline)) dahin treten. Aber nicht übertreiben, sonst wird der code zu gross wiederum ;-) Auch generell mit -O2 zu kompilieren, was ja auf 32bit OS Standard ist, hilft bei komplexen Sourcen für den uC nichts, da man dann ständig an den Hardwaregrenzen hängen bleibt. Wie gesagt, wissen wo es sich lohnt. Also, einfach mal winavr installieren. Wie ich gerade sehe, ist gprof anscheinend nicht für den AVR portiert, aber gcov - der kann auch beim optimieren bzgl. der 90-10/80-20 Regel helfen (mal googeln). Viele Grüße Olaf
Du kannst für jede rechenoperation einen anderen Scalierungsfaktor verwenden. Dadurch kannst du auch mit 16bit-Operationen eine hohe Genauigkeit erzielen. inl= 237/100 * s - 14/1000 *s^2 - 36 erstmal umformen damit das s^2 verschwindet: inl= (237/100 - 14/1000*s)*s - 36 Die Berechnung enthält drei Faktoren, wird mit 16 bit gerechnet, so darf jedes einzelergebnis die 32000 Marke nicht überschreiten: fakt1=(1<<13)*237/100; /* Berechnung durch den User */ fakt1=19415; fakt2=(1<<13)*14/1000; /* Berechnung durch den User */ fakt2=114; die Berechnung sieht also so aus: inl=((((fakt1-fakt2*s)>>8)*s)>>5)-36 Ich hoffe ich hab mich nicht verrechnet. Selber hab ich diese Methode bei für die skalierung eines SHT11 verwendet. Der Fehler lag bei ca. 0,6 LSB _____________________ uint16_t sht11_humidity_scale(uint16_t value) { /* scaling factors as defined in the datasheet with factor 100 */ /*-4.0 * 100*/ #define SHT11_C1 -400 /*0.0405 100 (1<<28)*/ #define SHT11_C2 1087163597 /*-0.0000028 100 (1<<30)*/ #define SHT11_C3 -300648 /* humidity = SHT11_C1 + value * ( SHT11_C2 + SHT11_C3 * value); */ /* I will calc (humidity * 100) here, the same scale that temperature has. */ /* every step is calculated in the best possible scale */ /* we calculate in long -> we have 31 bits + sign */ /* value is 12 bit long */ return (int32_t) (((((SHT11_C3 * value >>2) /* rescale 30 -> 28 */ + SHT11_C2) >> 11) /* rescale 28 -> 17 */ * value + ((int32_t)1<<16)) /* round: add (1<<16) */ >> 17) /* rescale 17 -> 0 */ + SHT11_C1; } __________________
Sehr gute Idee mit dem Ausklammern! Simpel aber wirkungsvoll. Nochmals vielen Dank an alle! Ihr habt mir sehr geholfen und mir ein bisschen von der Platzangst genommen:-) Michael
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.