Hi @ll, ich bin grade dabei für mein Praktikum (Studium) den letzten C164 Versuch zu programmieren. Leider haben wir von Keil µVision nur Evaluations Versionen und diese haben eine Beschränkung der Codelänge auf 8KB. Daher die Frage, wo kann man effektiv den Code verkürzen. Was sich schon gut gemacht hat, war alle strncpy durch for-Schleifen zu ersetzen. Außerdem lasse ich den Compiler schon auf Größe optimieren [#pragma ot(7, size)]. Hat noch jemand Ideen für typische Konstrukte die man verkürzen kann. Ich will den ganzen Code jetzt nicht posten, der wäre etwas zu lang. Sascha
hmm deinen Code zu kürzen ohne den zu kennen ist natürlich etwas schwierig. Du kannst alles kürzen was du kürzer schreiben kannst!
Was auch noch ganz gut klappt, möglichst viele Variablen in den near-Speierbereich legen. Um in far zu schreiben/lesen sind ja ein paar Maschienenbefehle mehr nötig.
Hinter vorgehaltener Hand: Wenn genug RAM, dann statt lokaler Variablen möglichst globale Variablen verwenden, denn dann entfällt das gepushe und gepoppe.
Die Idee mit den globalen Variablen ist auch gut. Ich denke da muss man keine Hand vorhalten. Außerdem habe ich bemerkt, dass die Variante A mehr Code braucht, als die Variante B: A: UmdrehTime = UmdrehTime + ( (float) drehBuffUeber[i] * ( (float) 0xFFFF ) + (float) drehBuff[i] )* T8SETING; B: UmdrehTime = UmdrehTime + (float) drehBuffUeber[i] * ( (float) 0xFFFF ) * T8SETING; UmdrehTime = UmdrehTime + (float) drehBuff[i] * T8SETING;
Variablen wo's geht wiederverwenden. Funktionen statt Makros. Überlegen, ob eine Funktion über ein zusätzliches "Steuerbit" nicht 2 oder 3 Funktionen ersetzen kann usw... Wenn man z.B. über ein Feld von XY immer wider verschiedene Funktionen aufruft, kann man auch eine Funktion: void ForEach(XY* pFirst, XY* pEnd, void (*foo)(XY* pA, XY* pB) ); machen, die dann die Schleife aufruft und dabei jedes Element mit seinem Nachbarn einsetzt. Beispiel (ungetestet)
1 | void Drucken(XY* pA) |
2 | { |
3 | LCD_out(pA->text); |
4 | } |
5 | |
6 | void ForEach(XY* pFirst, XY* pEnd, void (*foo)(XY* pA) ) |
7 | { |
8 | XY* pA; |
9 | for(pA=pFirst; pA!=pEnd; ++pA) |
10 | { |
11 | foo(pA); |
12 | } |
13 | } |
14 | |
15 | int main(int, char**) |
16 | { |
17 | ... |
18 | // pEnd = 1 nach dem letzen Element |
19 | ForEach(sXY, sXY+xycount, Drucken); |
20 | } |
Keine Format-Strings für printf verwenden und wenn´s doch nich anders geht wenigstens auf Fliesskomma dabei verzichten.Man kann z.B auch eine Fliesskommazahl in 2 Integer verwandeln und die dann formatiert ausgeben.Bei einigen Compilern kann man das in den Compiler/Linker Einstellungen angeben,das z.B keine Fliesskomma-Unterstützung erwünscht ist.
"Die Idee mit den globalen Variablen ist auch gut." Genau das Gegenteil ist der Fall ! Lokale Variablen können oft in Registern gehalten werden und sind daher viel effizienter, sowohl vom Code-Verbrauch, als auch von der Geschwindigkeit. Ich kenne den C164 nicht, aber dann muß der schon sehr vekorkst sein, wenns bei dem wirklich andersrum wäre. Sehr viel Code kann man auch sparen, wenn man statt float, int oder long nimmt. Da sind durchaus 1..2kB Ersparnis drin. Peter
In Funktionsaufrufen kann man manchmal sparen (zumindest beim 8051) wenn ne funktion wenig Inhalt, aber viele Parameter hat, lieber den inhalt direkt an der Aufrufstelle reinschreiben, als eine funktion zu benutzen. Das Stack-geschiebe braucht schließlich viel platz. Ist unsauber, ich weiß, (genau wie global RAM geschichte) aber wenn mal tatsächlich die paar wenige bytes speicher fehlen, um das programm raufzubraten, macht mans lieber.
Also das mit dem NEAR scheint nur in manchen Fällen was zu bringen. Manchmal wächst der Code dadurch auch. In den meisten Fällen bleibt er scheinbar gleich. Wobei ich das jetzt nur durch rumprobieren getestet habe, den Assembler zu zerlegen war mir jetzt zu aufwändig. Auf jeden Fall passt es jetzt. Ich glaube die beste Lösung war doch, dass mit den globalen Variablen. Damit kann man ohne großen Aufwand schon mal einiges an Code gut machen. Auf jedenfall bin ich jetzt klein genug zum Kompilieren. Sascha
Wie ist die Beschränkung auf Codegrösse realisiert - über "Abzählen" oder über eine Beschränkung des Adressraums oder über einen Offset der Codeposition im Speicherraum...? Ersteres ist nicht so kritisch und kann bei der Entwicklung berücksichtigt werden. d.h. vielleicht kannst du grössere Funktionen zusammenfassen und als getrenntes Stück in den µC laden... Dazu musst du rausfinden, wie du eine Funktion an eine von dir gewünschte Position im Speicher legst und den Linker überredest dafür zu relozieren. Im abgespeckten Restcode verwendest du die ausgelagerten Funktionen über Funktionspointer. Du solltest Ausschau nach Funktionen halten, die wenige bis keine Libraryfunktionen benutzen. Ich würde mir eine Initfunktion in dem ausgelagerten Block schreiben, die mir eine Tabelle mit den benötigten Funktionspointern zusammenbaut. Mit den Aufrufen von Libraryfunktionen im abgetrennten Codestück verfährst du im Prinzip gleich. Du baust dir eine Tabelle mit den benötigten Funktionspointern im Hauptprogramm.
Wenn das ganze ein Praktikum ist und davon die letzte Aufgabe wundert es mich schon ein wenig, wenn du 8Kb sprengst. Denn dann haben ja wohl auch andere Studenten das Problem bestimmt schon gehabt und den Prof. informiert. Wenn du die Aufgabe nicht nur lösen willst, sondern dabei auch was lernen willst: Mach dir Gedanken über deinen Algorithmus und deine Datenstrukturen, denn dies bestimmt zum größtenteil wie effektiv oder auch klein dein Programm wird. Ich finde es sehr verwunderlich das ein ersetzen von allen strncpy durch for-Schleifen einen kleineren Code ergibt. Lass dir mal die Mapping Datei ausgeben, da siehst du welche Funktionen den grössten Platzbedarf haben.
Ab und zu ist das sogar Absicht mit der Codegröße. Bei unserem letzten Praktikum war es auch nur ganz knapp möglich alle gewünschten Funktionen zu realisieren bevor einem der Speicher ausgegangen ist. War sozusagen eine Qualitätskontrolle :-)
Praktikum sieht halt so aus, dass man von Versuch zu Versuch zusätzliche Funktionen zum Programm hinzufügt. Ich bin auch nicht der einzige der das Problem hatte. Zu den globalen Variablen: Also nach genauerem Testen kann ich sagen, dass man nicht pauschal sagen ob das mehr oder weniger Code gibt. Das scheit echt davon abzuhängen ob er die irgendwann auf den Stack schreibt, oder ob er sie in einem Register hält. -> Ausprobieren Wie die Codegröße vom Linker limitiert wird weis ich nicht. Da habe ich mir auch noch keinen Kopf drum gemacht. Auf jeden Fall habe ich das ganze auf die Reihe bekommen und jetzt funktioniert es.
Globale Variablen sollten ebenso wie statische Variablen nicht auf dem Stack landen, das tun nur automatische Variablen.
zu "Zu den globalen Variablen: Also nach genauerem Testen kann ich sagen, dass man nicht pauschal sagen ob das mehr oder weniger Code gibt. Das scheit echt davon abzuhängen ob er die irgendwann auf den Stack schreibt, oder ob er sie in einem Register hält. -> Ausprobieren" Datentyp "sdata" verwenden (sdata ist im interen RAM des C16x und wird mit einem Wort-Befehl angesprochen) Bsp: unsigned int sdata wWort; Variablen die nur zwei Zustände haben (AN/AUS,WAHR/FALSCH etc) auch als "bit" deklarieren (Wieder ein Wort-Befehl) Bsp: bit bSearch = 1; Innerhalb Prozeduren/Funktionen kann per "register" Variablen im Register gehalten werden. Bei Interrupts die viele Register verwenden ist es sinnvoller eine ganze Registerbank zu verwenden Bsp: void NMIInt (void) interrupt 0x02 using TRAP_REG {} Gruss
Also wenn du mit float rechnest, dann hast du wohl schon verloren mit der Eval Version. Vergiss LIB Aufrufe, unter keinen Umstaenden PRINTF benuetzen. All die schoenen Dinge in "C" verbraten eine Unmenge Speicher und somit Programmgroesse. Robert
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.