Ringpuffer vs Linkedlist

OP #642409
Lesenswert?

Hallo,
ich habe daten, die manchmal per irq schlagartig anfallen, und 
langfristig über uart weggeschickt werden müssen. da die uart verbindung 
ggf. unterbrochen sein kann, hab ich bisher eine linked-list 
programmiert, die dynamisch wachsen kann (vom irq handler gefüllt), und 
von oben abgearbeitet wird, sobald zeit dazu ist.

aufbau düfte klar sein:
eine struktur, die ein datenfeld und einen pointer 'next' auf die 
nächste instanz der struktur enthält, ein pointer der struktur als 
'nexttoprocess' und eine 'appendhere' die auf die letzte instanz zeigt.
beim anfügen wird ein addhere.next alloziiert,
beim abarbeiten wird nexttoprocess auf den nächsten gesetzt, der erste 
freigegeben usw.

hat den vorteil, daß es nicht wie beim ringpuffer zum überlauf kommen 
kann (solange der sram noch was hergibt).
nachteil: die daten liegen ggf. im speicher verstreut und man hat nen 
gewissen overhead.

und genau da bin ich mir nicht sicher:
ich habe
typedef struct ll_type {
  uint8_t *dat;   //allocatable int array, hold the data
  struct ll_type *next;
}ll_type;

also ein dynamisches dat array, so daß ich mal 8 und mal n*8 bit daten 
speichern kann.
wie groß ist denn dann der overhead? (sizeof(ll_type)) gibt 8bit
,d.h. ich benötige 8bit zum zeigen auf den nächsten container,+die größe 
für die im datarray gespeicherten daten?
-> für 8bit daten also overhead:daten = 1:1 (wenn man den nexttoprocess 
und addhere vernachlässigt)?


am ringpuffer gefällt mir nicht, daß derspeicher ja permanet für andere 
'tasks' unbrauchbar ist. gibts noch ne andere möglichkeit?

alex
Gast #642416
Lesenswert?

>am ringpuffer gefällt mir nicht, daß derspeicher ja permanet für andere
>'tasks' unbrauchbar ist.

Das stimmt, aber er benötigt pro Byte wesentlich weniger Speicher als 
deine
LinkedList. Und wenn du sehr viele Daten puffern musst zerstört dir
deine Liste wesentlich früher andere Speicherbereiche wenn die Liste
z.B. in den Stack wandert. Beim Ringpuffer kannst du die Notbremse
ziehen, weil du weisst wie groß er ist ! Und es ist ein Trugschluss das
deine LinkedList nicht auch permanent Speicher belegt. Du musst immer
so viel Speicher freilassen das deine Liste da auch reinpasst !
#642419
Lesenswert?

holger wrote:
>>am ringpuffer gefällt mir nicht, daß derspeicher ja permanet für andere
>>'tasks' unbrauchbar ist.
>
> Das stimmt, aber er benötigt pro Byte wesentlich weniger Speicher als
> deine
> LinkedList. Und wenn du sehr viele Daten puffern musst zerstört dir
> deine Liste wesentlich früher andere Speicherbereiche wenn die Liste
> z.B. in den Stack wandert. Beim Ringpuffer kannst du die Notbremse
> ziehen, weil du weisst wie groß er ist ! Und es ist ein Trugschluss das
> deine LinkedList nicht auch permanent Speicher belegt. Du musst immer
> so viel Speicher freilassen das deine Liste da auch reinpasst !

Nicht bei dynamischer Speicherverwaltung. Für Daten im Byte-Bereich 
halte ich einen Ringpuffer allerdings auch für wesentlich sinnvoller 
als eine verknüpfte Liste. Kann es denn vorkommen, dass du die Daten die 
reinkommen überhaupt nicht mehr rauskriegst? Dann würde ich mal das 
Konzept des ganzen Aufbaus überdenken...
#642427
Lesenswert?

Vermutlich meinst Bytes, statt Bit...

Die ökonomischste Lösung ist der Ringpuffer: Er nutzt den Speicher am 
besten und der Code, der ihn verwaltet, ist wesentlich einfacher und 
kleiner, als eine ausgewachsenen Heapverwaltung - die braucht nämlich 
neben dem nicht eben kleinen Code selbst nochmal für jedes allokierte 
Objekt einen ähnlichen Overhead, wie deine verzeigerte Liste.

Falls es sich bei deiner Hardware um einen µC mit nicht ausgesprochen 
üppigem RAM handelt, würde ich generell von verzeigerten Listen abraten. 
Nicht nur wegen des Overheads, sondern vor allem auch wegen der damit 
sprunghaft ansteigenden Anzahl möglicher Speicherlayout-Konstellationen 
und der der Probleme durch Speicherverschnitt und scheiternden 
Speicherallokationen.
OP #642481
Lesenswert?

also der µC ist ein ARM SAM7X256, sram könnte man als komfortabel 
beschreiben...

>Vermutlich meinst Bytes, statt Bit...

hm? wieso? eine uint8_t sind doch 8bit oder? 
size(uint8_t)=8=size(ll_type), oder seh ich's grad net?

>Du musst immer
>so viel Speicher freilassen das deine Liste da auch reinpasst !

na ja, ich hab 3 stellen, an denen daten schubweise ankommen, aber 
eigentlich nicht an allen 3 gleichzeitig -> der speicher ist dann für 
alle nutzbar.
beim ringpuffer muß ich entweder 3 stück mit der jeweils maximal 
auftretenden größe einrichten, oder wieder nen handler schreiben, der 
die datenblöcke 'gemischt' speichern kann...

>Kann es denn vorkommen, dass du die Daten die
>reinkommen überhaupt nicht mehr rauskriegst?

eigentlich nicht. ich hab z.B. eine Funkstrecke, die ggf. mal gestört 
sein kann etc.

>sondern vor allem auch wegen der damit
>sprunghaft ansteigenden Anzahl möglicher Speicherlayout-Konstellationen

wo ist das ein problem? ggf für den optimizer? klar, der speicher wird 
fragmentiert, da sich die linked-liste halt durch den freien speicher 
bewegt...
ah, ein malloc für ein array könnte scheitern, wenn derenstprechende 
speicherbereich nicht kontinuierlich zu haben ist - richtig?
(ich programmier sonst eher code für simulationscluster, da ist speicher 
kein thema ;-))

nochmal zum overhead:
so groß ist der ja nicht (oder verrechne ich mich):
wir haben n datensätze -> (n-1)*8bit für die Zeiger zwischen den 
datensätzen, 2*8bit für den zeiger auf den ersten und letzen satz.
->(n+1)*8bit

wenn ich z.B. 10 uint32_t sätze speicher, brauch ich im ringpuffer 
(10+2)*32bit=384bit, in derlinkedliste 11*8bit+10*32bit=408bit

oder überseh ich was?
#642487
Lesenswert?

Du willst ja die Daten schnell einfüllen können, in einem IRQ.
Bei der Linked list hast du dann einen malloc-Aufruf in deiner ISR, mit 
ziemlich unvorhersehbarer Laufzeit. Mal abgesehen davon, dass du dann 
alle weiteren mallocs/frees im Hauptprogram mit abgeschalteten IRQs 
ausführen musst, weils sonst deinen Heap zerschiesst.
#642491
Lesenswert?

> hm? wieso? eine uint8_t sind doch 8bit oder?
> size(uint8_t)=8=size(ll_type), oder seh ich's grad net?

Sizeof liefert die Länge in BYTE zurück.

>> sondern vor allem auch wegen der damit
>> sprunghaft ansteigenden Anzahl möglicher Speicherlayout-Konstellationen
>
> wo ist das ein problem? ggf für den optimizer? klar, der speicher wird
> fragmentiert, da sich die linked-liste halt durch den freien speicher
> bewegt...

Der Heap verleitet dazu, von einer näherungsweise unendlichen Resource 
auszugehen und Situationen mit Speichermangel garnicht in Betracht zu 
ziehen. Das ist eine Fiktion, die schon so mancher 'gut getesteten' 
Applikation den Ruf gekostet hat...

Das ist keine Frage des Optimizers, sonder des Software-Testers. Der 
Heap produziert sehr schnell so viele mögliche Speicherlayouts, daß man 
die nicht im entfernten auch nur einigermaßen testen kann. Ein gut 
verborgener Adressierfehler schlägt dann eben nur alle 5 Stunden mal zu, 
statt alle 5 Sekunden bei einem statischen Design - viel Spaß beim 
Suchen...

Dazu kommt, daß die kritischen Layouts meistens nicht erkannt und schon 
garnicht provoziert werden können, weil der Heap-Aufbau nicht ohne 
weiteres nachvollzogen werden kann.

> oder überseh ich was?

Ja. Siehe oben.

@Ernst Bachmann:
> Du willst ja die Daten schnell einfüllen können, in einem IRQ.
> Bei der Linked list hast du dann einen malloc-Aufruf in deiner ISR, mit
> ziemlich unvorhersehbarer Laufzeit. Mal abgesehen davon, dass du dann
> alle weiteren mallocs/frees im Hauptprogram mit abgeschalteten IRQs
> ausführen musst, weils sonst deinen Heap zerschiesst.

Sehr gut erkannt, das hatte ich noch garnicht in Betracht gezogen.

Ob die Heapverwaltung auf dem ARM threadsafe ist, weiß ich nicht, aber 
vermutlich eher nicht.
Gast #642507
Lesenswert?

>size(uint8_t)=8=size(ll_type), oder seh ich's grad net?

sizeof(uint8_t)=8
 das ist korrekt


>typedef struct ll_type {
>  uint8_t *dat;   //allocatable int array, hold the data
>  struct ll_type *next;
>}ll_type;


sizeof(uint8_t *) ist was anderes als 8 ;)
#642510
Lesenswert?

holger wrote:
>>size(uint8_t)=8=size(ll_type), oder seh ich's grad net?
>
> sizeof(uint8_t)=8
>  das ist korrekt

Hast du das ausprobiert?

uint8_t sieht mir eher wie 8 Bit aus, also müßte sizeof (uint8_t) == 1 
sein.

Nachtrag:

   #define   INT8_MAX   0x7f
   #define   UINT8_MAX   (__CONCAT(INT8_MAX, U) * 2U + 1U)

Also ist es 1 Byte.
Gast #643706
Lesenswert?

hallo,

entschuldigt, hab's grad nochmal ausprobiert
sizeof(uint8_t) ist 1 :schäm:
sizeof(uint8_t *) ist 8

ich hätte eigentlich erwartet, daß der zeiger auf einem 32bit system 
32bit braucht. 8bytes ist ja irre viel...

hmm, dann sieht meine overheadbetrachtung ganz anders aus :-(


> ziemlich unvorhersehbarer Laufzeit. Mal abgesehen davon, dass du dann
> alle weiteren mallocs/frees im Hauptprogram mit abgeschalteten IRQs
> ausführen musst, weils sonst deinen Heap zerschiesst.

interessanter punkt. ich hab zwar ein handling, damit sichergestellt 
ist, daß die linked-liste nicht gelichzeitig von 2 'tasks' bearbeitet 
wird, bisher die linkedliste aber noch nicht in einer irq routine 
verwendet (sondern dort daten in eine variable geschrieben, die ich 
mittels noicht-irq routine dann in die linkedliste fülle).

hab mich überzeugt:
ich werd wohn 'n ringpuffer verwenden. muß mal den naßcomputer arbeiten 
lassen, vielleicht fällt mir was gescheites ein um eine ringpuffer für 
verschiedene bereiche zu verwenden....


gruß
alex

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren