Große Datenmengen effizient programmieren

Bitte aktiviere JavaScript!
Hi,

Hintergrund:
Ich analysiere Börsendaten aus der Vergangenheit über Sinn bzw. Un-Sinn hier bitte nicht diskutieren :)

Ich habe diese Daten im csv Format vorliegen. Einmal die Werte von jedem Tag und einmal jeden einzelnen Tick pro Tag.
Die csv von den Tagen hat ein paar Megabyte, das ist kein Problem, aber die Datei mit den Ticks hat ca. 5 Gigabyte. Ich lese zuerst die Tagesdaten aus und speichere sie in ein entsprechendes Objekt:
Java:
    private LocalDateTime date;
    private BigDecimal open;
    private BigDecimal high;
    private BigDecimal low;
    private BigDecimal close;
    private List<CandleStickTick> listCandleStickTick = new ArrayList<>();
Je nach "Testfall" gibt es in dem Objekt auch noch 4-5 weitere Felder, booleans oder auch BigDecimals.

Dann lese ich die Tickdaten aus und speichere sie in die Klasse CandleStickTick und die schaut so aus:
Java:
    private LocalDateTime date;
    private BigDecimal ask;
    private BigDecimal bid;
Danach mappe ich dann alle Ticks zu den Tagesdaten, die ArrayList wird dann befüllt.

Wenn ich das alles getan habe, habe ich eine Arbeitsspeicherauslastung von über 50GB!
Dann verarbeite ich die Daten in mehreren Threads mit unterschiedlichen Ausgangsparametern.

Problem:
Wieso ist die Auslastung so extrem hoch? Also das 10-fache von der csv Datei? BigDecimal wird vermutlich teuer sein? Wie viel teurer als ein int? Ich habe nicht nur ganze Zahlen könnte aber mit einem entsprechenden Faktor auf eine kommen und somit einen int verwenden, aber wie viel spare ich damit?
Eine Datenbank verwenden? Da ich aber mehrere Threads verwende (8Kerne / 16 Threads) könnte es hier ja zu Problemen kommen? Ich brauche nicht immer alle Ticks...demnach könnte ich dann immer mit einem Statement auf die DB gehen und mir immer nur die Ticks holen die ich aktuell brauche. Läuft das Programm dann aber immer noch so schnell? 1 Thread braucht aktuell ca. 0,5-1 Sekunde. Wenn ich jetzt z.B. an 500 Tagen die Ticks brauche, dann brauche ich ja schon 500 DB Zugriffe in dem Thread. Vermutlich ist das dann deutlich langsamer?

Bevor ich jetzt anfange und ein paar Sachen davon ausprobieren, habe ich hier mal nachgefragt. Vielleicht hat jemand ja noch eine viel bessere Idee :)

Wenn ich die Speicherauslastung runterbekomme, dann kann ich noch mehr Daten auf einmal testen. Alternativ müsste ich mir sonst einen Threadripper kaufen und 128 Gigabyte Speicher. Klar 32 Kerne und 128 Gigabyte Speicher macht es so oder so schneller, aber eventuell bekomme ich ja eine Performance hin, die mit 8 Kernen und 64 Gigabyte auch in Ordnung ist :)

Danke!
 
Kannst du die Daten stattdessen nicht streamen, statt sie ALLE erstmal in den Speicher zu laden und DANN zu analysieren?
Das hangt jetzt davon ab, was du da genau analysierst. Vielleicht musst du auch nur pro Tag buffern und kannst einzelne Tage streamen.
Ich würde zumindest niemals alle Daten zeitgreich im Speicher halten.
 
Na dann füge mal ein paar Beispieldaten hier ins Forum ein ;)

Dann können wir Dir sagen, wie Du sowas wie BigDecimal und LocalDateTime vermeiden tust...

@httpdigest Das KANN manchmal sinnvoll sein... aber sicherlich nicht so
 
@httpdigest Das KANN manchmal sinnvoll sein... aber sicherlich nicht so
Was meinst du denn mit "aber sicherlich nicht so"? Ich hab ja nicht gesagt, WIE er das machen soll. Nur, dass er die Daten streamen soll und mit "streamen" meine ich: Jedes Datum verarbeiten, wenn er es einliest und nicht erst ALLES einlesen und dann ALLES verarbeiten. Solch eine Vorgehensweise ist generell IMMER sinnvoll, wenn es die Operation, die man auf den Daten durchführen möchte, zulässt.
 
K

kneitzel

Und Datenbank Ioannis durchaus sehr sinnvoll sein, auch bei mehreren Threads. Die Hersteller stecken da halt sehr viel KnowHow in Methoden, wie Daten in einem Cache gehalten werden können und wie am gezieltesten die benötigten Daten ausgelesen werden können.

Es sei denn, dass es einmalige Auswertungen sind, denn dann macht es wenig Sinn. Dann wäre tatsächlich der Vorschlag von @httpdigest gut, bei dem die Daten nur einmal beim Lesen ausgewertet wird. So musst Du Dir genau überlegen, welche Daten du brauchst. Wenn du nicht alle Ticks brauchst, dann ist es auch nicht notwendig, die Daten komplett in die Datenbank zu übernehmen.

(Und es kann natürlich auch Mix sein: beim Lesen findet direkt eine Vorverarbeitung statt um dann eine konsolidierte Datenbasis zu haben für die weitere Analyse.
 
Beispieldaten kommen später.
Das mit dem Streamen wird glaube ich nicht funktionieren. Wenn ich alle Daten im Speicher habe, lasse ich parallel auf 15 Threads den gleichen Programmcode laufen nur mit unterschiedlichen Ausgangsparametern.
Beispiel: Das heißt Thread 1 läuft über alle Daten rüber und schaut was passiert wäre, wenn nach 10€ Buch-Gewinn der Gewinn realisiert worden wäre, Thread 2 prüft das gleiche mit 15€ Gewinn usw.
Da es noch an mehr Bedingungen hängt, kann es sein das Thread 1 die Ticks von Tag 5 nicht braucht, aber Thread 2 schon.
Vorher raus zubekommen ob bestimmte Ticks von gar keinem Thread benötigt werden, ist nicht möglich.
 
Wenn die Streaming-Lösung mit einem Thread funktioniert, sollte das grundsätzlich auch mit mehreren Threads funktionieren - entweder weiter alle "Auswertungen" parallel mit Threads oder aber seriell hintereinander.
 
Ich würde das blockweise verarbeiten. Von mir aus kann ein Block einen Tag beinhalten. Dann kann ich die Ticks für den Tag laden, auswerten und in eine Datei schreiben. Damit soll eine Verringerung der Datenmenge erreicht werden.
 
K

kneitzel

Also ich würde die Raten, dich mit der Mathematik im Detail auseinander zu setzen, damit du die Daten so komprimiert bekommst, dass Du die Werte, die zu den gewünschten Berechnungen notwendig sind, in komprimierter Form vorhalten kannst.

Oder Du brauchst tatsächlich einen Weg, bei dem Du die Daten in einem Weg liest und parallel die x Berechnungen durchführst (Da muss es aber eine triviale Berechnung sein).

Aber ohne Details, was du genau wie berechnen willst, wird Dir niemand Vorschläge machen können. Und selbst wenn: nicht ohne Grund sitzen da oft Mathematiker und arbeiten an diesen Berechnungen.... Es geht dabei ja nicht nur um 1:1 Umformungen sondern auch um Vereinfachungen mit Bestimmung der maximalen Abweichung und so. Wenn einen nur die ersten 2 Nachkommastellen interessieren, dann ist es egal, wenn das Ergebnis um bis zu 0,005 abweicht vom korrekten Ergebnis.

Aber das ist ein Bereich, in dem ich bisher auch noch nicht aktiv war und das ich nur von Erzählungen von Kollegen kenne.

Ansonsten ist die Möglichkeit tatsächlich, die Daten, die man verarbeiten will, komplett zu nutzen und da massiv drüber zu gehen. Da ist aber die Frage tatsächlich, ob die Ressourcen ausreichen, die da sind. Für vieles gibt es Lösungen:
Auf große Datenmengen mit ‚wenig‘ Hauptspeicher zugreifen: Datenbanken!
Zu viele Klassen pro Datensatz kann problematisch sein, da ja deutlich mehr gespeichert werden muss als nur der eigentliche Wert (das war im ersten Post ja Dein Problem!)
Aber auf die Daten muss man ja auch zugreifen. Somit braucht man gewisse Datenstrukturen....

Aber ohne Details ist das ein raten. Du willst Hilfe, dann bist Du in der Bringschuld. Hinweise zu theoretischen Möglichkeiten hast Du bekommen.
 
Erst mal danke für die Meinungen. Ich merke schon ich muss mehr Fleisch an den Knochen bringen, damit mich alle verstehen.
Die csv Dateien die ich verarbeite sehe wie folgt aus:
1. 1 Stunde Kerzen:
Code:
2014070110:00:00,1.36921,1.36928,1.36845,1.36898
2014070111:00:00,1.369,1.36972,1.3679,1.36938
2014070112:00:00,1.3694,1.36966,1.3687,1.36889
....
2. Ticks:
Code:
2018110109:00:00:086,1.13603,1.13605
2018110109:00:00:594,1.13601,1.13605
2018110109:00:00:916,1.13593,1.13597
...
Und werden dann in die Objekte gepackt (siehe oben).
Die 1 Stunden Kerzen sind kein Problem, da habe ich nur eine Auslastung von ca. 100mb.
Ja bei den Ticks brauche ich kein BigDecimal, das werde ich mal als erstes ausprobieren wie es dann aussieht. Das Date brauche ich aber, damit ich die Ticks zu den Stunden mappen kann. Klar ich kann mir das auch aus den Strings rauslesen, aber ob das performanter ist bzw. weniger Speicher frisst? Es ist auch gar nicht so wichtig wie lange es dauert bis ich alle Werte im Speicher habe. Wenn ich aktuell die csv Dateien auslese und die Ticks zu den Stunden mappe dauert das ~3-5 Minuten, da kann man ganz gewiss auch etwas optimiere, aber darum geht es mir erst mal nicht!

Wenn ich alles eingelesen habe erweitere ich das 1 Stunden Objekt mit ein paar zusätzlichen Parametern / Werten. Die bleiben aber für jeden Testlauf gleich!
Dann durchlaufe ich mehrere for schleifen und setze über diese die verschiedenen Parameter mit denen der Testlauf durchgeführt werden soll:
Java:
            for (int prozentDcAbstand = 1; prozentDcAbstand <= 25; prozentDcAbstand = prozentDcAbstand
                                                                                      + 1) {

                prozentDcAbstandBd = new BigDecimal(prozentDcAbstand
                                                    * 0.01).setScale(2,
                                                                     RoundingMode.HALF_UP);

                for (int mindAbstandAbsolut = 3; mindAbstandAbsolut <= 10; mindAbstandAbsolut++) {

                    RunGenerateCandleSticks2.mindAbstandAbsolut = mindAbstandAbsolut;

                    for (int faktorHalfTp = 0; faktorHalfTp <= 10; faktorHalfTp++) {

                        if (faktorHalfTp == 10) {
                            faktorHalfTpDouble = 1;
                        } else {
                            faktorHalfTpDouble = new BigDecimal("0."
                                                                + faktorHalfTp).doubleValue();
                        }

                        for (int faktorFirstTp = 0; faktorFirstTp <= 10; faktorFirstTp++) {
                       
                        ....
Da kommen noch mehr for-Schleife deswegen kürze ich das hier ab.
Mit "mindAbstandAbsolut", "faktorHalfTp", "faktorFirstTp",... starte ich dann den Testlauf in einem seperaten Thread max. 16 Stück, wenn alle 16 Threads am arbeiten sind warte ich bis einer fertig ist und dann legt er wieder mit den nächsten Parametern los.

In dem Testlauf durchlaufe ich dann mit einer for-schleife alle 1 Stunden Kerzen und prüfe dann ob ich mit den übergegebenen Parametern in dieser Stundenkerze in einen Trade eingestiegen wäre, wenn ja durchlaufe ich die Ticks und hole mir dann den Preis wo ich eingestiegen wäre. Wenn Trades offen sind werden auch die Ticks geprüft wann ich wieder mit Gewinn oder Verlust ausgestiegen wäre. Durch die verschiedenen Parameter aus den for Schleifen verändern sich die Ein-und Ausstiege. Zum Schluss werden alle geschlossenen Trades zusammengerechnet und ein Ergebnis auf der Console ausgegeben. Dieses Ergebnis aus der Console kopiere ich dann manuell heraus und verarbeite es in Excel weiter (meist nach ein paar Stunden, wenn ich über 100.000 Ergebnisse habe). Ein Testdurchlauf dauert 0,5-1Sekunde pro Thread. Damit bekomme ich schon jede Menge Ergebnisse in ein paar Stunden zusammen. Wenn ich alle for-Schleifen zusammenrechne komme ich auf über 1 Million verschiedene Möglichkeiten...aber in bestimmten Bereichen breche ich ab und schärfe die Parameter nach.

Den gesamten Code zu posten sind weit über 1000 Zeilen, glaube nicht, dass das viel helfen würde.
Ist jetzt klar geworden wie ich vorgehe? Aktuell ist es so, dass ich einen Zeitraum von 4 Jahren abdecken kann. Ziel wäre mind. 8 Jahre abzudecken und dafür müsste ich die Speicherauslastung runterbekommen.
Wenn sich jeder Thread z.b. immer nur die Daten für einen Monat streamt, dann muss jeder Thread 48 mal für jeden Durchlauf sich die Daten von der Platte streamen. Ich vermute einen Monat einzulesen inkl. Ticks wird schon fast so lange dauern wie jetzt ein Durchlauf. Das gleiche bei einer Datenbank. Ziel ist die Speicherauslastung runterzubekommen, aber ein Testdurchlauf darf dadurch nicht wesentlich länger dauern! Deswegen kann es in meinen Augen nur funktionieren wenn alle Daten im Speicher sind. Alles andere würde das Programm verlangsamen.
Es sei denn ich habe den komplett falschen Ansatz!
Wenn ich nur die Daten aus den csv Dateien + Mapping gemacht habe, habe ich eine Auslastung von 50Gb, was dann danach kommt frisst vielleicht noch 2-3Gb aber das ist erst mal egal. Ist die ArrayList in dem Stunden Objekt wo alle Ticks drin hängen vielleicht auch sehr teuer?
Nach den BigDataTools habe ich noch nicht geschaut, werde ich mir aber mal anschauen.

EDIT: Ok ganz ohne LocalDate und mit float anstatt BigDecimal bin ich nach dem Auslesen bei 16Gb :)
 
Zuletzt bearbeitet:
Nur um zu gucken, ob ich das korrekt verstanden hab:
* Die zig Schleifen dienen nur dazu, einige "Testläufe" zu erstellen.
* Alle Testläufe sind unabhängig voneinander
* Ein Testlauf steigt bei bestimmen Werten ein/aus, ermittelt also, wie "gut" er für den jeweiligen Kursverlauf abgeschnitten hat?


Unter den Annahmen spricht nicht wirklich etwas gegen den Streaming-Ansatz. Ein Testlauf braucht (korrigier mich wenn ich falsch liege) nie alle Daten auf einmal, sondern nur Tick zu Zeitpunkt t_0, danach t_1, t_2, ..., t_n, (und u.U. noch (gleitender) Durchschnitt/min/max/sonstiges?).
Streaming heißt ja nicht, dass du das monatsweise einliest und die Datei dabei X mal einlesen muss - man würde das weiterhin einmal einlesen, aber eben nicht den gesamten Inhalt auf einmal im Speicher halten.
 
Yepp. Der einzige Grund, warum man `N` Daten im Speicher haben _müsste_, wäre, wenn bei einer Berechnung ein Datensatz an Stelle 0 von einem Datensatz an Stelle `N` abhängt (für ein beliebig großes `N`). Das ist aber bei den wenigsten Berechnungen der Fall.
Falls `N` aber eben nicht _alle_ Daten beinhaltet, sondern lediglich kleinere Chunks, wie zum Beispiel die jetzt das erste Mal angesprochenen "Tage" mit ihren "Kerzen", dann braucht man lediglich eine Puffergröße von `N`.
Auf der anderen Seite wollen aber die Speicherchip-Hersteller auch Geld verdienen. Und mit mehr Metall/Hardware konnte man noch immer jedes Problem erschlagen - und im Zweifel ist ein weiterer 64GB Riegel im Rechner günstiger als die Arbeitszeit, die ein besserer Algorithmus an Kosten verursachen würde. :)
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben