ich lerne gerade C++ mithilfe eines ESP32 Projekts, keine nennenswerten C-Kenntnisse, daher Anfängerfrage:
In meinem Projekt habe ich eine std::queue um einen asynchronen event bus (oder command queue wenn man will) zu realisieren. Innerhalb von interrupts wird was reingeschrieben und in der main loop wird dann am anderen Ende ausgelesen und abgearbeitet.
Jetzt habe ich gelesen, dass solche Datenstrukturen, wie auch z.B. vector, heap allocation machen. Ich geh jetzt zwar nicht davon aus, dass die queue gefährlich groß wird, aber ich habe in dem Zusammenhang auch von heap fragmentation gelesen, die sich über die Zeit verschlimmern kann. Da die queue ständig beschrieben und ausgelesen wird, kann ich mir vorstellen (weiß es aber nicht genau), dass da viel Allokation und Freigabe passiert die ganze Zeit und daher fragmentiert (falls ich das mit der Fragmentierung jetzt richtig verstehe).
Insgesamt finde ich widersprüchliche Infos dazu, Hinweise, dass man heap allocation im embedded gar nicht machen soll, andere, die sagen, das sei quatsch, wenn man die Größe der collections voraussagen kann. Es scheint auch die Möglichkeit zu geben, die Allokationsstrategie auf diverse Arten zu beeinflussen. Da blick ich aber noch nicht so durch.
Könnt ihr mir ein paar Tipps zur Orientierung geben oder klarstellen, was ich da verzapfe?
ich lerne gerade C++ mithilfe eines ESP32 Projekts,
Kein gute Idee
In meinem Projekt habe ich eine std::queue um einen asynchronen event
bus (oder command queue wenn man will) zu realisieren. Innerhalb von
interrupts wird was reingeschrieben und in der main loop wird dann am
anderen Ende ausgelesen und abgearbeitet.
Achtung: in C++ sind konflikt-behaftete Zugriffe auf eine gemeinsame Datenstruktur undefined behaviour, wenn keine geeigneten Maßnahmen ergriffen werden. Die Realisierung von std::queue ist sicher nicht lock-free/atomic, also musst Du die happens-before Relation einhalten. Bei Nebenläufigkeit via Threads könnte man das mit mutex/cv herstellen, bei Interrupts geht es über Interruptsperren und memory-barriern.
ich lerne gerade C++ mithilfe eines ESP32 Projekts,
Kein gute Idee
das mit den guten Ideen hab ich vor Jahren aufgegeben. Sonst würde ich hier nicht posten ;)
In meinem Projekt habe ich eine std::queue um einen asynchronen event
bus (oder command queue wenn man will) zu realisieren. Innerhalb von
interrupts wird was reingeschrieben und in der main loop wird dann am
anderen Ende ausgelesen und abgearbeitet.
Achtung: in C++ sind konflikt-behaftete Zugriffe auf eine gemeinsame
Datenstruktur undefined behaviour, wenn keine geeigneten Maßnahmen
ergriffen werden. Die Realisierung von std::queue ist sicher nicht
lock-free/atomic, also musst Du die happens-before Relation einhalten.
Bei Nebenläufigkeit via Threads könnte man das mit mutex/cv herstellen,
bei Interrupts geht es über Interruptsperren und memory-barriern.
sprichst du darauf an, dass ggf verschiedene Interrupts konfliktbehaftet auf die Datenstruktur zugreifen?
Aus reiner Anwendungssicht ist Nebenläufigkeit und "korrekte" Synchronisation nicht so kritisch (glaube ich aktuell). Ich benutze das nur um die interrupts und einen asynchronen Webserver (der crasht, wenn ich blockierendes Zeugs direkt aus dessen handlern aufrufe) zu entkoppeln und sich das mit queues sehr bequem gestaltet.
Insgesamt finde ich widersprüchliche Infos dazu, Hinweise, dass man heap
allocation im embedded gar nicht machen soll, andere, die sagen, das sei
quatsch, wenn man die Größe der collections voraussagen kann.
Dynamische Speicherverwaltung scheitert manchmal an der Fragmentierung des Heaps. Ob dein Programm von dem Problem betroffen ist, hängt sehr stark davon ab, in welcher Reihenfolge bein Programm unterschiedlich große Blöcke belegt und wieder frei gibt.
Es ist richtig, dass std::queue und andere C++ Standard-Datenstrukturen, wie std::vector, Heap-Speicherallokation verwenden, um ihre Elemente zu speichern. Heap-Fragmentierung kann tatsächlich zu einem Problem werden, wenn es um Embedded-Systeme geht, da der verfügbare Heap-Speicher begrenzt sein kann und durch häufige Allokation und Freigabe von Speicherplatz fragmentiert werden kann.
In Bezug auf Ihr Projekt, da die std::queue ständig beschrieben und ausgelesen wird, ist es wahrscheinlich, dass es zu einer Menge Heap-Allokation und Freigabe kommt. Eine Möglichkeit, dies zu vermeiden, ist die Verwendung einer Ringpuffer-Datenstruktur anstelle einer std::queue. Ein Ringpuffer verwendet einen festen Speicherbereich und kann daher die Heap-Fragmentierung vermeiden.
Eine andere Möglichkeit besteht darin, die Verwendung von std::queue mit Vorsicht zu verwenden und sicherzustellen, dass die maximale Größe der Queue begrenzt wird, um die Heap-Fragmentierung zu minimieren.
Es gibt auch Möglichkeiten, die Allokationsstrategie in C++ zu beeinflussen, wie z.B. durch die Verwendung von benutzerdefinierten Allokatoren. Aber das ist ein fortgeschrittenes Thema und es ist empfehlenswert, sich erstmal mit den Grundlagen auseinandersetzen.
Letztendlich hängt es von den Anforderungen Ihres Projekts und der verfügbaren Ressourcen ab, welche Technik am besten geeignet ist. Es ist wichtig, die Auswirkungen auf die Speicherverwaltung und die Leistung zu berücksichtigen, bevor Sie eine Entscheidung treffen.
Dynamische Speicherverwaltung scheitert manchmal an der Fragmentierung
des Heaps. Ob dein Programm von dem Problem betroffen ist, hängt sehr
stark davon ab, in welcher Reihenfolge bein Programm unterschiedlich
große Blöcke belegt und wieder frei gibt.
um einen asynchronen event
bus (oder command queue wenn man will) zu realisieren
Nur so als Denkanstoß:
Schau dir die Datenstruktur (Binary-)Heap / priority queue an.
Damit kannst du deine Events/Tasks priorisieren, und die ganze Struktur lässt sich auf ein Array (ggfs. mit fixer Größe) mappen, dann bist du alle dynamischen Speicheranforderungen los.
Nachteil: Einfügen&Entnehmen eines Events sind dann O(n log n). Solange du nicht tausende Events verwalten willst, ist das aber relativ egal.
Apropos dynamische Speicherverwaltung: ist malloc&co sicher aus der ISR verwendbar, oder knallt das wenn der IRQ grad einen malloc-Aufruf aus der main unterbricht?
Mikrocontroller und Boostlib: jetzt wird's richtig lustig.
Wenn Dein Projekt privat ist, OK, probiere alles aus, was es gibt.
Sollte Dein Projekt irgendwelche Sicherheitsbelange (Safety) erfordern?
!!! DANN FINGER WEG VON DYNAMISCHEN SPEICHER - OPERATIONEN !!!
Dann müssen alle Variablen statisch angelegt werden.
Insgesamt finde ich widersprüchliche Infos dazu, Hinweise, dass man heap
allocation im embedded gar nicht machen soll, andere, die sagen, das sei
quatsch, wenn man die Größe der collections voraussagen kann. Es scheint
auch die Möglichkeit zu geben, die Allokationsstrategie auf diverse
Arten zu beeinflussen. Da blick ich aber noch nicht so durch.
Diese Pauschalaussagen kommen aus Zeiten wo Mikrocontroller keine 8MB SRAM hatten.
Dass das bei einem ESP32 wohl nicht mehr so allgemein gilt macht wohl folgendes deutlich:
Diese Pauschalaussagen kommen aus Zeiten wo Mikrocontroller keine 8MB
SRAM hatten.
Dass das bei einem ESP32 wohl nicht mehr so allgemein gilt macht wohl
folgendes deutlich:
1
>╭─vinci@garudain~/esp-idftook2ms
2
>╰─λgrep-r"malloc"|wc-l
3
>3741
4
>
3741 mallocs im Quellcode sagen ja noch nichts darüber, wie häufig diese aufgerufen werden, unter welchen Bedingungen. Der OT spricht von einer dynamischen queue die ständig in Bewegung ist. Schwer zu sagen zu was für einer Fragmentierung das führt. Aber du hast Recht, schwarzweiß Pauschalisierung ist wenig hilfreich.
Sollte Dein Projekt irgendwelche Sicherheitsbelange (Safety) erfordern?
!!! DANN FINGER WEG VON DYNAMISCHEN SPEICHER - OPERATIONEN !!!
Dann müssen alle Variablen statisch angelegt werden.
Bullshit. Safety bedeutet, dass solche Fehlerfälle erkannt werden und rechtzeitig in den sicheren Zustand gewechselt wird. Das schließt dynamische Speicherverwaltung keinesfalls aus.