Flash sparen - unerwartetes Ergebnis

Moin in die Runde,

ich habe, als C++ Novize, meine erste Anwendung fertig gestellt. Jetzt möchte ich sie nur noch "aufräumen" und optimieren. Großes Potential sehe ich in den vielen (später mehrsprachigen) LCD Ausgaben, die ich tätigen muss. Dazu ein Auszug aus dem Code:

    switch (lCnt) {
      case 0:
        lcdHelper(4); lcd.print(F("Default "));
//        lcd.print(F("Load Conf.: Default "));
        break;
      case 1:
        // Factory reset
        lcdHelper(4); lcd.print(F("Factory "));
//        lcd.print(F("Load Conf.: Factory "));
        break;
      case 2:
        // C1 step
        lcdHelper(4); lcd.print(F("C1 Step "));        
//        lcd.print(F("Load Conf.: C1 Step "));
        break;
      case 3:
        // C2 step
        lcdHelper(4); lcd.print(F("C2 Step "));        
//        lcd.print(F("Load Conf.: C2 Step "));
        break;
      case 4:
        // C1 slide
        lcdHelper(4); lcd.print(F("C1 Slide"));        
//        lcd.print(F("Load Conf.: C1 Slide"));
        break;
      case 5:
        // C2 slide
        lcdHelper(4); lcd.print(F("C2 Slide"));        
//        lcd.print(F("Load Conf.: C2 Slide"));
        break;
      case 6:
        // C1 macro
        lcdHelper(4); lcd.print(F("C1 Macro"));        
//        lcd.print(F("Load Conf.: C1 Macro"));
        break;
      case 7:
        // C2 macro
        lcdHelper(4); lcd.print(F("C2 Macro"));        
//        lcd.print(F("Load Conf.: C2 Macro"));
        break;                
      default:
        lcdHelper(4); lcd.print(F("cancel  "));      
//        lcd.print(F("Load Conf.: cancel  "));
        break;
      }

Der auskommentierte Teil ist die ursprüngliche Ausgabe. Alle Strings beginnen mit "Load Conf.: ", und nach meinem Verständnis landet jeder dieser redundanten Teile im Flash. Also habe ich mir eine Art ViewHelper geschrieben, um den redundanten Teil nur 1x im Flash abzulegen:

void lcdHelper(uint8_t i) {
  switch (i) {
    case 0:
      lcd.print(settings.lang == 1 ? F("\xE1ndern: Links/Rechts") : F("Left/Right to change"));
      break;
    case 1:
      lcd.print(settings.lang == 1 ? F("Zifferneing. einzeln") : F("Adjust digit-wise"));
      break;
    case 2:
      lcd.print(F("*** Initializing ***"));
      break;
    case 3:
      lcd.print(F("     Please wait    "));
      break;
    case 4:
      lcd.print(F("Load Conf.: "));
      break;      
  }
}

Nach meiner (naiven?) Kalkulation müsste ich damit exakt 8 x 13 = 104 Byte einsparen. Der tatsächliche Nutzen liegt jedoch nur bei 42 Byte.

Ich leide nicht an Speicherknappheit. Mit geht es ausschließlich um Verständnis. Kann jemand dieses Verhalten erklären?

Hi

Der Helper wird wohl auch Speicherplatz brauchen, auch die Aufrufe, daß der Helper Dir den Text auf's Display zaubert, wollen ja irgendwo stehen.

Denke, zuvor hattest Du die jetzt auskommentierten Varianten drin und zumindest den Helper-Case 4 nicht (oder gar den ganzen Helper), danach hast Du zig Aufrufe des Helper und den Helper selber ja auch noch.

Alternativ habe ich Deine Frage nicht ganz verstanden :o

MfG

Der Helper war bereits erstellt, als ich die ursprüngliche (komplette) Ausgabe auskommentiert habe. Der Helper sollte bei 9 identischen Ausgabeteilen wirken. Er "kostet" natürlich selbst auch mindestens eine davon - und belegt auch selbst Ressourcen. Ich hätte aber nicht erwartet, dass die doch relativ groß ausfallen.

====
EDIT: Nachdem ich jetzt auch eine weitere Funktion (Save Config) auf den Helper umgestellt habe, ist das Ergebnis total verwirrend. Es gibt ebenfalls 9 Vorkommen des identischen String-Teils, und diesmal ist der Speicherbedarf mit Helper sogar größer, als ohne. Es scheint so zu sein, dass bei jedem Aufruf des Helpers der String erneut im Flash abgelegt wird.

Wenn du mit Standard-IDE Einstellungen für einen kleinen 8bit AVR ( Uno/Nano etc. ) kompilierst, sollte eigentlich auf Speicherplatz optimiert werden.

Dass ein Text, der nur einmal in deinem Code vorkommt, ( F("Load Conf.: ") ) mehrfach im Flash-Speicher stehen soll, wundert mich auch.
Falls du überhaupt von einem "echten" :wink: Arduino redest: Ein Fall für avr-objdump.

Umgekehrtes Verhalten hätte ich dem Compiler eher bewundernd zugetraut.

Doch, es ist ein Original Uno Rev. 3.

ich habe jetzt in einem anderen Beitrag zum gleichen Thema den Hinweis gefunden, dass das Zusammenbasteln von Strings durch mehrfache Aufrufe einer Function oft nach hinten losgeht:

Zitat Dr. Diettrich:

Jeder Aufruf eines Unterprogramms kostet Speicher. Deshalb dürfte das Zerlegen längerer Strings eher mehr Speicher kosten (beim Aneinanderhängen der Teile), als an Literalen eingespart werden kann.

Dies scheint so zu sein.

Du könntest mal mit PROGMEM - Arrays of strings probieren. Ist kein leichter Stoff für einen C++ Novizen, aber Du willst ja was lernen :slight_smile:

Ob es zu einer Einsparung führt, vermag ich nicht zu versprechen.

Texte könnte man auch im EEPROM ablegen, geht aber wohl an Deiner Zielsetzung vorbei, Du hast ja noch genügend Speicher frei.

Demokrit:
ich habe jetzt in einem anderen Beitrag zum gleichen Thema den Hinweis gefunden, dass das Zusammenbasteln von Strings durch mehrfache Aufrufe einer Function oft nach hinten losgeht:

Ich glaube damit ist eher etwas anderes gemeint. Funktionsaufrufe kosten RAM. Aber auch nur solange man in der Funktion ist

@agmue: Genau, Lernen ist mein Haupt-Anliegen. Ich werde mich da mal einlesen.

@Serenify: Der RAM Verbrauch blieb durch meine Minimalfunktion unverändert. Im Flash sah dies anders aus. Bei einer Ausgabe hatte ich einen kleinen Nutzen durch die Auslagerung, bei einer anderen (fast identischen) einen merklichen Verlust. Einen roten Faden konnte ich dabei bislang nicht entdecken.

Der Compiler kann dir nur den statischen RAM Verbrauch durch globale Variablen anzeigen. Was lokal während der Laufzeit gebraucht wird ist was ganz anderes

Aktuell habe ich lt. IDE 74% des Flash belegt, und 60% des RAM. Ich habe so weit als möglich mit lokalen Variablen gearbeitet, die m.W. nur während der Laufzeit RAM belegen, und danach wieder frei gegeben werden. Ich hoffe, ich sehe das so richtig.

Das Speicherverhalten der zusammengebastelten Output Strings bleibt mir weiterhin ein Rätsel. Manche davon zeigen nämlich exakt den Nutzen, den ich mir erhofft habe (z.B. Benutzerführung für den Encoder, wo nichts zusammen gesetzt wird). Bei Mehrfachnutzung dieser Strings reduziert sich der Flash Verbrauch fast wie errechnet. Ich nutze die "LiquidCrystal_I2C" Bibliothek. Vielleicht liegt ja dort der Grund für das Verhalten begraben.