Hallo Zusammen 64bit Pointer sind gross, wenn man Datenstrukturen mit sehr vielen davon hat. Nun hab ich eine Serveranwendung mit mehreren Clients, und zu diesen gehöre diverse Datenstrukturen (unter anderem auch Baum und Graph Strukturen), mit vielen Pointern. Nun will ich einerseits die Speichermenge, die ein Client nutzen kann, limitieren, und andererseits bei den Pointern Speicherplatz sparen. (2^32 pro Client ist schon mehr als genug). Konkret will ich für jeden Client einen grossen Speicherbereich reservieren (das kann ich mit mmap PROT_NONE machen). In den Objekten verwende ich dann einen uint32_t statt Pointer. Das ist dann der Offset in den Speicherbereich, den ich für den Client reserviert habe. Hat auch den Vorteil, dass ich das alles in einem Rutsch wieder freigeben kann, wenn der Client abrauscht. Nun bräuchte ich eine Speicherverwaltung für diese Speicherbereiche. Quasi ein Malloc & free, wo man den Addressbereich angeben kann, und sich dann um den rest kümmert. Ich weiss es gibt so Sachen wie z.B. jemalloc, aber die sind oft recht komplex mit mehreren Arenas usw., und sind vermutlich nicht genau dafür gedacht. Gibt es da schon was fertiges, oder etwas was sich dafür einfach übernehmen lässt? Hat das eventuell schon mal jemand gemacht?
Klingt ... wirr. Lass' doch den clientspezifischen Teil als 32-Bit-Code laufen, da hast Du automatisch Deine Beschränkungen mit drin. Ansonsten ist der Code von malloc & Co. bei jedem anständigen Compiler im Quelltext vorhanden, daraus kannst Du Dir Dein client_castratet_malloc und korrespondierendes client_castrated_free basteln.
Harald K. schrieb: > Klingt ... wirr. Lass' doch den clientspezifischen Teil als 32-Bit-Code > laufen, da hast Du automatisch Deine Beschränkungen mit drin. Die Objekte sind auf der Server Seite, und beschreiben den Zustand des Clients, der diesen indirekt manipulieren kann. Was der Client Clientseitig noch so treibt, ist mir recht egal. Harald K. schrieb: > Ansonsten ist der Code von malloc & Co. bei jedem anständigen Compiler > im Quelltext vorhanden, daraus kannst Du Dir Dein > client_castratet_malloc und korrespondierendes client_castrated_free > basteln. Die die ich bisher angeschaut habe kann ich eben nicht einfach 1:1 übernehmen. Einerseits haben die oft globale Zustände, und andererseits wird teils dem Kernel beim mmap überlassen, wo die Page hin kommt (was ich ja manuell machen müsste, womit ich die Speicherverwaltung wieder selbst machen müsste), oder haben mehrere Arenas, was wieder nicht mit dem einen Reservierten Bereich den ich brauche zusammen passt. Ich hab im Anhang mal etwas Beispiel Code hochgeladen, damit man eine bessere Idee bekommt, was ich machen will. Aber vielleicht war die Idee wirklich nicht so gut. Ich glaube ich wechsle einfach zu einem dynamischen Array pro Client und Objekt Typ, und nehme 32bit indeces mit einem Tag für den Typ. Und eine free list dazu. Ist sowieso sicherer. Anfangs dachte ich vielleicht mache ich auch noch etwas shared Memory zwischen Clients und Server, wenn ich eh schon alle Pointer indirekt als indeces habe, aber das wird zu komplex mit der Validierung der Daten auf der Server Seite usw. (Da bräuchte ich mehr Speicher dafür, als ich durch das Memory Sharing einsparen würde...).
:
Bearbeitet durch User
Das sind die Dinge, wo man 5 Jahre später sagt: "Hätte ich diesen Sch..." nur nicht gemacht". Kling für mich alles wirr und krank. Damit erzeugt man nur noch ein weiteres Level an Komplexität, was Entwickeln, Kodieren, Debuggen noch eine Stufe komplizierter macht. Starte einen eigenen Prozess für den Client, und wenn der abraucht, bereinigt das OS den Speicher.
Was ich da raus lese: Du hast den Server zu klein dimensioniert, beziehungsweise die Datenstrukturen falsch gewählt. Zu naiv alles genommen was so in den Lehrbüchern steht und wunderst dich jetzt, dass das Platz braucht? Weiterhin hast du dich für einen einzigen fetten Prozess entschieden, der alle Clients bedient und es dir daher erschwert ein vernünftiges Lifecycle-Management pro Client zu machen. Allerdings scheinen die Daten gar nicht wichtig zu sein, weil alles in einem Rutsch freigegeben werden soll wenn der Client unvorhergesehen terminiert. Arme Client-User. Statt die Probleme an der Wurzel anzugehen willst du eine eigene Speicherverwaltung programmieren, schaffst es aber nicht einen beliebigen malloc/free Sourcecode für deine vorgeblichen Bedürfnisse umzuschreiben. Also, mach dir ein Heißgetränk deiner Wahl, setz dich hin und denke über dein Leben und die gesamte Architektur nach. Dann korrigierst du überall dort wo du falsch abgebogen bist, statt den geplanten üblen Hack oben drauf zu setzen, den du sowieso nicht beherrschst.
:
Bearbeitet durch User
Peter schrieb: > Das sind die Dinge, wo man 5 Jahre später sagt: "Hätte ich diesen > Sch..." nur nicht gemacht". Das denke ich auch. Große Datenmengen sind meistens auch Performance-kritisch. Auf einem 64 Bit Rechner mit 64 Bit Betriebssystem werden 32 Bit Indices langsamer verarbeitet, als 64 Bit. Dazu kommt das unvorteilhafte Aligning durch deine Platz-Sparsamkeit. Stecke besser mehr RAM in den Rechner, falls nötig. Das ist so oft günstiger, dass man schon die Evaluierung deiner Idee als Zeitverschwendung ablehnen kann.
:
Bearbeitet durch User
Moin, Ich bin zu doof, die Problematik zu verstehen, werfe aber mal den Bootloader u-boot bzw. seine sourcen ins Rennen. iirc ist da eine simple malloc() Implementierung eingebaut. Gruss WK
Dergute W. schrieb: > Moin, > > Ich bin zu doof, die Problematik zu verstehen, werfe aber mal den > Bootloader u-boot bzw. seine sourcen ins Rennen. iirc ist da eine simple > malloc() Implementierung eingebaut. > > Gruss > WK Naja wer es ganz einfach haben will: schon im ersten K&R von 1978 war schon eine Beispielimplementierung drin. Und das Buch sollte man doch mal gelesen haben.
Hannes J. schrieb: > Weiterhin hast du dich für einen einzigen fetten Prozess entschieden, > der alle Clients bedient und es dir daher erschwert ein vernünftiges > Lifecycle-Management pro Client zu machen. Allerdings scheinen die Daten > gar nicht wichtig zu sein, weil alles in einem Rutsch freigegeben werden > soll wenn der Client unvorhergesehen terminiert. Arme Client-User. Es soll ein Display Server werden. Die Clients synchronisieren ihren UI Trees mit dem Server, der Server rendert den dann. Ein Client hat keinen Zugriff auf den UI Tree anderer Clients, aber es wird ein feature ähnlich dem HTML Shadow DOM und XEmbed geben. z.B. Der Window Manager Client stellt den Fenster UI Tree zur Verfügung. Die anderen Clients können den nicht ansehen oder modifizieren, aber sie können sagen, das ist ein Window / Fenster, und folgende Elemente müssen da rein. Die Clients können also nur ihre eigenen UI Trees modifizieren, aber der Server muss sie alle rendern. Würde ich da mehrere Prozesse nehmen, würde das sicher umständlich und langsam zwischen denen zu wechseln & die zu synchronisieren. Und ja, die Daten auf dem Server sind grösstenteils "unwichtig", nachdem der Client weg ist. Der Client hat die ja noch, wenn er sich wieder verbindet kann er den UI Tree einfach nochmal senden, und alles ist wieder da. Hannes J. schrieb: > Du hast den Server zu klein dimensioniert, beziehungsweise die > Datenstrukturen falsch gewählt. Zu naiv alles genommen was so in den > Lehrbüchern steht und wunderst dich jetzt, dass das Platz braucht? Ich bin noch in der Entwurfsphase. Das ist momentan alles eher noch Brainstorming, ich werfe das Konzept regelmässig wieder über den Haufen und fange wieder von neu an. Ich habe quasi 2 Probleme, um die ich mir grade Gedanken mache. Der UI Tree ist auf den Clients, und eine Kopie auf den Servern, und ich gehe davon aus, dass er ziemlich gross werden könnte. Darum überleg ich mir, wie ich die Datenstruktur selbst klein halten könnte. Der UI Tree ist eigentlich nur Pointer zu anderen Nodes. Hab ich also 2 UI Trees, aber die Pointer sind nur halb so gross, bin ich wieder bei der Anfangsgrösse. Das zweite Problem ist die Synchronisierung. Jede Node muss durch den Client identifizierbar sein, damit er die ändern kann. Wenn ich die Position im Speicher / in einem Array, als ID nehme, dann darf ich die nicht mehr verändern. Ich könnte zwar auch einfach ein Map von ID -> Node machen, dann könnte ich die Nodes verschieben, und Child Nodes in Arrays statt linked lists speichern. Dann fielen die next, prev pointer weg, und Cache Locality wäre besser. Aber dann müsste ich das Map up-to-date halten usw., das bringt auch overhead. Wobei, wenn ich so darüber nachdenke, eigentlich braucht der Server gar nie Rückwerts durch den UI Tree zu gehen, das braucht voraussichtlich nur der Client. Wenn ich da 2 unterschiedliche Datenstrukturen nehme, könnte ich 2 Pointer pro Node einsparen... Hannes J. schrieb: > Also, mach dir ein Heißgetränk deiner Wahl, setz dich hin und denke über > dein Leben und die gesamte Architektur nach. An dem Schritt bin ich ja gerade dran. Nachdenken & ausprobieren. Viele Probleme sieht man erst, wenn man es probiert hat. Sherlock 🕵🏽♂️ schrieb: > Große Datenmengen sind meistens auch Performance-kritisch. Auf einem 64 > Bit Rechner mit 64 Bit Betriebssystem werden 32 Bit Indices langsamer > verarbeitet, als 64 Bit. Ich hatte mal ein eigenes HashMap / HashSet geschrieben. Die gehashten Keys waren quasi Indeces in ein Array, in die nähe des gesuchten Eintrags. (Ich nutzte Robin-hood hashing). Es gab Varianten mit 8, 16, 32, 64, 128bit keys. Und die mit den 16bit keys war am schnellsten: Beitrag "Re: Diverse fragen zu Hash Maps in C" Bei Performance weiss man am Ende erst, was dabei rauskommt, wenn man nach misst. Alles was über O-Natation hinaus geht ist da sowieso nur raten und hoffen. Das kommt nie so raus, wie man erwartet. > Dazu kommt das unvorteilhafte Aligning durch deine Platz-Sparsamkeit. Das Alignment ist kein Problem, das past schon. Ich mache hier keine Hacks mit __attribute__((packed)) oder so. PS: Warum wird mir hier eigentlich immer gleich gesagt, der Ansatz muss Mist sein, wenn ihr doch noch gar nicht wisst wo ∕ wie ∕ wofür das später verwendet wird? Ich hab mir da schon etwas dabei überlegt, und probiere halt auch manchmal was neues aus.
:
Bearbeitet durch User
Daniel A. schrieb: > Warum wird mir hier eigentlich immer gleich gesagt, der Ansatz muss Mist > sein, Das ist ein Bauchgefühl aus Erfahrung. Solche Kunstgriffe haben in gepflegter Software aus unterschiedlichen Gründen selten lange Bestand. Und wenn doch, dann enden sie nicht selten in einer "do not touch" Tabuzone, um die man dann jahrelang herum frickelt, weil niemand durchblickt, was sich der ursprüngliche Autor dabei gedacht hat. Falls du alleine an dem Projekt arbeitest und es nach dir eh in die Tonne kommt, kann dir das egal sein. Ausptobieren und Messen/Vergleichen ist auf jeden Fall besser, als es nur im Kopf zu durchdenken und dann umzusetzten.
:
Bearbeitet durch User
Daniel A. schrieb: > PS: Warum wird mir hier eigentlich immer gleich gesagt, der Ansatz muss > Mist sein, wenn ihr doch noch gar nicht wisst wo ∕ wie ∕ wofür das > später verwendet wird? Und ich frage mich immer, warum Leute hier her kommen und nach unserer Meinung und Erfahrung fragen um die Antworten dann bockig zu ignorieren weil sie nicht in ihr Weltbild passen. > Ich hab mir da schon etwas dabei überlegt, Nur ging das unserer Meinung nach in die Hose. > und > probiere halt auch manchmal was neues aus. Ja, dann mach doch. Wozu brauchst du unsere Bestätigung? Wenn du wirklich so schlau bist müsstest du uns nicht fragen und hättest dir dein malloc/free schon lange gebastelt.
Ein einfaches malloc/free mit free-list kann jeder schreiben. Aber ein gutes malloc/free ist weniger einfach. Darum hab ich gefragt, ob es schon was fertiges gibt, dass sich hierfür, für diesen speziellen Fall, einsetzen lässt. Dergute W. schrieb: > Ich bin zu doof, die Problematik zu verstehen, werfe aber mal den > Bootloader u-boot bzw. seine sourcen ins Rennen. iirc ist da eine simple > malloc() Implementierung eingebaut. Uboot scheint eine modifizierte Version von dlmalloc zu verwenden. dlmalloc sieht recht vielversprechend aus, das ist noch relativ simpel im vergleich zu anderen Implementationen. Aber auch nicht ganz ohne, das für den use-case anzupassen. Der Beitrag war hilfreich.
:
Bearbeitet durch User
Daniel A. schrieb: > Die die ich bisher angeschaut habe kann ich eben nicht einfach 1:1 > übernehmen. Einerseits haben die oft globale Zustände, und andererseits > wird teils dem Kernel beim mmap überlassen, wo die Page hin kommt Dann siehst Du Dir was falsches an. Es geht um den simplen Code der Standard-Library, nicht um das, was dann sbrk übernimmt.
Unter Windows war es schon immer üblich, in Programmen mit mehreren Fenstern separate Heaps für die User Daten zu verwenden. Da wird das aber gut unterstützt mit CreateHeap() und HeapAlloc(). Die GUI und das System verwendet den Prozess-Heap, da ist die Grösse und die Fragmentierung beschränkt. User Daten können aber in manchen Fällen sehr gross werden, und Fragmentierung kann ein Problem sein. Wenn das Programm wegen Out-Of-Memory abschmiert, oder wegen einem Zugriffsfehler, wird das in einem Exception Handler abgefangen, und das Programm kann unter Umständen mit den Daten in den restlichen Fenstern weitermachen. Da die GUI einen separaten Heap hat, sind auch noch Fehlermeldungen möglich. Altium und viele CAD Programme machen das so. Was ich weiss verwendest du Linux, da kannst du smalloc ausprobieren: https://github.com/strlcat/smalloc Oder alternativ das ausgereiftere mimalloc: https://github.com/microsoft/mimalloc Gruss Udo
:
Bearbeitet durch User
Danke. Ich glaube, das kann so ziemlich genau das, was ich gesucht habe. Ich muss mir die `mi_heap_*` Funktionen noch genauer ansehen, aber ich denke damit komm ich weiter!
Sherlock 🕵🏽♂️ schrieb: > Große Datenmengen sind meistens auch Performance-kritisch. Auf einem 64 > Bit Rechner mit 64 Bit Betriebssystem werden 32 Bit Indices langsamer > verarbeitet, als 64 Bit. Dazu kommt das unvorteilhafte Aligning durch > deine Platz-Sparsamkeit. Nein, da ist kein Unterschied (amd64). Wenn du viele Daten und Zeiger herumschaufelst, dann ist der 32 Bit Code 2x schneller, da das Speicherinterface die Geschwindigkeit bestimmt.
Hannes J. schrieb: > Daniel A. schrieb: >> und >> probiere halt auch manchmal was neues aus. > > Ja, dann mach doch. Wozu brauchst du unsere Bestätigung? Wenn du > wirklich so schlau bist müsstest du uns nicht fragen und hättest dir > dein malloc/free schon lange gebastelt. Wer hier im Forum schon eine Weile dabei ist, kennt diese Threads von Daniel. Der programmiert auf einem anderen Level in C (und das ist völlig unironisch gemeint), was dazu führt, daß dessen Fragen und die Antworten dazu immer so ähnlich wie hier ablaufen. Ob man so programmieren muß, wie er das tut, ist allerdings eine durchaus diskutierbare Frage. Da hier im Thread aber gar kein Code diskutiert wird, spielt das jetzt keine Rolle. Oliver
Daniel A. schrieb: > Ein einfaches malloc/free mit free-list kann jeder schreiben. Aber ein > gutes malloc/free ist weniger einfach. Darum hab ich gefragt, ob es > schon was fertiges gibt, dass sich hierfür, für diesen speziellen Fall, > einsetzen lässt. Alternative zu malloc/free wäre alloc+garbage collect. Hat den Vorteil, dass es keine Memory Leaks gibt, weil man ja eh nix explizit freigeben muss. Allerdings muss man die Wurzeln der allokierten Objekte kennen, um sie im Marking / Collecting markieren und freigeben zu können.
Daniel A. schrieb: > Jede Node muss durch den > Client identifizierbar sein, damit er die ändern kann. Oder anders gesagt: Jede Node bringt schon bei der ersten Sync mit dem Server eine für diesen Client eindeutige ID mit. Dann könntest Du diese ID direkt als Index für ein Pointer-Array nehmen, in dem die aktuelle Adresse dieses Objekts hinterlegt ist; so ein Mapping brauchst Du sowieso, wenn der Client einzelne Nodes ändern können soll, weil der Server das Objekt dann anhand der ID einfach/schnell finden muß. Außerdem könnte der Client, zu Beginn jedes (größeren, also nicht nur eine Node modifizierenden) Syncs, dem Server die Größe des zusätzlich benötigten Speichers bzw. die Anzahl und Typen der neuen Objekte (wenn man die serverseitige Speicherung von der clientseitigen abstrahieren will) mitteilen, sodaß der Server den nötig Speicher gleich en-bloc reservieren kann. Daniel A. schrieb: > Der UI Tree ist eigentlich nur Pointer zu anderen Nodes. Die einzelnen UI-Tree-Nodes müssen doch neben Child-Pointern (bzw. Child-IDs, denn das ist der gemeinsame Nenner um ein Objekt am Server und am Client identifizieren zu können) auch Attribute (Typ, Ecke rel. zum Elter, Größe, Farbe etc.) zu dem UI-Element beinhalten, sonst kanns Dein Server ja nicht rendern. Keine Ahnung wie kleinteilig Du deine UI-Trees aufziehen willst, aber eigentlich sollten doch 65.000 Nodes pro Client reichen, oder? Dann kommst bei der ID sogar bis auf 2 Byte runter, das würde sich dann richtig auszahlen.
Hannes J. schrieb: > Und ich frage mich immer, warum Leute hier her kommen und nach unserer > Meinung und Erfahrung fragen um die Antworten dann bockig zu ignorieren Das liegt an Leuten wie dir, die ein Bedürfnis haben, ihre Antworten passiv aggressiv zu gestalten und wo es geht Polemik rein zu mischen - auch wenn der Inhalt fachlich wahrscheinlich richtig ist. Ich habe deinen Beitrag >Hannes J. schrieb: >> Du hast den Server zu klein dimensioniert, beziehungsweise die >> Datenstrukturen falsch gewählt. Zu naiv alles genommen was so in den >> Lehrbüchern steht und wunderst dich jetzt, dass das Platz braucht? mal in ChatGPT gegeben mit der Bitte so um zu formulieren, dass es nicht mehr polemisch klingt. - Was 'ich' daraus lese ist, dass du eigentlich einen Raum suchst, um angestauten Frust los zu werden. Wenn helfen deine primäre Intention wäre, würdest du von vornherein anders formulieren. Wenn es dich so sehr aufregt, hast du die Freiheit gar nicht zu antworten - das schaffst du aber aus irgendeinem Grund nicht.
1 | Es scheint, dass der Server möglicherweise nicht ausreichend dimensioniert wurde oder die gewählten Datenstrukturen nicht optimal auf die Anforderungen abgestimmt sind. Die verwendeten Ansätze scheinen stark an Lehrbuchbeispielen orientiert zu sein, was verständlich ist, aber nicht immer die effizienteste Lösung darstellt. Dies könnte die Ursache für den erhöhten Platzbedarf sein. |
2 | |
3 | Zusätzlich wurde offenbar ein einzelner Prozess gewählt, um alle Clients zu bedienen. Dieser Ansatz erschwert ein flexibles Lifecycle-Management für einzelne Clients. Es wäre hilfreich zu prüfen, ob eine granularere Steuerung sinnvoller wäre. Auch die aktuelle Strategie zur Datenfreigabe, bei der alle Daten sofort verworfen werden, wenn ein Client unerwartet terminiert, könnte überdacht werden, um die Nutzererfahrung zu verbessern. |
4 | |
5 | Statt die grundlegenden Herausforderungen anzugehen, wird aktuell in Betracht gezogen, eine eigene Speicherverwaltung zu implementieren. Dies scheint jedoch mit Schwierigkeiten verbunden zu sein, da bestehender malloc/free-Code offenbar nicht erfolgreich an die spezifischen Anforderungen angepasst wurde. |
6 | |
7 | Ich empfehle, sich in Ruhe mit der Gesamtarchitektur auseinanderzusetzen und die grundlegenden Entscheidungen zu überdenken. Es könnte sinnvoll sein, bestehende Problemstellen systematisch zu identifizieren und zu korrigieren, anstatt eine schnelle, möglicherweise fehleranfällige Lösung zu implementieren. |
:
Bearbeitet durch User
Daniel A. schrieb: > Gibt es da schon was fertiges, oder etwas was sich dafür einfach > übernehmen lässt? Hat das eventuell schon mal jemand gemacht? Mache es in Assembler - oder auch in Haskell/Rust. Ist ernst gemeint, da kann man viel neues dazu lernen. https://medium.com/@wweziza/building-an-http-server-in-x86-assembly-a-deep-dive-into-low-level-web-development-c79b157c8070 Ansonsten helfen auch Malloc-Optimier-Seiten, z.B. Static mit kleineren Werten hinzubekommen.
Daniel, hast du darüber nachgedacht, ob du für deinen speziellen Heap eine Defragmentierung brauchst?
Sherlock 🕵🏽♂️ schrieb: > Daniel, hast du darüber nachgedacht, ob du für deinen speziellen Heap > eine Defragmentierung brauchst? Da kann ich mir später drum Gedanken machen. Sollte aber kein grosses Problem darstellen.
Daniel A. schrieb: >> Daniel, hast du darüber nachgedacht, ob du für deinen speziellen Heap >> eine Defragmentierung brauchst? > Da kann ich mir später drum Gedanken machen. Sollte aber kein grosses > Problem darstellen. Na hoffentlich hast du Recht. Ich habe anderes erlebt.
Sherlock 🕵🏽♂️ schrieb: > Daniel, hast du darüber nachgedacht, ob du für deinen speziellen Heap > eine Defragmentierung brauchst? Zuforderst sollte man darüber nachdenken, ob man diesen speziellen Heap überhaupt braucht.
Beitrag #7790106 wurde vom Autor gelöscht.
Allenfalls haben in einem Serverumfeld alle Datenstrukturen eine Lebensdauer, und daher koennte auch ein Ringspeicher, als Queue (Schlange) passen. Das hat den Vorteil, dass der Speicher nicht fragmentiert, und man muesste sich nur Indices merken.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.