Hallo,
ich möchte in der nächsten Zeit mal meine Codes zum Speichern und Abrufen von persistenten Daten überarbeiten. Aktuell speichere ich in CSVs Datenpunkte(Klimadaten) in ca 3 bis 6 Spalten getrennt durch Semikolon. Jede Zeile ist ein Datenpunkt. Die maximale Größe der CSVs gehen so bis max 10GB. Der normale Vorgang ist die Daten in den RAM zu laden und dann dort solange wie benötigt zu halten da mehrere Workerthreads parallel und wiederholend diese Daten nutzen.
Zwei Dinge möchte ich verbessern:
Thema: Spalten vs binär
Das Einlese-Ziel ist im RAM immer ein vector mit ensprechendem Struct in dem ausschließlich simple Datentypen wie float/int vorkommen. Meistens 12Bytes Struct. Aktuell wird beim Lesen der String aus jeder Zeile zerlegt und dieses Stringfragment zu int/float gewandelt. Das ist natürlich umständlich. Da die Datentypen primitiv sind könnte man sicher ohne Risiko das komplette Struct binär abspeichern. Das wäre mein erstes Ziel. Ich habe mal irgendwo gelesen, dass man generell beim Files einlesen größere Stücke einlesen sollte und nicht nur Zeile für Zeile um die Geschwindigkeit zu erhöhen. Idee daher ein Array für das Struct zu erstellen mit fixer size. Man spart dann auch etliche Zeilenumbrüche die auch Zeichen sind und Platz auf der Platte kosten.
Mein Ansatz wäre jetzt:
Thema: Komprimierung
Mir ist aufgefallen, dass die Daten scheinbar gut zum Packen geeignet sind. Eine typische 5.5 GB Datei ist per 7z gepackt 0.318 GB !
Es drängt sich mir daher der Gedanke auf ob der Zeitverlust einer Decompression durch die so geringe einzulesende Datenmenge vielleicht sogar egalisiert wird? Die Anforderung an die CPU steigt zwar aber es muss dafür ja auch viel weniger von Platte gelesen werden. Evt ist die Decompression auch multicore skalierbar möglich dann würde das noch weniger ins Gewicht fallen.
Eine geringe Datenmenge bringt langfristig auch eher eine SSD ins Spiel wo sich aktuell nur eine Datengrab-HDD lohnt. SSD wäre wiederum ein Pluspunkt beim Thema Einlesespeed.
Aspekte:
Im Netz sind mir auf Anhieb zwei Algos ins Auge gesprungen:
LZ4 scheint für Speed ausgelegt zu sein während brotli eher bessere Kompression hat aber auch noch ganz ok ist von dem Speed.
Ansonsten klang das noch interessant:
Hier gibts ein Vergleich vieler Algos mit verschiedenen Settings:
https://quixdb.github.io/squash-benchmark/#results
Meine Fragen wären:
Soweit erstmal
Grüße
ich möchte in der nächsten Zeit mal meine Codes zum Speichern und Abrufen von persistenten Daten überarbeiten. Aktuell speichere ich in CSVs Datenpunkte(Klimadaten) in ca 3 bis 6 Spalten getrennt durch Semikolon. Jede Zeile ist ein Datenpunkt. Die maximale Größe der CSVs gehen so bis max 10GB. Der normale Vorgang ist die Daten in den RAM zu laden und dann dort solange wie benötigt zu halten da mehrere Workerthreads parallel und wiederholend diese Daten nutzen.
Zwei Dinge möchte ich verbessern:
- Die Datenmengen auf Platte reduzieren.
- Das Einlesen der Daten von Platte auf RAM beschleunigen.
Thema: Spalten vs binär
Das Einlese-Ziel ist im RAM immer ein vector mit ensprechendem Struct in dem ausschließlich simple Datentypen wie float/int vorkommen. Meistens 12Bytes Struct. Aktuell wird beim Lesen der String aus jeder Zeile zerlegt und dieses Stringfragment zu int/float gewandelt. Das ist natürlich umständlich. Da die Datentypen primitiv sind könnte man sicher ohne Risiko das komplette Struct binär abspeichern. Das wäre mein erstes Ziel. Ich habe mal irgendwo gelesen, dass man generell beim Files einlesen größere Stücke einlesen sollte und nicht nur Zeile für Zeile um die Geschwindigkeit zu erhöhen. Idee daher ein Array für das Struct zu erstellen mit fixer size. Man spart dann auch etliche Zeilenumbrüche die auch Zeichen sind und Platz auf der Platte kosten.
Mein Ansatz wäre jetzt:
- Das Struct zur Sicherheit mal mit #pragma pack(push,1) für ein immer exaktes Fitting.
- Das Struct als Array-Block mit zb 10000 Einträgen wird dann binär als eine Zeile abgelegt, so als Ansatz. myfile.write((char *) &StructArray, sizeof(StructArray));
- Einlesen dann per fcntl / unistd soll sehr schnell sein: https://stackoverflow.com/questions/17925051/fast-textfile-reading-in-c
Thema: Komprimierung
Mir ist aufgefallen, dass die Daten scheinbar gut zum Packen geeignet sind. Eine typische 5.5 GB Datei ist per 7z gepackt 0.318 GB !
Es drängt sich mir daher der Gedanke auf ob der Zeitverlust einer Decompression durch die so geringe einzulesende Datenmenge vielleicht sogar egalisiert wird? Die Anforderung an die CPU steigt zwar aber es muss dafür ja auch viel weniger von Platte gelesen werden. Evt ist die Decompression auch multicore skalierbar möglich dann würde das noch weniger ins Gewicht fallen.
Eine geringe Datenmenge bringt langfristig auch eher eine SSD ins Spiel wo sich aktuell nur eine Datengrab-HDD lohnt. SSD wäre wiederum ein Pluspunkt beim Thema Einlesespeed.
Aspekte:
- Compression Ratio: je höher desto besser aus Speicherplatzsicht.
- Compression Speed: das ist völlig egal. kann und darf langsam sein.
- Decompression Speed: damit die Daten dann schnell in den RAM kommen sollte das natürlich nicht zu lahm sein. Je höher die Compression Ratio desto geringer der Decompression Speed.
- Zugriff: Im Hinterkopf hab ich beim Ganzen noch, dass ich bei meinen Zeitstempelbasierten Daten auch einen bestimmten Startzeitpunkt finden muss von dem ab dann die Daten benötigt werden. Aktuell springe ich per seekg() im Abstand von 2000000 Zeichen weiter bis ich den Zeitpunkt gefunden habe. Das geht gut und sehr schnell. Wenn allerdings alles komprimiert ist, wäre meine Frage wie das Finden von einem Zeitpunkt dann geht...
- Hinzufügen: Wäre es gut wenn sich bestehende Daten anhängen lassen um bestehende Datensätze um aktuelle Daten zu erweitern.
Im Netz sind mir auf Anhieb zwei Algos ins Auge gesprungen:
LZ4 scheint für Speed ausgelegt zu sein während brotli eher bessere Kompression hat aber auch noch ganz ok ist von dem Speed.
Ansonsten klang das noch interessant:
- FSST: https://github.com/cwida/fsst Mit der Behauptung im Vergleich zu LZ4: "similar decompression speed and compression speed, and better compression ratio."
Hier gibts ein Vergleich vieler Algos mit verschiedenen Settings:
https://quixdb.github.io/squash-benchmark/#results
Meine Fragen wären:
- binär speichern lohnt sich, richtig?
- lohnt sich die Sache mit dem Binär-Array im Gegensatz zu Binär-Zeile-für-Zeile und wenn ja, wie groß sollte das Array sein? Bei 12Bytes Struct: ArrSize=10000 wären 0.12 MB, 100000 wären 1.2 MB. Gibt es da eine bestimmte Größe die gut ist?
- Welche Lib/Algo würdet ihr empfehlen?
- Kompensiert die geringere Datenmenge von komprimierten Daten die eingelesen werden müssen die Decompression Zeit? Weniger Daten von Platte holen -> spart Zeit, Daten entpacken -> kostet Zeit. Was fällt stärker ins Gewicht?
- Kann man in komprimierten Daten einen Abstand/Block x solange springen + kurz extrahieren bis gewünschter Zeitstempelbereich gefunden? Also wie oben beschrieben das "schnelle finden" durch größere Spünge in den Daten, geht das auch bei komprimierten Daten?
- Es gibt ja dann eine Kaskade aus Buffern und Blöcken bis die Daten im Vector landen. So stelle ich mir das vor:
- 1: Buffer beim Auslesen: im stackoverflow-beispiel ist zB BUFFER_SIZE = 16*1024
- 2: Buffer bei Decompression. Das Buffer-Thema verstehe ich da noch nicht gut. Da es komprimiert ist, weiß man ja nicht wieviel ungepacktes im gepackten genau steckt...?
- 3. Block BinärStructArray -> muss in vector.
- Ich schätze die Buffer und Wahl der BinärStructArray-Size sollte/muss man möglichst schlau aufeinander abstimmen richtig?
Soweit erstmal
Grüße
Zuletzt bearbeitet: