Speichernutzung von Methoden im Vergleich zu kopiertem Code

7

Wenn Sie wirklich kleine Codeausschnitte haben, die in Ihrem Mikrocontroller-Programm mehrmals wiederholt werden, ist es besser, eine Methode zu schreiben? Ich meine besser in Bezug auf Leistung und Speichernutzung. Ich weiß, dass bei der Programmierung für den PC die Codewiederholung ein großes Problem ist, aber was ist mit Mikrocontrollern?

Im folgenden Beispiel (Arduino Nano mit Adafruit 2,8 "Cap Touch Display) sollte das Snippet aussehen, wenn die Zurück-Taste auf mehreren Bildschirmen gedrückt wird. Ich habe in der Arduino IDE überprüft, wie viel Speicherplatz jede Version benötigt:

Version 1, 15114 Bytes für komplettes Programm:

void battScreen(TS_Point p){
  // some other code
  // ....
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

void alarmScreen(TS_Point p){
  // some other code
  //...
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

void pumpScreen(TS_Point p){
  // some other code
  // ...
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

Version 2, 15234 Bytes für komplettes Programm:

void checkBack(TS_Point p){
  if ((p.x > backBox.posx) && (p.x < backBox.posx + backBox.sidex)){
    if((p.y > backBox.posy) && (p.y < backBox.posy + backBox.sidey)){
      screen = MAIN1;
      showMainScreen();
    }
  }
}

void battScreen(TS_Point p){
  // some other code
  // ...
  checkBack(p);
}

void alarmScreen(TS_Point p){
  // some other code
  // ...
  checkBack(p);
}

void pumpScreen(TS_Point p){
  // some other code
  // ...
  checkBack(p);
}

Wie Sie sehen können, benötigt Version 2 mit der zusätzlichen Methode 120 Byte mehr Speicherplatz. Gibt es eine Faustregel, wenn es sinnvoll ist, eine neue Methode zu erstellen, anstatt Code zu kopieren?

Lehue
quelle
Vielleicht gibt Ihnen die folgende Frage auch einen Einblick: stackoverflow.com/questions/144993/…
Michel Keijzers
Ihr Vergleich ist ziemlich seltsam; Funktionen benötigen WENIGER Platz als das Kopieren von Code. Ich werde versuchen, es zu implementieren, da ich denke, dass es ein anderes Problem mit Ihrem spezifischen Code gibt
frarugi87
2
Auch (mit Ausnahme des interessanten Ergebnisses), nicht zu optimieren, wenn es nicht benötigt wird. Meistens ist die Lesbarkeit und Wartbarkeit besser als die Leistung oder der verwendete Flash-Speicher.
Michel Keijzers
1
@ frarugi87 Dies ist möglich, obwohl ich gerade den Code wie in der Frage geändert habe. Vielleicht ist (ein Teil) des Grundes das, was Code Gorilla in seiner Antwort mit dem Kopieren von Variablen geschrieben hat.
Lehue

Antworten:

5

Es hängt davon ab, ob

Ein Aufruf zur Funktion beinhaltet einige Dinge

  1. Speichern Sie den Kontext
  2. Verschieben Sie die Parameter in die entsprechenden Register
  3. Geben Sie die Funktion ein
  4. Ausführen
  5. Speichern Sie schließlich den Rückgabewert in einem Register
  6. Stellen Sie den Kontext wieder her
  7. Kopieren Sie schließlich den zurückgegebenen Wert in die Variable

Wie Sie sehen können, ziemlich viele Dinge. Die fett gedruckten Punkte werden auch ausgeführt, wenn Sie keine Funktion verwenden.

Bitte beachten Sie, dass Sie, wenn diese Funktionen "Methoden" sind, auch einen "versteckten" Parameter haben (das Objekt, auf das Sie die Methode anwenden).

Warum also Funktionen verwenden? Nun, Sie müssen diesen Code nur einmal statt mehrmals in Flash schreiben.

Was ist das beste? Das hängt von Ihrer Bewerbung ab.

  • Sie rufen diese Funktion nur einmal auf? Verwenden Sie keine Funktion
  • Ihre Funktion ist wirklich kurz (zum Beispiel ein Einzeiler) *? Verwenden Sie keine Funktion
  • Ihnen geht der Flash-Speicher aus? Verwenden Sie eine Funktion
  • Du brauchst viel Geschwindigkeit? Verwenden Sie keine Funktion (Sie fügen Overhead hinzu)
  • Sie sind nicht in einem der vorherigen Fälle? Mach was du willst

    • Mit Einzeiler meine ich zum Beispiel so etwas wie byte sum(byte a, byte b) { return a + b; }; In diesem Fall nimmt die Funktion auch mehr Platz ein, da der Aufruffunktionsbefehl mit der Bytesumme kompatibel ist

Bitte beachten Sie, dass in der Regel mehr Lesbarkeit wichtiger ist als Leistungen. Daher sollte möglicherweise die Verwendung von Funktionen empfohlen werden. Ich meine, in Ihrem Fall würde ich sie wahrscheinlich verwenden, es sei denn, es gibt ein Problem

Was tun, wenn Sie keine Funktion verwenden möchten? Nun, es gibt andere Techniken:

  • Markieren Sie die Funktion als Inline. Dies ist ein Hinweis für den Compiler, keine Funktion zu erstellen, sondern diese jedes Mal zu replizieren. Dies ist nur ein Hinweis, daher kann der Compiler dies ignorieren
  • Schreiben Sie anstelle einer Funktion ein Makro. Dies wird jedes Mal ersetzt, aber der Code kann gewartet werden. Als Nachteil ist es viel schwieriger, mit den üblichen Methoden zu debuggen.

Nur eine Bemerkung: Normalerweise können intelligente Compiler selbst für Nicht-Inline-Funktionen erraten, welche Option (Inline oder Funktion) die beste ist. So können Sie dem Compiler in 90% der Fälle auch vertrauen

frarugi87
quelle
1
Sie haben die Optimierung nicht erwähnt: Wenn Sie eine Funktion nur einmal verwenden, platziert der Compiler den Code höchstwahrscheinlich nur inline, wodurch der Overhead einer Funktion vermieden wird. Sie können auch erzwingen, dass eine Funktion inline ist, sodass Sie den Vorteil haben, dass kein doppelter Code vorhanden ist, und dass Inline-Code effizienter ist.
Majenko
Die Optimierung ist in der letzten Bemerkung; Ich hätte es mehr hervorheben können. Wie können Sie den Compiler außerdem zum Inline zwingen? Ich dachte, dass das Schlüsselwort "inline" nur ein Hinweis ist; Gibt es ein anderes Schlüsselwort, um das Inlining zu erzwingen?
frarugi87
1
__attribute__((always_inline)).
Majenko
2

Der Grund, warum ich eher zur zweiten Methode tendiere, ist, dass es in der zweiten Version weniger Möglichkeiten für Fehler gibt.

Wenn die Speichergröße ein Hauptanliegen ist und Sie wissen, dass das Wiederholen des Codes weniger Speicherplatz erfordert, würde ich ihn als Makro definieren und das Beste aus beiden Welten herausholen.

Sie könnten den Code jedoch verbessern. In dem Moment, in dem Sie Kopien der Variablen an Funktionen übergeben, benötigt das Übergeben von const-Referenzen möglicherweise weniger Platz.void checkBack(const TS_Point& p){

Code Gorilla
quelle
mit in der void checkBack(const TS_Point& p)Tat reduziert die Größe um 80 Bytes. Danke für das!
Lehue
Was scheint seltsam ist jedoch, dass const type& variable nur reduziert Größe mit den TS_PointVariablen, nicht mit int, uint16_toder anderen normalen Typen, wo es der Größe zunimmt. Ich weiß, das ist nicht die Frage, aber kennen Sie den Grund dafür?
Lehue