C++ Konzept persitente Daten

Im Grunde ist alles kein akutes Problem sondern eher eine Optimierung eines schon lang funktionierenden Systems. Neben der Dateigröße stört mich aber vor allem eher die Einlese-Zeit aber wie ja bereits festgestellt, ist meine Art mit dem parsen Stringzeile-to-Struct, Zeile per Zeile das wahrscheinlich langsamste was nur geht, damals war ich ein noch schlechterer Coder als heute ;) Ist eh lustig wenn man sich seine eigenen Codes nach 2 Jahren anschaut und denkt ach du sch...e, 2 Jahre später wieder das Selbe mit den Codes von heute... :D

Was Plattenkompression angeht hatte ich mir das gespeichert:
https://en.wikipedia.org/wiki/Btrfs
Das Thema guck ich mir auch mal an.

NemesisFS schrieb:
Die Daten selbst binär zu schreiben kostet dich eine Menge Arbeit und das erkaufst du dir durch Komplexität.
Das ist eigentlich easy und ich denke es verringert die Komplexität weil ich die Strings nicht aufbröseln muss, hatte schon während dem Thema hier einen kleinen Entwurf um das in Blöcken zu machen:

C++:
#pragma pack(push,1) // paranoia exaktes fitting
struct struct_Temps
{
   unsigned int TS;
   float T;
   float T2;
   // ggf weitere
}
#pragma pack(pop)

const ArrSize = 1000000;
struct_Temps Temps[ArrSize];
und dann einfach:
myfile.write((char *) &Temps, sizeof(Temps));
myfile.read((char *) &Temps, sizeof(Temps));

Alleine nur das sollte schon extrem viel bringen.
Wie richtig geschrieben wurde, ein Bottleneck ist auch echt die Auslesegeschwindigkeit von HDDs. Insofern bin ich fast sicher, dass Kompression den Speed verbessert weil komprimiert mehr Daten schneller im RAM sind selbst wenn sie dort noch dekomprimiert werden müssen. Bei Kompression ist zstd richtig fein (merci für den Tipp an @Bagbag). Auch da hatte ich mal was probiert, ist denke ich auch kein so großes Ding, sah zumindest nicht so kompliziert aus.

Hier war die umwerfend einstimmige Meinung Datenbank. Allerdings wenn man mal so liest bei ähnlichen Themen im Netz wird meist gesagt, dass Flat File schneller ist (it depends ;))
https://stackoverflow.com/questions...om-files-or-a-database-server/2148081#2148081
https://stackoverflow.com/questions/2356851/database-vs-flat-files
In den Meisten Fällen ist die MySQL Datenbank ein Overkill. Diese würde sich von der Performance her bemerkbar machen, wenn die Seite hunderttausende Besucher täglich erreicht
https://carstengrimm.com/blog/ab-wann-brauch-man-eine-datenbank

Aber wie immer "it depends" es ist natürlich klar, dass je nach Zugriffsverhalten pro/contra gewichtet ist. Bei mir werden ja einfach Daten von Position x bis Position Y am Stück in RAM übertragen nix mehr. Es geht nicht ums permanente Finden von unterschiedlichen Stellen, nur einmal Stelle finden und dann ab dort in RAM kopieren. Hat alles seine Daseinsberechtigung für den jeweiligen Usecase, nachdem ich mich jetzt etwas mehr damit beschäftigt habe finde ich immer noch, dass die Notwendigkeit/Stärken einer DB jetzt nicht unbedingt in meinem Anwendungsfall so klar ausgespielt werden. Auf der anderen Seite kann man die soliden Mechanismen einer DB natürlich später vielleicht mal für andere Projekte gebrauchen.
MongoDB hat inzwischen zstd was sehr cool ist. Allerdings ist ein Influxdb (thx @cloudman) natürlich für den Anwendungsfall sicherlich viel besser/schneller. Mongo wiederum wäre eben auch ggf für zukünftige Projekte cool und irgendwie universeller. Und SQLite ist eh immer im Hinterkopf.

Ich werde am Ende wohl beides ausprobieren, also das Ganze einmal als Datenbank was mich langfristig mal reizt aber auch mein "flat files" Ansatz den ich sicherlich schneller fertig habe weil nur binär+zstd ins aktuelle Geschehen zwischengeschaltet werden muss. Habe aktuell nicht viel Zeit aber wenn ich was fertig hab werde ich hier berichten.
 
T_55 schrieb:
Hier war die umwerfend einstimmige Meinung Datenbank. Allerdings wenn man mal so liest bei ähnlichen Themen im Netz wird meist gesagt, dass Flat File schneller ist (it depends ;))
Das hängt hauptsächlich von deinem Zugriffsmuster ab. Wenn du immer sämtliche Daten aus der Datei brauchst, dann wird die Datenbank das auch nicht schneller erledigen.
Aber wenn du z.B. in deiner Datei eine Millionen Einträge hast, aber nur 10000 irgendwo aus der Mitte brauchst, dann wird der Index einer Datenbank enorm helfen, weil dadurch eben extrem schnell die relevanten Datensätze ermittelt werden können, anstatt alle Einträge zu durchsuchen.
Du kannst so einen Index natürlich auch selbst bauen und mit deinem eigenen Format benutzen. Das wäre sicherlich in Sachen Performance und Speicherbedarf das effizienteste.
Wahrscheinlich kann dir auch (fast) jede Datenbank deine Berechnungen abnehmen. Sowas wie "gib mir die Mittelwerte aller Montage pro Monat für die Jahre 2016 bis 2020" ist für eine Datenbank ein Kinderspiel. Die Datenbank Abfrage dafür ist in 10 Zeilen geschrieben, und die Datenbank kümmert sich darum, dass der Zugriff auf die Datensätze möglichst effizient geschieht, und nicht Millionen Datensätze betrachtet werden, die nichts zum Ergebnis beitragen.

Die Informatik hat sich allerdings in den letzten 10-20 Jahren stark verändert: Performance und Speicherbedarf sind keine echten Probleme mehr. Man kann sie einfach für ein paar Euro mit mehr Hardware erschlagen.
Das was wirklich Kohle kostet ist: Zeit und Arbeitsstunden. Deshalb greift man in 99,9% der Fälle auf etablierte und getestete Dinge zurück (in diesem Fall eben eine Datenbank). Das spart Unmengen an Zeit und Nerven. Und macht das gesamte System viel einfacher wartbar und erweiterbar.
 
  • Gefällt mir
Reaktionen: kuddlmuddl und Raijin
Hä? Ich bin hier bei jedem post ein bissel mehr am Staunen.

1 natürlich ist ein dbms schneller, kleiner, effizienter, performanter als plaintext. Außer man hat faktisch keine Daten. Keine Ahnung wie man auf was anderes kommen kann.

2 SQLite ist nett zum lernen, aber ehrlich gesagt würde ich es meiden. Einfach deswegen, weil es so gut wie nicht typsicher ist. Grad bei Datetime geht alles rein, das ist einfacher Text. Schlecht, wenn man mit Zeit/kalenderdaten arbeiten muß und jedes Datum anders aussieht!

3 wie schon ab und an angemerkt, Kompression erstmal vergessen. Vorher in Sachen Datenbanksystem schlau machen, gibt genug, hat man ziemlich Freiheit. Solange das nicht gewerblich ist, sind praktisch alle dbms kostenlos.
Die “komprimiert“ vollautomatisch. Wenn das dann nicht reicht, kann man ja nochmal gucken.

4 keine blobs! Wer kommt auf sowas? Blobs dann wenn unstrukturiertes Zeugs dazukommt, wo der Aufwand Dateisystem größer ist als das aus dem Datensatz mitzunehmen. Zb ein kleines Icon. Ne Konfigdatei die zum Datensatz dazugehört und die die Anwendung selber verarbeiten soll. Kurz alles was einen bei der Verarbeitung nicht nur selten, sondern bereits konzeptuell nicht interessiert und nie interessieren wird und was man technisch als Datei irgendwo hin tun könnte.

ich verstehe ja den Anspruch, Bekanntes weiterverfolgen zu wollen. Mach ich ja auch. Aber, hier hab wir ein BilderbuchBeispiel für Reimplementierung in DBMS. Da reden wir noch gar nicht von der Datensicherheit-cf Transaktion und Acid- ich meine es ist definitiv mutig, man könnte es aber auch grob fahrlässig nennen, da mit plaintext im backend zu arbeiten.
 
  • Gefällt mir
Reaktionen: Raijin
Ich kann nur nochmal influxdb anpreisen 🙂
Die Auswertung ist damit wirklich ein Kinderspiel. Zusammen mit Grafana gibt es auch schöne Grafiken dazu.
Ein Beispiel mit 3 Temperatursensoren in 10 Minuten zusammen geklickt 🙂 Der Anzeigezeitraum ist auch variabl
Screenshot_20201130-231016_Chrome.jpg


Nur eine offizielle C++ lib gibt es nicht allerdings findet man dazu genügend auf github

Das Protokoll zum schreiben ist eine einfache Rest Api : https://docs.influxdata.com/influxdb/v2.0/write-data/developer-tools/api/
 
  • Gefällt mir
Reaktionen: T_55
RalphS schrieb:
1 natürlich ist ein dbms schneller, kleiner, effizienter, performanter als plaintext. Außer man hat faktisch keine Daten. Keine Ahnung wie man auf was anderes kommen kann.
Das hat auch nirgendwo jemand behauptet. Der Witz und Sinn des Threads ist ja von Anfang an Plaintext NICHT mehr zu nutzen. Die Debatte spielt sich zwischen binärblock und dbms ab. Das eine dbms in meinem UseCase schneller als binärblock ist würde ich nach meinen Stand erstmal bezweifeln. Ich werde es irgendwann einfach messen weil ich beides ausprobieren will.
Wie eine dbms kleiner sein soll als eine of/ifstream Funktion der STL erschließt sich mir nicht.

RalphS schrieb:
Da reden wir noch gar nicht von der Datensicherheit-cf Transaktion und Acid- ich meine es ist definitiv mutig, man könnte es aber auch grob fahrlässig nennen, da mit plaintext im backend zu arbeiten.
Ich bin völlig dabei, dass dbms viele Mechanismen hat (die ich zum Teil auch nicht mal kenne) welche sehr nützlich sind und Sicherheit schaffen.
Mir ist allerdings auch mit den normalen filewrite/read Vorgängen nie etwas negatives passiert oder aufgefallen. Wenn bei einer Platte Sektoren flöten gehen und eine dbms hat die Daten auch nur an einem Ort dann sind die doch genauso weg. Da wäre doch eher das Thema RAID der Sicherheitsfaktor.

cloudman schrieb:
Ich kann nur nochmal influxdb anpreisen 🙂
Die Auswertung ist damit wirklich ein Kinderspiel. Zusammen mit Grafana gibt es auch schöne Grafiken dazu.
Ein Beispiel mit 3 Temperatursensoren in 10 Minuten zusammen geklickt 🙂
Ja das würde zu dem UseCase wahrscheinlich auch am besten passen. Visualisierung sieht auch richtig gut aus per Grafana 👍 Ansonsten war ich noch über TimescaleDB gestoplpert.
 
Zuletzt bearbeitet:
Für mich klingt das hier nach nem typischen fall von "Wenn man nur nen Hammer hat, dann sieht jedes Problem aus wie ein Nagel". Natürlich ist ne Datenbank immer ne sinnvolle erste Wahl, wenn es um das managen großer Datenmengen geht, aber das als "die einzig wahre" Lösung für dieses spezielle Problem darzustellen ist "etwas" übertrieben.

Wir haben hier (soweit bisher verstanden) den Spezialfall, dass einfach nur X-seriell vorliegende Einträge mit ner einheitlichen, fixen Größe auf die Plattte geschreiben werden und wieder ausgelesen werden sollen. Die meisten Lesezugriffe werden wohl linear erfolgen. Es müssen nachträglich kein Daten eingefügt, gelöscht oder modifiziert werden, sondern einfach nur neue Datensätze angehängt werden. Die Daten werden nicht von mehrern Usern gleichzeitig bearbeitet und es scheint hier auch keine Anforderungen an Hochverfügbarkeit oder Datensicherheit zu geben die über das hinausgeht, was man für alle private Daten gerne hätte. Und zu guter letzt reden wir hier auch nicht von X Terrabyte an Daten, sondern vermutlich gerade mal von bein paar Gigabyte oder weniger (wenn binär abgespeichert).

Was ich da erstmal ausprobieren würde wäre ein simples memory mapped file (und zwar unkomprimiert). Das passt eigentlicih wie die Faust aufs Auge und je nach dem, was du genau vor hast müsstest du noch nicht mal die Daten in ein lokales array einlesen, sondern direkt auf den daten im Dateisystem arbeiten. Weniger overhead geht eigentlich nicht und da du weißt, wie groß die einzelnen Datensätze sind ist es z.B. trivial die Position des 50.000en Datensatzes zu finden.

Ich hab nen kurzen test auf meinen Windows System (Haswell mit ssd) gemacht: 100 Millionen zufällig generierte Datensätze bestehend aus je 4 float Werten und einem Integer Wert (knapp 2 GB). Das Berechnen von Durchschnitt und Varianz für alle 5 Werte hat beim ersten durchlauf 3,5 Sekunden gedauert (Single threaded, ohne spezielle optimierungen) und unter einer Sekunde beim zweiten, weil Windows die Daten noch im RAM hatte (Wohlgemerkt, das Program wurde zwischendrin komplett neugestertet). Ich bin mir sicher, dass es je nach Datenbanktyp und Operation die man ausführt einiges gibt, was die Datenbank schneller kann, aber für den hier vorgestellten Anwendungsfall bezweifle ich, dass diese Unterschiede relevant sind.

Was ich aber aus vollstem Herzen unterstützen würde ist zu schauen, obs nicht ein einfach zu nutzendes High Level Tool gibt, mitdem man das ganze System (inklusive Userinterface) ersetzen kann und nicht nur die Datenaufbewahrung (influxdb wurde ja schon genannt). Temeraturwerte über die Zeit aufzunehmen, darzustellen und zu analysiseren ist ja nun nicht gereade eine besonders exotische Aufgabe. Das mit selbst geschriebenen C++ Programmen zu erledigen (egal ob mit oder ohne Datenbank im Hintergrund) macht außerhalb von Übungsaufgaben oder high performance Szenarien meiner Meinung nach wenig Sinn (evtl. noch im embedded bereich).

RalphS schrieb:
Da reden wir noch gar nicht von der Datensicherheit-cf Transaktion und Acid- ich meine es ist definitiv mutig, man könnte es aber auch grob fahrlässig nennen, da mit plaintext im backend zu arbeiten.
Genau, davon reden wir hier nicht. Nichts davon is hier notwendig oder relevant.

benneq schrieb:
Aber wenn du z.B. in deiner Datei eine Millionen Einträge hast, aber nur 10000 irgendwo aus der Mitte brauchst, dann wird der Index einer Datenbank enorm helfen, weil dadurch eben extrem schnell die relevanten Datensätze ermittelt werden können, anstatt alle Einträge zu durchsuchen.
Ich glaube du überschätzt massiv, wie lange ne simple binary search in einem array dauert (selbst wenn dieses array ein paar GB groß ist). Klar, wenn ich jede Sekunde mit zig Anfragen bombardiert werde sind solche sachen essentiell, aber hier?
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: kuddlmuddl, T_55 und BeBur
Miuwa schrieb:
Ich glaube du überschätzt massiv, wie lange ne simple binary search in einem array dauert
Nein. Ich habe da auch gleich die gesparte Zeit mit einbezogen, die sich durch das nicht-laden von 10 GB in den RAM ergibt. Für die Suche muss ja nur der deutlich kleinere Index im RAM liegen.
Aber wie ich eingangs schon sagte: Ob/wie/was hier welche Vorteile hat, hängt maßgeblich von den Zugriffsmustern, Querys und Aggregationen ab. Dazu schweigt der TE ja äußerst fleißig seit Seite 1.
 
  • Gefällt mir
Reaktionen: kuddlmuddl
benneq schrieb:
Nein. Ich habe da auch gleich die gesparte Zeit mit einbezogen, die sich durch das nicht-laden von 10 GB in den RAM ergibt.
Brauchst du ja nicht. Memory mapped file läd standardmäßig lazy (+ prefetch, wenn das OS erkennt, dass du sequentiell auf die daten zugreifst). Ein naive binäre suche über 100 Mio elemente läd im worst case 27 Pages (wobei die letzten paar Zugriffe in der gleichen Page liegen). Mit ner SSD ist das praktisch instantan (ne HDD hab ich grad nicht zum testen da).
 
  • Gefällt mir
Reaktionen: BeBur
Ich verweise grad nochmal hierauf: Link
So wie ich den TE verstehe will er quasi einmal pro Tag sein CSV File laden und dann über die Zeilen iterieren. Ein DBMS als notwendige Lösung leuchtet mir hier auch nicht ein, wenn das auch sicherlich eine technisch nicht unsinnvolle Option ist.

Jedenfalls würde ich erst einmal niederschwelligere Optionen erforschen.
 
Wir nähern uns wieder #1 an ;)

benneq schrieb:
Ob/wie/was hier welche Vorteile hat, hängt maßgeblich von den Zugriffsmustern, Querys und Aggregationen ab. Dazu schweigt der TE ja äußerst fleißig seit Seite 1.
Hatte ich im ersten Beitrag geschrieben.
T_55 schrieb:
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.
T_55 schrieb:
Bei mir werden ja einfach Daten von Position x bis Position Y am Stück in RAM übertragen nix mehr. Es geht nicht ums permanente Finden von unterschiedlichen Stellen, nur einmal Stelle finden und dann ab dort in RAM kopieren.



Miuwa schrieb:
Was ich da erstmal ausprobieren würde wäre ein simples memory mapped file (und zwar unkomprimiert).
Ja exakt das wird auch in der am höchsten bewertetsten stackoverflow-Anwort als erstes genannt den ich im ersten Post drin hatte. Der Typ dort hat es dann weiter mit mmap optimiert und dann abschließend war das schnellste in seinem Test das Einlesen per fcntl / unistd was er sich bei der source von coreutils wc abgeschaut hat.
T_55 schrieb:

Also ich werde auf jeden Fall berichten sobald ich was fertig hab 👍
 
T_55 schrieb:
Hatte ich im ersten Beitrag geschrieben.
Hilft aber immer noch nicht weiter. "Daten in den RAM laden" sagt halt nichts darüber aus was danach mit den Daten passiert. ...und nun sag nicht, dass die Daten von Workerthreads genutzt werden :D
Interessant wäre was mit den Daten gemacht wird, was da berechnet wird.
 
Was da genau berechnet wird würde den Rahmen sprengen und hat eigentlich nichts direkt mit dem Speichern und in den RAM laden zu tun, aber wenn es interessiert, die Daten werden genutzt um bauphysikalische Effekte an beispielsweise Verglasungssystemen und deren Luftschichten in verschiedenen Klimazonen/Jahreszeiten/Uhrzeiten zu simulieren. Da auch manchmal große Parametersets durchgeführt werden, bei denen es zu > 100k Durchläufe geben kann, was teilweise mehrere Tage auf 32 Threads läuft, müssen die Daten auf dem RAM liegen und deshalb wird auch mit C++ gearbeitet. Am Ende ist es ja aber eigentlich für das Thema egal was mit den Daten gemacht wird, sobald egal in welchem Anwendungsbereich mit Daten parallel und wiederholt gearbeitet wird ist der RAM eigentlich immer der beste Ort bzw der näheste Ort am L1/2/3 cache der CPU.
 
T_55 schrieb:
Ja exakt das wird auch in der am höchsten bewertetsten stackoverflow-Anwort als erstes genannt den ich im ersten Post drin hatte. Der Typ dort hat es dann weiter mit mmap optimiert und dann abschließend war das schnellste in seinem Test das Einlesen per fcntl / unistd was er sich bei der source von coreutils wc abgeschaut hat.
Der Unterschied ist, dass ich dir vorschlage, die Daten aus dem memory mapped file NICHT in ein lokales array zu laden/kopieren, wie das im SO thread gemacht wurde, sondern direkt auf dem Addressbreich des files zu arbeiten. Du willst ja eben kein text/csv File mehr einlesen, dass dann"übersetzt" werden muss, sondern direkt ein Binärformat verwenden und da kannst die Daten halt auch gleich genau in dem Format auf der Platte schreiben, wie sie auch in deinem Programm genutzt werden sollen und dann direkt drauf arbeiten und dir so das einlesen sparen.

Man müsste jetzt Benchmarks fahren um zu sehen ob deine nachfolgenden Operationen signifikant schneller laufen, wenn sie auf Daten in einem lokalen array zugreifen, statt auf daten in einem File-Backed Bereich (entschuldige das Denglish), aber ich vermute eher nicht, weil sie so oder so im RAM (oder sogar CPU cache) liegen.

Übrigens ist mmap keine Optimierung von memory mapped, sondern boost::mapped_file ist letzendlich ein wrapper um mmap bzw. das windows äquivalent.
 
  • Gefällt mir
Reaktionen: T_55
Alles klar verstehe, ja das ist ne sehr gute Idee auf ein lokales (zwischen)Array zu verzichten, direkter geht es dann echt nicht wenn man es gleich ins Endziel reinlegt. Speicherziel ist derzeit std::vector von dem sich die Arbeiterthreads dann bedienen, dieser ist soweit ich weiß save immer ein zusammenhängender Speicherbereich wie ein C-Array also kann ich die Blöcke dort direkt auf die richtige Block-Startadresse reinschieben. Bei einem deque oder anderen gestückelten Containern wäre es nicht save. Natürlich vorher den vector in korrekter/ausreichender Größe bereitstellen bzw einen ausreichendes resize() durchführen.
 
T_55 unterschätz bitte nich, wie sinnvoll mehr Kontext ist!
zB würde ich jetzt mit deinen zusätzlichen Erläuterungen " bei denen es zu > 100k Durchläufe geben kann, was teilweise mehrere Tage auf 32 Threads läuft, müssen die Daten auf dem RAM liegen "
pauschal behaupten, dass viele "pro DB" Empfehlungen in die falsche Richtung gegangen sind. (Höchstens, um nur Teile und nicht alles gleichzeitig in den RAM zu holen - was du niemals brauchen wirst nach den zusätzlichen Infos zu urteilen).

Ob du damit nun Klima oder Statik berechnest ist "für uns" natürlich irrelevant. Aber was benneq meint sind sicherlich Fragen, wie:
- Liest oder schreibst du eher? Und auch gleichzeitig? Also kann es sein, dass 31 Threads noch lesen, während einer schon schreibt? Oder rechnen alle 32 lesend auf den Daten und aktualisieren dann halbwegs gleichzeitig? Oder schreibst du evtl nur in ganz anderem Speicher und dein riesen-vector ist read-only? Und in welchen 'Regionen' der Daten liest+schreibst du. Sind sie sortiert? Kann man das pro Thread vorher schätzen? Nur nach t? (=1D) Oder auch mehrdimensional? Arbeitest du linear durch? Oder brauchst du nahezu random-access?

Musst du nicht alles beantworten! Aber hat großen Einfluss auf die Wahl des idealen Containers.
Wenn es nur nach t geht: Wieso nutzt du dann nen vector und nicht ne std::map<time, data> ? Oder wenn du nichtmal sortiert durchiterierst eine std::unordered_map? Machst du fuzzy-(unpräzise)querys? Oder kennt ein Thread die genauen Keys, welche geladen werden?

etc etc...

Und wie hier empfohlen: https://www.computerbase.de/forum/threads/konzept-persitente-daten.1984152/post-24936538
Man serialisiert nicht selber sondern nutzt zB Boost.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: T_55 und benneq
Als kleine Erläuterung wieso ich und ein paar andere hier Zeitreihen DBs empfohlen haben (das ist nochmal was ganz anderes als relationale DBs). Ein Problem das hier geschildert wurde war der Speicherplatz und die Geschwindigkeit beim einlesen. Das ist ein Faktor bei dem man sehr viel rausholen kann wenn man die besonderen Eigenheiten von Zeitreihen ausnutzt. Man kann diese Daten sehr viel mehr komprimieren wenn man die Struktur kennt als mit einem komplett generischen Algorithmus. Eine Übersicht kann man z.B. hier finden:

https://blog.timescale.com/blog/time-series-compression-algorithms-explained/

Insbesondere für die Timestamps selbst kann man mit delta-of-delta encoding extrem viel rausholen. Das geht aber halt schon in eine Richtung in der man nicht mehr alles selbst implementieren will.

Ein einfaches, eigenes Binärformat ist sicher auch möglich und kann durchaus gut genug sein. Ich wäre da aber fast geneigt einfach weiter Textdateien zu nehmen und das Problem mit besserer Hardware (SSDs, evtl sogar NVMe) zu erschlagen. Wenn die Daten am Ende sowieso in eigener Struktur im RAM liegen müssen da man mehrmals drüber muss, geht es halt wirklich nur um die Geschwindigkeit beim ersten Einlesen.
 
  • Gefällt mir
Reaktionen: T_55
@kuddlmuddl
Das ging ja mit dem nicht durchlesen meines Textes los ;)
NJay schrieb:
Ich hab mir jetzt nicht alles durchgelesen, aber wie waere es einfach mit einer Datenbank?

Das die Daten in den RAM müssen hab ich in #1 gesagt, ok hab dies nicht weiter begründet aber ist natürlich immer die Frage wie weit man ins Detail geht, es war ja viel Text, sodass er so schon nicht ganz gelesen wurde, ich wollt euch jetzt nicht irgendwas bis hin zu Transmisson bis Absorptionskoeffizienten ausführen, dann liest sich das gar keiner mehr durch. Die Frage bezog sich ja speziell auf das Thema Datei->RAM und nicht das Thema RAM->CPU(also die calcs). In meinem Programm sind das eben zwei völlig getrennte Schritte und es ging mir um den ersten.

Ich finde aber die "pro DB" Empfehlungen keineswegs falsch oder in die falsche Richtung, war eben nur zu Beginn noch etwas einseitig, es ist immer gut alle Möglichkeiten in Betracht zu ziehen und vor allem die TimeSeries DB sind eine gute Idee und ich sehe das gleichwertig beides hat seine Vor- und Nachteile je nach Gewichtung der Aspekte.

kuddlmuddl schrieb:
- Liest oder schreibst du eher? Und auch gleichzeitig? Also kann es sein, dass 31 Threads noch lesen, während einer schon schreibt? Oder rechnen alle 32 lesend auf den Daten und aktualisieren dann halbwegs gleichzeitig? Oder schreibst du evtl nur in ganz anderem Speicher und dein riesen-vector ist read-only? Und in welchen 'Regionen' der Daten liest+schreibst du. Sind sie sortiert? Kann man das pro Thread vorher schätzen? Nur nach t? (=1D) Oder auch mehrdimensional? Arbeitest du linear durch? Oder brauchst du nahezu random-access?

Musst du nicht alles beantworten! Aber hat großen Einfluss auf die Wahl des idealen Containers.
Ist kein Geheimnis und schnell beantwortet. Die Daten sind nach Zeitstempel sortiert und der gewünschte Zeitabschnitt wird komplett in den RAM geladen. Danach wird dieser vector nur read-only genutzt von einem oder vielen Threads welche diesen Vector linear durchlaufen. Manchmal werden auch mehrere vectoren angelegt dann läuft das sozusagen doppelt parallel ab. ZB die Reihen von Städten von Südafrika, Finnland, und Italien würden jeweils dann einen vector darstellen.
Aber ändert grundsätzlich nichts am Thema Datei->RAM ;)
Die Wahl des Containers also ein Array wie der klassische vector sollte aufgrund der linearen Verarbeitung daher am besten passen. Eine Map würde performacemäßig nicht passen da eher fragmentiert im RAM.

@Dalek danke das schaue ich mir an. NVMe hab ich auch im Hinterkopf.
 
Zuletzt bearbeitet:
T_55 schrieb:
Alles klar verstehe, ja das ist ne sehr gute Idee auf ein lokales (zwischen)Array zu verzichten, direkter geht es dann echt nicht wenn man es gleich ins Endziel reinlegt. Speicherziel ist derzeit std::vector von dem sich die Arbeiterthreads dann bedienen, dieser ist soweit ich weiß save immer ein zusammenhängender Speicherbereich wie ein C-Array also kann ich die Blöcke dort direkt auf die richtige Block-Startadresse reinschieben. Bei einem deque oder anderen gestückelten Containern wäre es nicht save. Natürlich vorher den vector in korrekter/ausreichender Größe bereitstellen bzw einen ausreichendes resize() durchführen.
Ich sag mal so: mit dem Neuen Kontext, dass du erstmal tagelang auf die Daten zugreifst ist es erstmal völlig egal, ob du dir den overhead beim Einlesen einmalig sparst oder nicht - von demher ist mein Vorschlag für dein Problem ziemlich irrelevant (bzw. verursacht vielleicht mehr Probleme, als es dir bringt).

Ich hab aber den Eindruck, dass wir immer noch aneinander vorbeireden. Mein Vorschlag war es, den std::vector oder was auch immer du verwendest komplett zu eliminieren. Du weißt einfach dein OS an, dass es alle Lesezugriffe im Addressbreich von X bis Y auf Lesezugriffe innerhalb der Datei umleitet. Aus sicht des Programms gibt es schlicht weg kein "einlesen" mehr. Die Daten tauchen plötzlich 1 zu 1 so, wie sie in der Datei vorliegen im Prozess auf. Du bekommst nen Pointer an den Anfang dieses Bereichs und kannst sie sofort in deinen threads nutzen (statt vector[1000] um auf den 1001ten Eintrag zuzugreifen nutzt du jetzt halt start_ptr[1000]).

Unter Windows würde das auslesen z.B. so aussehen:

Code:
struct Entry
{
    std::size_t i;
    float a;
    float b;
};


int main()
{
    constexpr std::size_t entry_cnt = 100'000'000;
    constexpr std::size_t data_size = entry_cnt * sizeof(Entry);

    auto fhandle = CreateFileA(
        "database",
        GENERIC_READ
        0,
        nullptr,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        nullptr
    );

    auto fmap = CreateFileMappingA(
        fhandle,
        nullptr,
        PAGE_READONLY,
        // Have to split the 64bit size into upper and lower DWORDS (32bit)
        data_size >> 32,
        data_size & 0x00'00'00'00'ff'ff'ff'ff,
        nullptr
    );

    auto data_start = (const Entry*)MapViewOfFile(
        fmap,
        FILE_MAP_READ,
        0,
        0,
        0
    );

    double acc_a = {};
    for (std::size_t i = 0; i < entry_cnt; ++i) {
       // directly reading data from file when needed
        acc_a += data_start[i].a;
    }

    std::cout << "The average value of a is " << acc_a / entry_cnt << std::endl;
}
Fehler behandlung hab ich jetzt mal außen vor gelassen und unter linux brauchst glaube ich einen system call weniger (nur fopen und mmap), aber das Prinzip ist das gleiche. Je nach Vorliebe kann man dem ganzen noch eine vector-ähnliches Interface verpassen (ich vermute mal, dass boost das out of the box bietet):

Code:
const auto dataview = std::span<const Entry>(data_start, entry_cnt);
double acc_a = {};
for (const auto& e : dataview) {
    acc_a += e.a;
}

Die Daten werden vom OS dann automatisch im RAM gecached (was es ohnehin bei jeden Lesezugriff auf eine Datei der Fall ist), d.h. logisch greift data_start[2] zwar auf Daten auf der Festplatte zu, aber nach dem ersten Zugriff wirds dann aus dem RAM oder sogar L1,L2,L3 cache geholt.

Aber wie schon gesagt: Ob du dir mit der Methode jetzt 20 Sekunden beim Einlesen sparst, wenn danach eh stundenlang gerechnet wird ist dann auch wieder egal.
 
  • Gefällt mir
Reaktionen: T_55
Ich beschränke mich in meiner Antwort auf C++ Aspekte:

T_55 schrieb:
Alles klar verstehe, ja das ist ne sehr gute Idee auf ein lokales (zwischen)Array zu verzichten, direkter geht es dann echt nicht wenn man es gleich ins Endziel reinlegt. Speicherziel ist derzeit std::vector von dem sich die Arbeiterthreads dann bedienen, dieser ist soweit ich weiß save immer ein zusammenhängender Speicherbereich wie ein C-Array also kann ich die Blöcke dort direkt auf die richtige Block-Startadresse reinschieben. Bei einem deque oder anderen gestückelten Containern wäre es nicht save. Natürlich vorher den vector in korrekter/ausreichender Größe bereitstellen bzw einen ausreichendes resize() durchführen.

Korrekt, ein std::vector speichert die Daten in einem zusammenhängenden Speicherbereich, während ein std::deque das im Allgemeinen nicht macht.
std::vector ist am effizientesten wenn die Gesamtgröße des finalen vectors von Beginn an bekannt ist. Dann machst du nach Erstellung des vectors entweder ein std::vector::reserve gefolgt von push_back / emplace_back / back_inserter-iterator etc. um die Elemente einzufügen, oder ein std::vector::resize und überschreibst dann die Elemente später mit den tatsächlichen Werten. Falls der notwendige Speicher nicht alloziiert werden kann, wird eine exception geworfen. Gerade weil ein std::vector einen zusammenhängenden Speicherbereich hat, ist aber hiermit (bei großen vectors) auch die Gefahr größer dass die Speicheranforderungsanfrage fehlschlägt, denn der freie Speicher muss ja nicht nur mengenmäßig, sondern auch in einem kontinuierlichen Bereich zur Verfügung stehen -> und je stärker der RAM fragmentiert ist, desto geringer ist die Chance dafür.
Ein std::deque hat keinen zusammenhängenden Speicherbereich. Er kommt mit einem kleinen Speicheroverhead daher (zum Verwalten der einzelnen memory-chunks), da jeder chunk aber "relativ klein" ist kommt ein deque viel besser mit fragmentiertem RAM zurecht. Beim deque ist es auch wesentlich unerheblicher ob die Gesamtgröße von Beginn an bekannt ist oder nicht, denn ein std::deque ist generell ziemlich effizient bzgl. Einfügen und Entfernen von Elementen sowohl am Beginn als auch am Ende (std::vector: nur beim Ende und wenn dabei nicht neu-alloziiert werden muss).
Sowohl vector als auch deque bieten random-access auf die Elemente, sind also lesend sehr effizient (std::vector wegen seinem Speicherlayout wohl minimal effizienter).

Unter Umständen bietet sich auch eine mixed-case Verwendung an: In einem std::deque werden die "großen" Datenmengen gespeichert, und falls worker-threads jeweils nur einen relativ kleinen Teil dieser Daten brauchen, dann kopieren sie diese Daten einmalig in ihren eigenen (kleineren) std::vector und haben sie dann maximal lokal zur Verfügung.


@ Binärdaten schreiben / lesen (egal ob z.B memory-mapped file oder aus File lesen und in Container kopieren etc.):
Aus Sicht von C++ funktioniert das nur +- garantiert wenn du mit derselben Plattform und Konfigurationen die Daten schreibst wie später auch wieder liest (Stichworte: sizeof, alignment, Endian etc.). Theoretisch kann (auch wenn praktisch unwahrscheinlich) bereits das nächste Compiler-update oder eine Änderung der Compiler-settings die Kompatibilität brechen. Um Plattformunabhängigkeit zu erzielen brauchst du sowas wie boost::serialization.
Bzgl. csv/ASCII - Daten: Selbstverständlich ist das im Vergleich zu den anderen Verfahren bzgl. Einlesen ineffizient, es hat aber wiederum den Vorteil dass die Daten human-readable gespeichert sind bzw. plattformunabhängig ausgelesen werden können. Durch entsprechend effiziente Einlese-verfahren kann man übrigens auch das Einlesen von ASCII-Daten merklich beschleunigen (als erste Anlaufstelle fällt mir da boost::spirit ein).
 
  • Gefällt mir
Reaktionen: T_55
Hallo,
Ich wollte nur ein paar Sätze beitragen, da schon vieles gesagt wurde.
  • du hast als Fachfremder allein angefangen und bist jetzt an diese Stelle gelangt. Hut ab!
  • für viele Arten von Anforderungen gibt es viele Standardlösungen. Nutze sie!
  • DBMS: Sqlite kommt nur in Betracht, wenn eine externe DBMS nicht möglich ist. Ich empfehle PostgreSQL oder MySql.
  • das empfohlene HDF ist sicher auch eine Lösung.
 
  • Gefällt mir
Reaktionen: T_55 und kuddlmuddl
Zurück
Oben