>Anders geht es nicht, auch wenn du vermutlich was anderes hören>wolltest.
Gibt's da keine Problem mit der Garbage Collection?
Gibt es ein Delete auf einem STM32?
chris schrieb:> Gibt's da keine Problem mit der Garbage Collection?
Welche Implementation benutzt du, welche Garbage Collection bietet?
Keine mir bekannte C++ Implementation für Mikrocontroller macht das.
chris schrieb:> Gibt es ein Delete auf einem STM32
Wenn es new gibt, ja. Ob das ganze sinnvoll ist ist eine andere Frage.
Und dann gehst du halt hin und machst eine schleife in der du durch den
Vector iterierst, delete auf die objekte aufrufst und danch clearst du
den vector.
chris schrieb:> Hier der nicht funktionierende Versuch auf einem ESP32:
Nicht funktionieren wird wohl Compilerfehlermeldungen bedeuten. Die
solltest du lesen und verstehen.
Und lies die Kapitel zu Pointern und new/delete in deinem C-Buch.
Oliver
Oliver S. schrieb:> Und lies die Kapitel zu Pointern und new/delete in deinem C-Buch.
Nein! Lies das Kapitel zu smarten pointern in einem aktuellen C++ Buch
(>=C++11). In modernem C++ sollte man new/delete eigentlich nicht mehr
verwenden müssen, abgesehen von einzelnen Spezialfällen.
Beispiel:
1
std::vector<std::unique_ptr<Test>>Tests;
2
3
for(intn=0;n<ANZAHL;n++)
4
{
5
Tests.emplace_back(std::make_unique<Test>(n));
6
}
7
8
Tests.clear();
Vorteile:
- Es ist direkt ersichtlich, dass der vector die Lebenszeit
kontrolliert.
- Bei einem clear des vector werden die Instanzen sauber zerstört.
Außerdem ist der Code sicherer, wenn eine Exception auftritt, weil am
Ende der vector sich selbst um die Zerstörung kümmert, auch wenn du aus
der Loopfunktion springst. In deinem Beispiel würde er dann beim
schließen der Applikation zerstört werden.
Wenn du den vector nur in loop() benötigst, dann würde ich den vector
auch nur lokal in der loop Funktion anlegen. Der Stack ist dabei kein
Problem, weil die Daten des vector sowieso auf dem heap liegen.
Wenn dir außerdem vorher die endgültige Größe des vectors bekannt ist,
dann kann du mit reserve schon mal den Speicher dafür anfordern. Das
spart evtl. mehrfache Speicheranforderungen durch den vector, wenn die
bisherige Anzahl von reservierten Elementen bei einem push_back
überschritten wird.
Dr. Sommer schrieb:> Welche Implementation benutzt du, welche Garbage Collection bietet?> Keine mir bekannte C++ Implementation für Mikrocontroller macht das.
C++ kann für new/delete keine Garbage Collection verwenden, weil man
sich in C++ darauf verlassen kann, das beim delete der Destruktor
aufgerufen wird. Sonst würde RAII z.B. für mutexes nicht funktionieren
(https://de.wikipedia.org/wiki/Ressourcenbelegung_ist_Initialisierung).
Ob der Speicher dann direkt wieder dem Heap zur Verfügung gestellt wird
oder dies erst mit einer Garbage Collection erfolgt ist dann wieder eine
andere Sache.
leo schrieb:> C/C++ haben keine Garbage Collection.
Um hier Missverständnisse zu vermeiden.
Aus Sicht von new/delete gibt es keine Garbagcollection. D.h. das Objekt
wird direkt zerstört und damit auch der Destruktor aufgerufen.
Was ich meinte bezog sich auf die interne Implementierung des Heaps.
Meines Wissens nach muss der Heap den Speicher nicht gleich bei delete
wieder zu Verfügung stellen, sondern kann dies auch erst bei einer
Garbagecollection tun.
leo schrieb:> M.K. B. schrieb:>> sondern kann dies auch erst bei einer>> Garbagecollection tun.>> Was verstehst du hier nicht: C/C++ haben keine Garbage Collection.>> leo
Aber C und C++ könnten einen GC in der Implementation von malloc nutzen
und das ist es was M.K. B. geschrieben hat.
mh schrieb:> Aber C und C++ könnten einen GC in der Implementation von malloc nutzen> und das ist es was M.K. B. geschrieben hat.
Genau!
leo schrieb:> Aha. Beispiel bitte.
Ich kenne keine konkreten Beispiele, aber es ging bei meiner Aussage
auch nur darum, dass es aus Sicht der Sprache prinzipiell erlaubt ist.
Ohne mich damit jetzt im Detail auszukennen, aber ein Prozess in einem
Betriebssystem könnte z.B. erstmal lokal im Prozess das free ausführen.
Erst wenn eine bestimmte Zeit oder ein anderes Kriterium erfüllt ist,
dann würde der Prozess diesen Speicher an das Betriebssystem zurückgeben
und damit stünde dieser dann auch anderen Prozessen zur Verfügung.
leo schrieb:> Nein.> [ ] Du weisst, wie ein GC funktioniert?
Um es nochmal klarzustellen.
Kein GC aus Sicht von new/delete.
GC bei der Speicherverwaltung durch den Heap.
M.K. B. schrieb:> GC bei der Speicherverwaltung durch den Heap.
Welches OS macht denn sowas? Eine MMU kann Speicherseiten ein- bzw.
ausblenden. Dass ist aber kein GC.
leo schrieb:> Aha. Beispiel bitte.https://www.hboehm.info/gc/
Der C++ Standard erlaubt definitiv ein "normales" GC in der Sprache,
ähnlich wie Java. Das benutzt nur keiner. Daher der verbreitete
Irrglaube, C++ würde prinzipiell kein GC unterstützen.
chris schrieb:> Hier der nicht funktionierende Versuch auf einem ESP32:
Das ist vorne und hinten falsch. Lies erstmal ein C++ Buch. C++ ist eine
komplexe Sprache, die lernt man nicht mal eben mit ein paar Tutorials.
Und überlege, ob du wirklich dynamische Speicherverwaltung brauchst. Die
macht nämlich nur Sinn, wenn man Speicher zu verschiedenen Zeitpunkten
unterschiedlich nutzen will, also z.B. mal 100 Test Objekte anlegen,
dann wieder freigeben, dann 100 Test2 Objekte. Wenn eine solche
abwechselnde Nutzung nicht gegeben ist, wird nach der Freigabe der
Speicher gar nicht benutzt, wofür es kein Geld zurück gibt. Daher ist es
oft besser, einfach fixe Arrays (keine Vektoren) der benötigten Größe
anzulegen und nie was freizugeben.
>Die macht nämlich nur Sinn, wenn man Speicher zu> verschiedenen Zeitpunkten unterschiedlich nutzen will,> also z.B. mal 100 Test Objekte anlegen,>dann wieder freigeben, dann 100 Test2 Objekte.
Das Programm soll folgendes tun:
1. es werden N-Testobjekte angelegt ( z.B. N=100 )
2. Die Testobjekte werden ein Zeit lang benutzt und eine Test.execute()
Methode aufgerufen.
3. Der Speicher, den die Testobjekte belegen, muss wieder frei gegeben
werden.
4. Goto 1.
Ich vermute, dass im Framework des ESP32 keine Garbage Collection
implementiert ist. In C wäre das Ganze extrem einfach zu lösen.
1. man reserviert Speicher für die Objekte und merkt sich den Speicher
Anfang
2. man legt Objekte an ( Strukturen )
3. wie oben ( 1-3)
4. Man setzt den Objektpointer zurück
5. Goto 1.
Aufwand, die Objekte zu löschen: Null
chris schrieb:> 3. Der Speicher, den die Testobjekte belegen, muss wieder frei gegeben> werden.
Warum muss? Wofür wird der Speicher danach gebraucht?
chris schrieb:> 4. Man setzt den Objektpointer zurück>> Aufwand, die Objekte zu löschen: Null
Haha, weil du sie gar nicht löschst! Wenn du nur den Pointer auf Null
setzt, ist der Speicher noch belegt, und du kannst ihn nie wieder
freigeben - ein Speicherleck. Wenn du den Speicher tatsächlich
freigibst, über die free() Funktion, ist der Aufwand natürlich nicht
null, denn die Funktion kann ggf. nicht trivial sein.
chris schrieb:> In C wäre das Ganze extrem einfach zu lösen.
In C++ sogar noch einfacher dank unique_ptr. Allerdings muss man dazu
schon ein bisschen wissen was man tut und nicht einfach irgendwas
zusammen stoppen.
chris schrieb:> Das Programm soll folgendes tun:> 1. es werden N-Testobjekte angelegt ( z.B. N=100 )
Muss dafür wirklich jedes dieser Objekte einzeln dynamisch angelegt
werden? Woraus besteht so ein Objekt? Wäre es ein Problem, wenn das
Objekt kopiert werden müsste? Wird Polymorphie benötigt?
> 2. Die Testobjekte werden ein Zeit lang benutzt und eine Test.execute()> Methode aufgerufen.> 3. Der Speicher, den die Testobjekte belegen, muss wieder frei gegeben> werden.
Muss jedes Objekt einzeln wieder freigegeben werden, oder reicht es,
wenn nach den 100 Tests alle am Stück gelöscht werden?
> 4. Goto 1.>> Ich vermute, dass im Framework des ESP32 keine Garbage Collection> implementiert ist. In C wäre das Ganze extrem einfach zu lösen.
Was macht es in C++ denn schwierig? Man kann es da genauso machen wie in
C.
> 1. man reserviert Speicher für die Objekte und merkt sich den Speicher> Anfang> 2. man legt Objekte an ( Strukturen )
Wo? Wie? Oben nutzt du für jedes Objekt einzeln new. Die Entsprechung in
C wäre, dass du für jede der Strukturen einzeln mit malloc() Speicher
reservierst. In dem Fall müsstest du die auch alle einzeln wieder
freigeben.
> 3. wie oben ( 1-3)> 4. Man setzt den Objektpointer zurück> 5. Goto 1.>> Aufwand, die Objekte zu löschen: Null
Verschwinden sie auf magischem Weg?
chris schrieb:> In C wäre das Ganze extrem einfach zu lösen...Dr. Sommer schrieb:> Das ist vorne und hinten falsch. Lies erstmal ein C++ Buch. C++ ist eine> komplexe Sprache, die lernt man nicht mal eben mit ein paar Tutorials.
Vieles, was für C++ gilt, gilt auch für C,
Oliver
Bevor man anfängt drauf los zu coden, sollte man sich erst mal
überlegen:
- Haben alle Objekte den gleichen Typ, oder sollen auch abgeleitete
Typen möglich sein (Polymorphie)? Sollen sogar komplett unterschiedliche
Typen ohne gemeinsame Basisklasse möglich sein?
- Wird die Größe des Containers initial festgelegt, oder sollen Elemente
einzeln hinzugefügt und einzeln gelöscht werden können? Wenn ja, wie
oft? Wenn nein, steht die Größe beim Kompilieren schon fest oder erst
zur Laufzeit?
- Wenn die Elemente gelöscht wurden, wofür wird der Speicher dann
gebraucht? Nur wieder für das erneute Befüllen des selben Containers?
- Soll der Container nur von Anfang bis Ende durchgegangen werden, oder
auch wahlfreier Zugriff per Index möglich sein?
- Soll eine Assoziation/Mapping erfolgen, d.h. Elemente z.B. anhand
eines Namens o.ä. suchen, und nicht nur nach Index?
- Spielt die Reihenfolge eine Rolle?
- Müssen die Elemente alle direkt im Speicher hintereinander stehen
(z.B. für DMA) oder können sie verteilt sein?
- Sind die Elemente simple Datenobjekte oder haben sie virtuelle
Funktionen?
Siehe auch: https://stackoverflow.com/q/471432
Dr. Sommer schrieb:> Der C++ Standard erlaubt definitiv ein "normales" GC in der Sprache,> ähnlich wie Java. Das benutzt nur keiner. Daher der verbreitete> Irrglaube, C++ würde prinzipiell kein GC unterstützen.
Hm. Ich würde es auch so formulieren, wie M.K.B;
M.K. B. schrieb:> Kein GC aus Sicht von new/delete.> GC bei der Speicherverwaltung durch den Heap.
Der Sprachstandard kennt keinen GC. Der definiert
allocator-/deallocator-Funktionen, die nach im Standard definierten
Regeln von von new und delete aufgerufen werden. Nach Aufruf von delete
ist das Objekt nicht mehr existent, und der Speicher deallokiert.
Mehr definiert der Sprachstandard nicht. Der ganze Rest liegt in der
Verantwortung der Implementierung.
Oliver
Rolf Magnus
>Muss dafür wirklich jedes dieser Objekte einzeln dynamisch angelegt>werden? Woraus besteht so ein Objekt? Wäre es ein Problem, wenn das>Objekt kopiert werden müsste? Wird Polymorphie benötigt?
Die Testobjekte sind von einem gemeinsamen Objekt abgeleitet, aber ihr
Speicherverbrauch ist unterschiedlich groß.
>> 2. Die Testobjekte werden ein Zeit lang benutzt und eine Test.execute()>> Methode aufgerufen.>> 3. Der Speicher, den die Testobjekte belegen, muss wieder frei gegeben>> werden.>Muss jedes Objekt einzeln wieder freigegeben werden, oder reicht es,>wenn nach den 100 Tests alle am Stück gelöscht werden?
Die gesamte Testliste wird durch eine neue mit einer anderen Anzahl von
Objekte ersetzt. Die alten werden nicht mehr gebraucht, die ganze Liste
kann also in einem Stück gelöscht werden.
chris schrieb:> aber ihr> Speicherverbrauch ist unterschiedlich groß.chris schrieb:> In C wäre das Ganze extrem einfach zu lösen.
Jetzt bin ich gespannt, wie das "extrem einfach" in C geht. Viele
unterschiedliche structs mit malloc() anlegen, Pointer darauf in ein
Array, und danach alle structs und das Array mit free() freigeben? Dann
stimmt aber
chris schrieb:> Aufwand, die Objekte zu löschen: Null
nicht.
Wie schon oben angedeutet wurde, beschwert sich der C-Compiler nicht
über ein fehlendes free(). Was die Programmierung natürlich sehr einfach
machen kann ;)
Oliver
chris schrieb:> Mein Programm hat ein Array von Objekten, die mit>>
1
>Objecto=newObject();
2
>
Das erzeugt kein Array, sondern ein einziges Objekt von Typen Object.
Das geht auch viel einfacher mit:
1
Objecto;
C++ ist deutlich anders, als Java! Der "übliche" Weg ein Array von
Objekten anzulegen, wäre dann entweder:
1
Objectos[10];
oder
1
std::array<Object,10>os;
In C++ arbeitet man viel häufiger mit Werten ohne den Umweg über Zeiger,
während in Java (fast) alles erst einmal nur ein Zeiger ist, der eben
auch Null sein kann.
Um das jetzt mal etwas abzukürzen, hier ein komplettes Beispiel mit
einer Basis-Klasse "Test" und 2 davon abgeleiteten, welche in einem
vector abgelegt werden:
1
#include<iostream>
2
#include<memory>
3
#include<vector>
4
5
classTest{
6
public:
7
virtual~Test(){}
8
virtualvoidshow()=0;
9
};
10
11
classTest1:publicTest{
12
public:
13
Test1(inti):id(i){}
14
virtualvoidshow(){std::cout<<id<<std::endl;}
15
private:
16
intid;
17
};
18
19
classTest2:publicTest{
20
public:
21
Test2(floatf):id(f){}
22
virtualvoidshow(){std::cout<<id<<std::endl;}
23
private:
24
floatid;
25
};
26
27
28
constexprstd::size_tANZAHL=4;
29
30
31
intmain(){
32
std::vector<std::unique_ptr<Test>>Tests;
33
// Speicher für alle Zeiger auf einmal anlegen
34
Tests.reserve(ANZAHL);
35
36
// Objekte einfügen
37
for(std::size_ti=0;i<ANZAHL/2;++i)
38
Tests.push_back(std::make_unique<Test1>(i));
39
40
for(std::size_ti=ANZAHL/2;i<ANZAHL;++i)
41
Tests.push_back(std::make_unique<Test2>(i+0.3f));
42
43
// Ausgeben
44
for(auto&t:Tests)
45
t->show();
46
47
// Alle löschen
48
Tests.clear();
49
50
// Eins einfügen
51
Tests.push_back(std::make_unique<Test1>(42));
52
53
// Ausgeben (diesmal leer)
54
for(auto&t:Tests)
55
t->show();
56
57
// Bei Rückkehr der Funktion wird "Tests" und alle Inhalte automatisch gelöscht
58
}
vector und unique_ptr sorgen dafür dass alles gelöscht wird.
PS: Für Konstanten sollte man "constexpr" und kein "#define" nutzen. In
C++ gibt es kaum einen Grund für #define, und #define kann diverse
Probleme machen.
leo schrieb:> Was genau hat eine externe Bibliothek mit den GC-Eigenschaften der> Sprache zu tun?
Ist nur ein Beispiel. Der Standard vermerkt an verschiedenen Stellen
etwas zur Garbage Collection, er ist praktisch aufwärtskompatibel, denn
es ist nicht vollständig spezifiziert. Es wird aber in der Praxis nicht
genutzt. Ist auch egal, denn der GCC für ESP kann das bestimmt nicht.
Dr.Sommer
>Um das jetzt mal etwas abzukürzen, hier ein komplettes Beispiel
Danke für's konkret werden. Das nenne ich vorbildlich.
Hier das ganze getestet für's Arduino Framework des ESP32:
Jetzt bin ich mal gespannt, wie sich das ganze laufzeitmässig mit C
schlägt.
Die Test sollen nämlich im Mikrosekundenbereich und so schnell wie
möglich laufen ( keine prints )
chris schrieb:> //make_unique is an upcoming C++14 feature
"Upcoming"...
chris schrieb:> Jetzt bin ich mal gespannt, wie sich das ganze laufzeitmässig mit C> schlägt.
Vermutlich etwas langsamer.
chris schrieb:> Die Test sollen nämlich im Mikrosekundenbereich und so schnell wie> möglich laufen ( keine prints )
Du bist lustig! new/malloc und delete/free sind natürlich eher langsam.
Für Echtzeit ist das natürlich vollkommen ungeeignet. Was machst du
wenn kein Speicher mehr da ist? Einfach abstürzen lassen?
chris schrieb:> Jetzt bin ich mal gespannt, wie sich das ganze laufzeitmässig mit C> schlägt.
Dazu müsste man mal analysieren, was das Program oben macht....
Ok, gibt 11 Zahlen auf der seriellen Schnittstelle aus. Das bekommt man
bestimmt auch in C "vernünftig" hin.
Dr.Sommer
>Du bist lustig! new/malloc und delete/free sind natürlich eher langsam.>Für Echtzeit ist das natürlich vollkommen ungeeignet.
Es geht natürlich um die noch zu erschaffende "execute" Methode der
Testklassen und nicht die Instantiierung der Objekte.
chris schrieb:> Es geht natürlich um die noch zu erschaffende "execute" Methode der> Testklassen und nicht die Instantiierung der Objekte.
Dann ist die Laufzeit des bisher diskutierten Codes also irrelevant und
somit C vs. C++ auch nicht von Bedeutung?
chris schrieb:> Wie kann ich alle Objekte löschen, damit das Array mit neu erzeugten> Objekten gefüllt werden kann?
Warum müssen die Objekte gelöscht werden?
Man kann auch Objekt-Pooling verwenden...
https://de.wikipedia.org/wiki/Objektpool
merciless
FreeHeap mit Objekten:373504
test1 int:0
test1 int:1
test2 float:2.30
test2 float:3.30
average execution time per object: 0.14 us
FreeHeap Objekte gelöscht:373600
test1 int:42
Falls der ESP 1Befehl/Zyklus erlaubt wären das:
>> FCPU=240e6;>> timePerObject=0.14e-6;>> numberOfCpuCommandsPerObject=timePerObject*FCPU
numberOfCpuCommandsPerObject = 33.600
Und ein Objekt braucht 96Byte/4 = 24Byte Speicher.
chris schrieb:> Das Programm soll folgendes tun:> 1. es werden N-Testobjekte angelegt ( z.B. N=100 )> 2. Die Testobjekte werden ein Zeit lang benutzt und eine Test.execute()> Methode aufgerufen.> 3. Der Speicher, den die Testobjekte belegen, muss wieder frei gegeben> werden.> 4. Goto 1.>> Ich vermute, dass im Framework des ESP32 keine Garbage Collection> implementiert ist. In C wäre das Ganze extrem einfach zu lösen.>> 1. man reserviert Speicher für die Objekte und merkt sich den Speicher> Anfang> 2. man legt Objekte an ( Strukturen )> 3. wie oben ( 1-3)> 4. Man setzt den Objektpointer zurück> 5. Goto 1.>> Aufwand, die Objekte zu löschen: Null
Ich glaube, du hast C/C++ so gar nicht verstanden.
Es gibt in C malloc und free, man braucht nur 1 Aufruf um ein ganzes
Array im noch freien heap Speicher zu reservieren, und wieder
freizugeben.
New und delete von C++ tun dasselbe, so lange in der Klassenstruktur nur
einfache Variablen enthalten sind, keine weiteren Objekte (also structs
mit ihrer eigenem Constructor und Destructor), und können auch ein
ganzes Array auf einen Schlag behandeln.
Im einfachsten Fall macht malloc/new:
MaWin schrieb:> Ich glaube, du hast C/C++ so gar nicht verstanden.>> Es gibt in C malloc und free, man braucht nur 1 Aufruf um ein ganzes> Array im noch freien heap Speicher zu reservieren, und wieder> freizugeben.
Und wie allokierst du ein Array aus lauter unterschiedlich großen
Elementen?
chris schrieb:> Die Testobjekte sind von einem gemeinsamen Objekt abgeleitet, aber ihr> Speicherverbrauch ist unterschiedlich groß.
chris schrieb:> Um mal konkret zu werden ;-)
Konkret wird es erst, wenn Du weist, welches Problem Du eigentlich lösen
möchtest.
> average execution time per object: 0.14 us
Absolute irrelevant, wenn in diesen 140ns kein Problem gelöst wurde.
Dafür ist das noch viel zu viel Zeit.
> Und ein Objekt braucht 96Byte/4 = 24Byte Speicher.
Wer mist, mist Mist. Sowohl `sizeof( Test1 )`, also auch `sizeof( Test2
)` werden wohl 8 sein. Ist aber auch egal, weil Du nicht weißt, was
`ESP.getFreeHeap()` überhaupt für einen Wert zurück gibt.
Wenn das wirklich wichtig wäre, dann verzichtest Du am besten auf die
Verwendung des heaps.
Rolf M. schrieb:> Und wie allokierst du ein Array aus lauter unterschiedlich großen> Elementen?
Der OP hatte "unterschiedlich groß" eigentlich nie erwähnt. Aber: Man
könnte die dynamischen Typen alle hinter einander legen und dazu noch
einen Array mit Zeigern auf den statischen Typen, um die Anfangsadressen
zu bekommen.
Rolf M. schrieb:> Und wie allokierst du ein Array aus lauter unterschiedlich großen> Elementen
Grundlagen der Programmierung: Ein Array besteht aus gleichartigen
Elementen.
Bei unterschiedlich grossen kann man nicht berechnen, dass das dritte
Element bei 2+sizeof(element) steht.
Man braucht Pointer, geht aber auch, gern gemacht um strings in Worte zu
zerlegen oder so:
Rolf M. schrieb:> Torsten R. schrieb:>> Der OP hatte "unterschiedlich groß" eigentlich nie erwähnt.>> Hä? Ich hab doch die Stelle, wo er das erwähnt hat, zitiert.
Sorry, habe ich übersehen!
Hier ein Versuch mit einer Objektliste fester Länge.
Meine Vermutung war, dass die "execute"-Schleife etwas schneller ist und
das die verkehrte Reihenfolge beim Löschen der Objekte zu Problemen
führt.
Aber scheinbar bleibt alles wie beim vorigen Beispiel. Die
Ausführungszeit ist gleich und mit dem "delete" der Objekte scheint es
wohl auch kein Problem zu geben.
1
#include<iostream>
2
#include<memory>
3
#include<vector>
4
5
classTest{
6
public:
7
Test(){}
8
virtual~Test(){}
9
virtualvoidshow()=0;
10
virtualvoidexecute()=0;
11
};
12
13
classTest1:publicTest{
14
public:
15
Test1(inti):id(i){}
16
virtualvoidshow(){
17
Serial.print("test1 int:");
18
Serial.println(id);
19
}
20
virtualvoidexecute(){
21
id++;
22
}
23
private:
24
intid;
25
};
26
27
classTest2:publicTest{
28
public:
29
Test2(floatf):id(f){}
30
virtualvoidshow(){
31
Serial.print("test2 float:");
32
Serial.println(id);
33
}
34
virtualvoidexecute(){
35
id*=3;
36
}
37
private:
38
floatid;
39
};
40
41
42
constexprstd::size_tANZAHL=4;
43
44
45
Test*Tests[ANZAHL];
46
47
voidsetup()
48
{
49
Serial.begin(115200);
50
}
51
52
voidloop()
53
{
54
Serial.print("FreeHeap ohne Objekte:");
55
Serial.println(ESP.getFreeHeap());
56
// Objekte einfügen
57
for(std::size_ti=0;i<ANZAHL/2;++i)
58
Tests[i]=newTest1(i);
59
60
for(std::size_ti=ANZAHL/2;i<ANZAHL;++i)
61
Tests[i]=newTest2(i);
62
63
Serial.print("FreeHeap mit Objekten:");
64
Serial.println(ESP.getFreeHeap());
65
//Ausgeben
66
for(auto&t:Tests)
67
t->show();
68
69
constuint32_tnumListExecutions=100000;
70
uint32_tstartTime_us=micros();
71
for(intn=0;n<numListExecutions;n++)
72
{
73
for(auto&t:Tests)
74
t->execute();
75
}
76
uint32_tstopTime_us=micros();
77
Serial.print("average execution time per object: ");
chris schrieb:> Man kann den Compiler-Explorer verwenden, um den Assemblercode für> verschiedene MCUs zu analysieren:
Mit objdump geht das auch lokal. Schalte mal die Optimierungen ein!
Hier im Netz gibt es einen schönen Artikel zu malloc und
Heap-Fragmentierung:
https://www.mikrocontroller.net/articles/Heap-Fragmentierung
Wird "malloc" bei C++ noch verwendet?
Ich suche nach einer Möglichkeit, in einem Objekt beim Instantiieren
eine variable Speichergröße anzulegen und später das Objekt mitsamt
Speicher wieder zu löschen.
chris schrieb:> Wird "malloc" bei C++ noch verwendet?
Ja, "new" ruft das auf.
chris schrieb:> Ich suche nach einer Möglichkeit, in einem Objekt beim Instantiieren> eine variable Speichergröße anzulegen und später das Objekt mitsamt> Speicher wieder zu löschen.
Also ein vector, genau wie gezeigt...
chris schrieb:> Hier im Netz gibt es einen schönen Artikel zu malloc und> Heap-Fragmentierung:
Das ist halt das Problem bei dynamischer Speicherverwaltung. Zum Glück
braucht man die bei Embedded Systems auch fast nie...
chris schrieb:> Wird "malloc" bei C++ noch verwendet?
Entweder verwendet "new", "malloc" direkt, oder eben etwas, das sehr
ähnlich funktioniert.
> Ich suche nach einer Möglichkeit, in einem Objekt beim Instantiieren> eine variable Speichergröße anzulegen und später das Objekt mitsamt> Speicher wieder zu löschen.
Da Du das auf einer Hardware mit begrenztem Speicher laufen lassen
möchtest, wäre der "übliche" Weg, Dir eine Maximalgröße zu überlegen und
den Speicher genau für dieses Maximalgröße anzulegen.
Dr. Sommer
>Also ein vector, genau wie gezeigt...
Ist der geeignet, um in den verschiedenen Objekten ein "uin16_t" Array
mit 10-4000 Werten anzulegen ( je nach geforderter Größe bei der
Instantiierung )? Wird in einem Vector nicht eine "linked list"
angelegt, die 3x soviel Speicher braucht?
chris schrieb:> Ist der geeignet, um in den verschiedenen Objekten ein "uin16_t" Array> mit 10-4000 Werten anzulegen
Ja natürlich.
chris schrieb:> Wird in einem Vector nicht eine "linked list" angelegt, die 3x soviel> Speicher braucht?
Nein, das macht std::list. vector legt ein Array aus zusammenhängendem
Speicher an.
Autor: mh (Gast)
>Wie kommt man auf so eine Idee?
Von hier:
https://de.cppreference.com/w/cpp/container/vector
"Der Speicherplatz des Vektors wird automatisch angepasst, er wird je
nach Bedarf erweitert und verkleinert. Vektoren belegen in der Regel
mehr Platz als statische Arrays, weil mehr Speicher zugewiesen wird um
zukünftiges Wachstum zu behandeln."
Zugegeben, das mit der "linked list" war eine Vermutung. Aber zumindest
hat "vector" einen Overhead. Die Frage ist, wie groß?
chris schrieb:> Aber zumindest hat "vector" einen Overhead. Die Frage ist, wie groß?
Ca 16 Bytes (einmalig). Siehe auch die Funktion std::vector::reserve...
chris schrieb:> "Der Speicherplatz des Vektors wird automatisch angepasst, er wird je> nach Bedarf erweitert und verkleinert. Vektoren belegen in der Regel> mehr Platz als statische Arrays, weil mehr Speicher zugewiesen wird um> zukünftiges Wachstum zu behandeln.">> Zugegeben, das mit der "linked list" war eine Vermutung. Aber zumindest> hat "vector" einen Overhead. Die Frage ist, wie groß?
Das kommt drauf an. Wenn der Platz, den ein vector bietet, aufgebraucht
ist, muss er reallokieren, d.h. einen neuen, größeren Speicherplatz
belegen, alle bereits enthaltenen Daten rüberkopieren und dann den
ursprünglichen Speicherbereich freigeben. Da man vermeiden möchte, dass
das bei jedem einzelnen Hinzufügen eines Elements passiert, wird bei
jeder Reallokation der Bereich üblicherweise nicht nur um ein einzelnes
Element, sondern um einen bestimmten Faktor gegenüber dem vorherigen
vergrößert, so dass noch Platz für weitere Elemente ist, bevor wieder
neu reallokiert werden muss. Wie groß dieser Faktor ist, hängt von der
Implementation ab. Manche verdoppeln z.B. immer. Während der
Reallokation braucht der vector kurzzeitig dann also den dreifachen
Platz (alter Speicherbereich + neuer, doppelt so großer Bereich).