Hi,
ich versuche gerade ein Programm so weit zu optimieren, dass es
zumindest auch mit 6.4 MHz statt 8MHz läuft, idealerweise auch mit 1
MHz.
Zur Zeit läuft das Programm mit 6.4 MHz, wenn ich als Optimierung -O2
benutze, allerdings gehen dann ein paar andere Sachen kaputt,
insbesondere geht der Controller nicht mehr in den power down mode (oder
wacht zumindest sofort wieder auf).
Mit -O0, -O1 und -Os läuft das Programm nur bei 8 MHz.
Ich vermute, dass das Problem darin liegt, dass ich per Software SPI ein
RFM12 Funkmodul anspreche, und dabei nicht schnell genug die Daten
rausschicke für 49 kbps.
Jetzt gibt es für mich 2 Lösungsansätze:
- Das Programm so fixen, dass es auch mit -O2 richtig funktioniert
- Das Programm so optimieren, dass es auch mit -O1 schnell genug ist
Allerdings komm ich bei beiden Problemen nicht weiter. Ich hab schonmal
probehalber die Funktionen "rfm12Cmd" und "rfm12_sendByte" als inline
deklariert, aber das hat nicht ausgereicht.
Falls ihr vorschläge zu einem der Probleme habt würde ich mich über
Antworten sehr freuen ;)
Ciao,
VA
Ich meine, so geht das nicht, das Du lediglich sagst, das Programm soll
schneller sein. Das ist zu allgemein, um Dir helfen zu können.
Du musst erstmal feststellen welcher Programmteil langsamer läuft als
Du es Dir wünschst. Dann gucke Dir den Teil an. Evtl. musst Du einen
anderen Algorithmus nehmen. Zu konkreten Alternativen kannst Du uns dann
fragen.
Abgesehen davon, ist ein Programm, das mit -O2 nicht läuft, obwohl es
ohne Optimierung läuft korrekturbedürftig. Es dürfte allenfalls
schneller laufen, sich abers sonst nicht anders verhalten. Das solltest
Du als erstes tun, ehe Du grundsätzlich an Geschwindigkeitsoptimierung
denkst.
Ohne das ich das jetzt im einzelnen nachgeprüft habe, wird die maximale
Tatktfrequenz der SPI-Schnittstelle von der Systemtaktfrequenz abhängen.
Das Datenblatt sollte Dir Auskunft geben, mit welchem Takt, welche
SPI-Frequenz möglich ist.
Verwirrter Anfänger schrieb:> Mit -O0, -O1 und -Os läuft das Programm nur bei 8 MHz.
Ich würde mich erstmal nicht drauf verlassen, dass -O2 durchgängig
schnelleren Code als -Os erzeugt. Auf dem AVR sind die meisten Befehle
in 1 oder 2 Taktzyklen abgearbeitet, sodass auf Grösse optimierter Code
schneller als -O2 sein kann. YMMV.
Schau dir erstmal das ASM listing an, vor allem der Routinen, die du im
Verdacht hast. Wieso sollte -O2 etwas "kaputt optimieren"? Auf dieses
Problem bin ich als Hobbyist bisher nicht gestossen (gut, ich optimiere
eigentlich immer auf Grösse).
Versuch auch mal mit dem alten Compiler zu übersetzen (avr-gcc-select)
und vergleich die Ergebnisse.
Guru schrieb:> Ich meine, so geht das nicht, das Du lediglich sagst, das Programm soll> schneller sein. Das ist zu allgemein, um Dir helfen zu können.>> Du musst erstmal feststellen welcher Programmteil langsamer läuft als> Du es Dir wünschst. Dann gucke Dir den Teil an. Evtl. musst Du einen> anderen Algorithmus nehmen. Zu konkreten Alternativen kannst Du uns dann> fragen.
Vielleicht hatte ich das nicht richtig ausgedrückt, es geht mir nicht
rein um die Optimierung auf Geschwindigkeit. Ich hab folgende Situation:
Ein Programm läuft immer bei 8MHz, bei 6.4MHz läuft es nur wenn ich -O2
nutze. Allerdings geht dann der Schlaf modus kaputt.
Hieraus folgere ich:
Bei den Optimierung 0,1 und s ist das Programm zu langsam. Bei der
Optimierung 2 werden irgendwelche Abfragen wegoptimert (wahrscheinlich
busy waits) die dazu führen, dass der µC nicht mehr schlafen geht.
Als Kandidat für die Optimierung hab ich den SPI Transfer im Verdacht,
da der die 49 kbps des Funkmoduls zufriedenstellen muss. Das sind vor
allem die Funktionen "rfm12Cmd" und "rfm12_sendByte".
In meinem ganzen Programm gibt es eigentlich keinen aufwendigen
Algorithmus, also zumindest nichts was man von O(n) zu O(log n)
optimieren könnte, eigentlich sind es nur Protokollimplementierungen.
> Abgesehen davon, ist ein Programm, das mit -O2 nicht läuft, obwohl es> ohne Optimierung läuft korrekturbedürftig. Es dürfte allenfalls> schneller laufen, sich abers sonst nicht anders verhalten. Das solltest> Du als erstes tun, ehe Du grundsätzlich an Geschwindigkeitsoptimierung> denkst.
Ich gehe davon aus, das hier die Reihenfolge von Operationen
umgefwürfelt wird, oder while Schleifen wegoptimiert werden. Allerdings
bin ich mir noch nicht sicher, inwiefern dass den Schlafmodus
beieinflussen sollte.
Aber hier fehlt mir halt die Erfahrung, wie man sowas richtig
programmiert, welche Variante währe zum Beispiel richtig:
Guru schrieb:> Ohne das ich das jetzt im einzelnen nachgeprüft habe, wird die maximale> Tatktfrequenz der SPI-Schnittstelle von der Systemtaktfrequenz abhängen.> Das Datenblatt sollte Dir Auskunft geben, mit welchem Takt, welche> SPI-Frequenz möglich ist.Verwirrter Anfänger schrieb:> per Software SPI
Ich denke nicht, dass die maximale Frequenz das Problem ist, sondern die
minimale. ich nehme an, dass meine SPI implementierung die Daten nicht
schnell genug rausschickt um den Sender zu befüllen.
Insbesondere nehme ich an, dass der Aufruf der Unterfunktionen zu lange
dauert und zu aufwendig ist, und dass diese bei -O2 ge-inlined werden.
Wanzenprofi schrieb:> Ich würde mich erstmal nicht drauf verlassen, dass -O2 durchgängig> schnelleren Code als -Os erzeugt. Auf dem AVR sind die meisten Befehle> in 1 oder 2 Taktzyklen abgearbeitet, sodass auf Grösse optimierter Code> schneller als -O2 sein kann. YMMV.
Das Problem ist halt, dass -O2 funktioniert, -Os aber nicht. Ich denke
dass das daran liegt, dass bei -Os eher Unterfunktionen als Funktionen
belassen werden, während die bei -O2 eher mal als inline gesetzt werden,
auch wenn der Code größer wird.
Wanzenprofi schrieb:> Schau dir erstmal das ASM listing an, vor allem der Routinen, die du im> Verdacht hast. Wieso sollte -O2 etwas "kaputt optimieren"? Auf dieses> Problem bin ich als Hobbyist bisher nicht gestossen (gut, ich optimiere> eigentlich immer auf Grösse).
Ich tippe mal, dass da Warteschleifen wegoptimiert werden und die
Reihenfolge von Befehlen verändert wird.
Aber ich werd mir mal das ASM listing angucken, danke für den Tipp
Wanzenprofi schrieb:> Versuch auch mal mit dem alten Compiler zu übersetzen (avr-gcc-select)> und vergleich die Ergebnisse.
Danke, dass werd ich gleich probieren.
Aber irgendwas stimmt doch da nicht, bei Deiner Darstellung.
>Zur Zeit läuft das Programm mit 6.4 MHz, wenn ich als Optimierung -O2>benutze, allerdings gehen dann ein paar andere Sachen kaputt
Was heisst denn, es "gingen ein paar Sachen kaputt". Ich bin, Deinem
ersten Post zufolge, davon ausgegangen, dass das Programm eben nicht
läuft. Bitte drücke Dich klarer aus.
>Als Kandidat für die Optimierung hab ich den SPI Transfer im Verdacht,
Verdacht? Prüf' es nach und dann sieh' weiter.
Was hier einfach zweifelhaft ist, ist das ein Programm
geschwindigkeitsmäßig durch die compilereigene Optimierung so hingebogen
werden soll, dass es schnell genug ist um zu funktionieren. Das kann ein
Profi in Ausnahmefällen sicherlich mal machen, wenn es anders garnicht
mehr geht. Aber wenn das nötig ist, wenn ein Anfänger was schreibt, dann
ist irgendwas grundsätzlich faul, denke ich. Das Programm muss so
parametriesierbar sein, das es (in den Grenzen des Grundsätzlichen) mit
jeder Frequenz geht.
>Ich denke nicht, dass die maximale Frequenz das Problem ist, sondern die>minimale.
Von was für einer minimalen oder maximalen Frequenz redest Du?
Ich lese hier
>Ich tippe mal,...>... Verdacht ...>Ich denke ...>Ich denke nicht, ...>Ich nehme an...
Nimm nicht nur an, sondern prüfe nach. Denke nicht nur, sondern ermittle
Fakten. Nicht tippen, das ist hier kein Glücksspiel.
Darauf will ich Dich nur mal aufmerksam machen. Ich meine es nicht böse,
will Dich nicht runtermachen oder so. Ich meine das ganz freundlich,
aber die Wahrheit klingt nunmal nicht immer angenehm.
Bisher kommt mir Dein Post vor wie eines der oft hier anzutreffenden, wo
jemand sich nicht die Mühe macht oder vielleicht auch nicht in der Lage
ist, gedanklich, d.h. sprachlich und/oder bildlich stringent vorzugehen.
Erwartest Du das sich hier jemand mit diesen Andeutungen, Vermutungen
und Annahmen Deinen Code anschaut um ein Problem zu finden, das
überhaupt nicht definiert ist.
Deswegen der Tip. Finde erstmal heraus wo genau der Flaschenhals liegt.
Finde erstmal heraus, was bei der Optimierung passiert, was Deinen Code
unbrauchbar macht. Das machen wir hier im allgemeinen nicht für Dich.
(Es gibt allerdings hier Leute die sich auch diese Mühe machen).
Und was diese Frage hier betriff:
>Aber hier fehlt mir halt die Erfahrung, wie man sowas richtig>programmiert, welche Variante währe zum Beispiel richtig:
Das führt bei mir zu kleinem Code,
wenn dein µP GPIOR0 - GPIOR2 hat, sollte man in GPIOR0 Bitfelder anlegen
und in GPIOR1 und GPIOR2 char Variable ablegen, die häufig gebraucht
werden.
So habe ich gerade ein Programm zusätzlich von 4340 Bytes auf 4254 Bytes
verkleinert.
Guru schrieb:> Aber irgendwas stimmt doch da nicht, bei Deiner Darstellung.>>>Zur Zeit läuft das Programm mit 6.4 MHz, wenn ich als Optimierung -O2>>benutze, allerdings gehen dann ein paar andere Sachen kaputt>> Was heisst denn, es "gingen ein paar Sachen kaputt". Ich bin, Deinem> ersten Post zufolge, davon ausgegangen, dass das Programm eben nicht> läuft. Bitte drücke Dich klarer aus.
Grundsätzlich geht es um einen kleinen Funksender mit einem ATtiny 45.
Bei 6.4 MHz und -O2 sendet der Sensor, aber geht nicht mehr schlafen
nach dem Senden, er sendet also durchgehend.
Bei 8 MHz und O0, O1 und Os sendet er, geht dann für 150 Sekunden
schlafen und sendet dann wieder.
Bei 6.4 MHz und -O0, -O1, -Os sendet er gar nicht, und dem
Stromverbrauch nach zu urteilen geht er auch nicht schlafen.
>>>Als Kandidat für die Optimierung hab ich den SPI Transfer im Verdacht,> Verdacht? Prüf' es nach und dann sieh' weiter.
Aber wie? Wenn ich den entferne funktioniert gar nichts mehr. Und wie
ich den weiter optimieren kann weiß ich nicht.
>> Was hier einfach zweifelhaft ist, ist das ein Programm> geschwindigkeitsmäßig durch die compilereigene Optimierung so hingebogen> werden soll, dass es schnell genug ist um zu funktionieren. Das kann ein> Profi in Ausnahmefällen sicherlich mal machen, wenn es anders garnicht> mehr geht. Aber wenn das nötig ist, wenn ein Anfänger was schreibt, dann> ist irgendwas grundsätzlich faul, denke ich. Das Programm muss so> parametriesierbar sein, das es (in den Grenzen des Grundsätzlichen) mit> jeder Frequenz geht.>
Als Parameter fallen mir grundsätzlich nur ein:
- Maximale SPI Frequenz für den SPI Slave 2.5 MHz
- Rate mit der der SPI Slave die Daten "verbraucht" 49 kbps
- Der Slave braucht pro Datenbyte noch ein Controllbyte.
Daraus folgt für mich, das ich bei 6.4 MHz maximal
(6.4MHz Inst/sek) / ((49 kb/s) * 2) = 65 Instruktionen / bit
brauchen darf. Das ist nach meiner Analyse der einzige Punkt im Program
bei den es auf die Geschwindigkeit ankommt.
War das mit parametriesierbar gemeint, oder hab ich da was falsch
verstanden?
>>Ich denke nicht, dass die maximale Frequenz das Problem ist, sondern die>>minimale.> Von was für einer minimalen oder maximalen Frequenz redest Du?
Die maximale SPI Frequenz ist denke ich nicht das Problem, da ich
maximal mit 2.5 MHz die Daten rausschicken darf, da bin ich auf jeden
Fall drunter. Ich muss aber mindestens 98 kb/s an den Slave liefern,
damit es bei der FIFO keine Probleme gibt.
>> Ich lese hier>>>Ich tippe mal,...>>... Verdacht ...>>Ich denke ...>>Ich denke nicht, ...>>Ich nehme an...>> Nimm nicht nur an, sondern prüfe nach. Denke nicht nur, sondern ermittle> Fakten. Nicht tippen, das ist hier kein Glücksspiel.>
Mein Problem ist halt das wie. Ich hab leider nur sehr eingeschränkte
Möglichkeiten hier, leider hab ich noch kein Oszilloskop oder Logic
Analysator, mit den ich mir genauer angucken könnte was zwischen den
Master und dem Slave passiert. Deshalb versuche ich gerade anhand der
Symptone auf die wahrscheinlichste Fehlerursache zu schließen.
Vielleicht gibt es ja auch noch andere Möglichkeiten das zu tun, aber
mir fallen zur Zeit keine ein.
> Darauf will ich Dich nur mal aufmerksam machen. Ich meine es nicht böse,> will Dich nicht runtermachen oder so. Ich meine das ganz freundlich,> aber die Wahrheit klingt nunmal nicht immer angenehm.
Keine Sorge, ich freu mich über alles was mir weiter hilft, auch wenn es
mir hilft mir selbst zu helfen :)
Vielen Dank auf jeden Fall das du dir überhaupt die Mühe machst.
>> Bisher kommt mir Dein Post vor wie eines der oft hier anzutreffenden, wo> jemand sich nicht die Mühe macht oder vielleicht auch nicht in der Lage> ist, gedanklich, d.h. sprachlich und/oder bildlich stringent vorzugehen.> Erwartest Du das sich hier jemand mit diesen Andeutungen, Vermutungen> und Annahmen Deinen Code anschaut um ein Problem zu finden, das> überhaupt nicht definiert ist.>> Deswegen der Tip. Finde erstmal heraus wo genau der Flaschenhals liegt.> Finde erstmal heraus, was bei der Optimierung passiert, was Deinen Code> unbrauchbar macht. Das machen wir hier im allgemeinen nicht für Dich.> (Es gibt allerdings hier Leute die sich auch diese Mühe machen).
Mein größtes Problem ist halt gerade das herausfinden, wo der
Flaschenhals liegt. Ich bin gerade dabei mir den Assembler Code
anzugucken, aber gerade auf avr hab ich damit noch nicht wirklich viel
gemacht. Der Tiny schränkt mich auch ein bischen ein, da da alle Pins
besetzt sind, und nichts mehr für irgendwelche Debug LEDs übrigbleibt.
auf einen größeren Chip kann ich das Programm schlecht überspielen, weil
ich keinen da habe, der intern eine 6.4 MHz Referenz hat und bei 8 MHz
funktioniert ja alles.
Hallo,
ich habe gerade die main.c überflogen.
|rfm12_interrupt()| würde ich als Macro anlegen, so wird der Code in der
1
ISR(INT0_vect){
2
3
rfm12_interrupt();
4
5
}
schneller und kleiner.
Wird das |cli();| in |rfm12_interrupt();| gebraucht ?
Die Macros
1
SENSOR_VCC_OUT();
2
3
SENSOR_VCC_LOW();
4
5
SENSOR_1_IN();
6
7
SENSOR_1_LOW();
8
9
SENSOR_2_IN();
10
11
SENSOR_2_LOW();
greifen jedes mal lesend und schreiben auf den Port bzw. DDR zu.
Über je eine Puffervar. für den port und das ddr spart man einige
Zugriffe und somit Zeit.
Bei den dummy ADC-Readout müssen die Rückgabewerte nicht gespeichert
werden !
z.B.:
1
temp1=adcRead(SENSOR_INTERNAL_CHANNEL);
Eine weitere Kleinigkeit kann man in diesen Blöcken sparen:
1
send_buffer[0]=gap>>8;
2
3
send_buffer[1]=gap&0xFF;
Optimiert:
1
send_buffer[0]=(uint8_t)(gap>>8);
2
send_buffer[1]=(uint8_t)gap;
und
1
for(i=0;i<8;i++)
2
3
send_buffer[i]=0;
ist evident und kann gelöscht werden !
Falls der C Compiler dies nicht included,
1
voidloop(void){..}
dann wäre es besser die Funktion so zu schreiben:
1
staticinlinevoidloop(void){..}
Das spart einige push / pop Anweisungen.
Das war's mal auf die Schnelle.
Normal sollte ein Programm mit allen Optimierungsstufen (O0,O1,O2 und
Os) laufen. Die Delay Routinen von GCC gehen ohne Optimierung nicht -
das ist aber eine Ausnahmen. Wenn es mit Optimierung nicht geht, ist das
oft so etwa wie ein Fehlendes volatile oder ein Wartescchleife die
abhanden kommt.
Bei einem Fehler mit den Sleep mode weiss man ja schon recht genau wo es
hakt - und sollte den Fehler im ASM Listing gut finden können.
Ein paar Ansatzpunkte zur Optimierung:
In dem Code wird in der ISR eine Funktion aufgerufen. Wenn man das
vermeiden kann, z.B. als Inline erzwingen, kann der Code deutlich
schneller und wohl auch kürzer werden. In der ISR werden so meist
unnötig viele Register gerettet.
Die Funktion Loop wird nur einmal in Main gebraucht, so etwas sollte
besser als Static inline deklariert werden, oder gleich ohne
Funktionsdefinition gemacht werden.
In Main werden Word variablen in Bytes zerlegt. Das geht vermutlich
schneller, aber auch weniger Portabel, mit der Definition einer UNION.
Bei der Einstellung des ADMUX wird viel mit &= und |= gearbeitet. Oft
kann man aber auch den ganzen Wert neu schreiben.
du könntest einen 74HC(T)573 als Latch hernehmen, mit einigen
Steuerleitungen kannst du so einen weiteren 8bit Port darstellen. Das
Latch lässt sich hochohmig stellen so das es deinen Port nicht mehr
beeinflusst und du ihn dann wieder für andere Sachen(Debugausgaben)
verwenden kannst inkl. ihn wieder als Eingangsport zu nutzen.
Anhang ist etwas einfach gefasst, am besten mal das Datenblatt
runterladen
Verwirrter Anfänger schrieb:> Der Tiny schränkt mich auch ein bischen ein, da da alle Pins> besetzt sind, und nichts mehr für irgendwelche Debug LEDs übrigbleibt.
darauf bezog ich meine Empfehlung für einen Latch. Ich habe z.B. mal mit
einem ATTiny26 und einem Latch mehrere ADC-Kanäle genutzt und zusätzlich
ein LCD im 8 Bit Modus angesteuert und hätte die 8 Pins fürs LCD
zusätzlich noch anderst nutzen können z.B. abwechselnd auch als
Eingänge.