Große Datenmengen effizient programmieren

Fabse

Aktives Mitglied
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!
 

httpdigest

Top Contributor
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.
 
X

Xyz1

Gast
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

Top Contributor
@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

Gast
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.
 

Fabse

Aktives Mitglied
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.
 

mrBrown

Super-Moderator
Mitarbeiter
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.
 

AndiE

Top Contributor
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

Gast
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.
 

Fabse

Aktives Mitglied
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:

mrBrown

Super-Moderator
Mitarbeiter
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.
 

httpdigest

Top Contributor
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. :)
 

Fabse

Aktives Mitglied
Super danke für die Antworten! Ja mrBrown du hast es richtig verstanden und ich habe somit den Streaming Ansatz noch nicht verstanden. Ich werde mich einlesen und wenn ich dann noch fragen habe, werde ich mich wieder melden.
 
X

Xyz1

Gast
BigDecimal, LocalDateTime und Objekte sind für so etwas zu teuer. Anstatt float kannst du auch int nehmen. Du brauchst nicht zu jedem Zeitpunkt alle Daten im Speicher. Schreibe Dir einen Stream dafür. ;)
 
X

Xyz1

Gast
Zum Bleistift so:
Java:
import java.util.Arrays;
import java.util.Iterator;
import java.util.Random;
import java.util.Scanner;
import java.util.StringJoiner;

public class Aktien implements Iterator<int[]> {
	Strings1 s1 = new Strings1();
	String[] s = new String[1000];
	int i = 0;
	int j = 0;

	@Override
	public boolean hasNext() {
		return i < j || s1.hasNext();
	}

	@Override
	public int[] next() {
		if (i == j) {
			i = 0;
			while (i < s.length && s1.hasNext()) {
				s[i++] = s1.next();
			}
			j = i;
			i = 0;
		}
		String[] split = s[i++].split(",");
		int[] l = new int[] { 
				Integer.parseInt(split[0].split(":")[0]), 
				(int) (Float.parseFloat(split[1]) * 100000),
				(int) (Float.parseFloat(split[2]) * 100000), 
				(int) (Float.parseFloat(split[3]) * 100000),
				(int) (Float.parseFloat(split[4]) * 100000) };
		return l;
	}

	public static void main(String[] args) {
		new Aktien().forEachRemaining(l -> System.out.println(Arrays.toString(l)));
	}
}

class Strings1 implements Iterator<String> {
	String s = "2014070110:00:00,1.36921,1.36928,1.36845,1.36898\r\n"
			+ "2014070111:00:00,1.369,1.36972,1.3679,1.36938\r\n" 
			+ "2014070112:00:00,1.3694,1.36966,1.3687,1.36889";
	Scanner sca;

	Strings1() {
		Random r = new Random();
		StringJoiner j = new StringJoiner("\n");
		for (int i = 0; i <= 1111; i++) {
			j.add(2014070110 + i + ":00:00," 
					+ (int) (r.nextFloat() * 100000) / 100000f + ","
					+ (int) (r.nextFloat() * 100000) / 100000f + "," 
					+ (int) (r.nextFloat() * 100000) / 100000f + ","
					+ (int) (r.nextFloat() * 100000) / 100000f );
		}
		s = j.toString();
		sca = new Scanner(s);
	}

	@Override
	public boolean hasNext() {
		return sca.hasNextLine();
	}

	@Override
	public String next() {
		return sca.nextLine();
	}
}


Statt meiner super tollen Klasse Strings1 nimmst Du an der Stelle s[i++] = s1.next(); einfach einen FileReader o.Ä.
 

AndiE

Top Contributor
Java:
public static void main(String[] args) {
        new Aktien().forEachRemaining(l -> System.out.println(Arrays.toString(l)));
    }

Wie soll diese Zeile funktionieren?

Es ist kein Konstruktor deklariert. Selbst wenn ich annehme, dass dennoch ein verwei auf ein Aktion-Ob´jekt übergeben wird, werden zwar hasNext zwei mal implementiert, aber die Methode "forEachRemaining" des gleichen Interfaces aber nicht. Selbst wenn die Variable l vom Typ "int[]" ist, wie komme ich dann zu den Strings?

Das mit dem "Scanner sca" kann ich auch nicht so richtig nachvollziehen.

Wahrscheinlich habe ich zu wenig ahnung von funktionaler Programmierung, aber ich finde das so nicht so richtig nachvollzieh- und wartbar.
 

mrBrown

Super-Moderator
Mitarbeiter
Wie soll diese Zeile funktionieren?

Es ist kein Konstruktor deklariert. Selbst wenn ich annehme, dass dennoch ein verwei auf ein Aktion-Ob´jekt übergeben wird, werden zwar hasNext zwei mal implementiert, aber die Methode "forEachRemaining" des gleichen Interfaces aber nicht. Selbst wenn die Variable l vom Typ "int[]" ist, wie komme ich dann zu den Strings?
"Es ist kein Konstruktor deklariert"-> default Konstruktor
"werden zwar hasNext zwei mal implementiert" -> nur einmal je Klasse ;)
"aber die Methode "forEachRemaining" des gleichen Interfaces aber nicht" -> ist eine default-Methode, muss also nicht implementiert werden
"Selbst wenn die Variable l vom Typ "int[]" ist," -> ist sie :)
"wie komme ich dann zu den Strings" -> zu welchen String, denen, die ausgegeben werden? Das macht doch genau Arrays.toString

Wahrscheinlich habe ich zu wenig ahnung von funktionaler Programmierung, aber ich finde das so nicht so richtig nachvollzieh- und wartbar.
Das liegt in diesem Fall nicht an Funktionaler Programmierung, sondern an grottenschlechtem Code :)
 
X

Xyz1

Gast
Das sind quasi zwei Puffer, wobei er dann für Berechnungen noch einen dritten Puffer bräuchte.

Es ist kein Konstruktor deklariert
Na und? default

Selbst wenn die Variable l vom Typ "int[]" ist, wie komme ich dann zu den Strings
Im Consumer, nicht Supplier...

Wahrscheinlich habe ich zu wenig ahnung von funktionaler Programmierung
Green check mark ✅ Aber kein Grund zur Besorgnis.
 

mrBrown

Super-Moderator
Mitarbeiter
Ansonsten, zum Kürzen reduziert auf die Ticks mit 2 Werten statt mit 4:

Java:
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.*;

public class StreamingExample {

    static class Tick {
        //FALLS das wirklich noch ein Problem ist, kann man es ersetzen, zB mit epochSeconds/NanoSecods
        LocalDateTime localDateTime;
        int ask;
        int bid;
        public Tick(final LocalDateTime localDateTime, final int ask, final int bid) {
            this.localDateTime = localDateTime;
            this.ask = ask;
            this.bid = bid;
        }
    }

    //Wie auch immer die Testläufe aussehen
    private static class Testlauf {
        int max = Integer.MIN_VALUE;

        void processTick(Tick tick) {
            max = Math.max(tick.ask, max);
        }
    }

    public static void main(String[] args) {
        Testlauf testlauf = new Testlauf();
        readFile()//Ersetzen mit Files.lines()
                .map(StreamingExample::parseLine)
                .forEach(testlauf::processTick);
        
        System.out.println(testlauf.max);
    }

    static Stream<String> readFile() {
        long epochSeconds = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
        final Random random = new Random();
        return IntStream.range(0, 1111).mapToObj(
                i -> String.format("%s,%.5f,%.5f",
                                   LocalDateTime.ofEpochSecond(epochSeconds + i, 0, ZoneOffset.UTC),
                                   random.nextFloat(),
                                   random.nextFloat())
        );
    }

    static Tick parseLine(String line) {
        String[] columns = line.split(",");
        return new Tick(LocalDateTime.parse(columns[0]),
                        (int) (Float.parseFloat(columns[1]) * 100000),
                        (int) (Float.parseFloat(columns[2]) * 100000));
    }

}
 
X

Xyz1

Gast
statische innere Klassen sind ein Unding,
unnötige Objekte,
hatten wir nicht über LocalDateTime gesprochen?
Note: 6, von mir. :)

Bearbeitung: Achja - das hat natürlich nicht dieselbe Funktionalität.
 

mrBrown

Super-Moderator
Mitarbeiter
statische innere Klassen sind ein Unding,
Mehrere Klasse pro Datei auch - einigen wir uns darauf, dass es einfach dem Format hier geschuldet ist und man in beiden Fällen mehrere Dateien mit Klassen hätte?

unnötige Objekte,
Da Java keine structs oder ähnliches bietet, muss man wohl Objekte nutzen. Arrays sind kein wirklicher Ersatz für Objekte, vor allem nicht in Hinblick auf Verständlichkeit und Erklärungen, worum es hier ja ging.

hatten wir nicht über LocalDateTime gesprochen?
Nein, nicht wir, nur du. Und weil du LocalDateTime hier nicht als sinnvoll ansieht, steht dort extra ein Kommentar genau dazu.

Bearbeitung: Achja - das hat natürlich nicht dieselbe Funktionalität.
Ich weiß. Allerdings würde ich die gleiche Funktionalität in deinen Augen wohl nur erreichen, wenn es genau der selbe Code ist.

Oder welche großen Unterschiede gibt es in der Funktionalität?
Die Reduzierung auf zwei statt 4 Worte? Ist erwähnt, der Kürze des Programms geschuldet und für die Veranschaulichung völlig unerheblich.
Hinzufügen von Testlauf statt einfach nur Ausgabe aus der Konsole? Dient der Verbesserung der Verständlichkeit im Kontext dieses Threads, wenn es dich stört, denk dir dort einfach einen entsprechendes println.


Falls die relevante Funktionalität, die dein Code zeigen soll, ist, wie man Arrays ausgibt - ja, dann ist das wirklich keine vergleichbare Funktionalität.
 
X

Xyz1

Gast
Da Java keine structs oder ähnliches bietet, muss man wohl Objekte nutzen. Arrays sind kein wirklicher Ersatz für Objekte, vor allem nicht in Hinblick auf Verständlichkeit und Erklärungen, worum es hier ja ging
Es ging hier um große Datenmengen, nicht besonders leserlicher Code. Und structs sind nebenbei unleserlicher als Array.

Wenn du zu derselben Lösung gelangt wärst, wieso spricht du dann von "grottenschlechten Code"? Das ist einfach das was man machen sollte. Das andere ist syntaktischer Zucker.

(So ähnlich wie eine Himbeere - schmeckt gut, hat aber keinen hohen Nährwert.)
 

mrBrown

Super-Moderator
Mitarbeiter
Es ging hier um große Datenmengen, nicht besonders leserlicher Code. Und structs sind nebenbei unleserlicher als Array.

Wenn du zu derselben Lösung gelangt wärst, wieso spricht du dann von "grottenschlechten Code"? Das ist einfach das was man machen sollte. Das andere ist syntaktischer Zucker.
Okay, ich geh mal einen Schritt auf dich zu.

Es gibt dabei wohl zwei Streitpunkte: Lesbarkeit und Effizienz (und letztere in Geschwindigkeit und Speicher-Verbrauch) - zumindest gehst du auf die beiden in dem Beitrag ein.

Wir können ja mal versuchen, das möglichst objektiv zu vergleichen, (bzw, bei Lesbarkeit so gut es eben geht).


Wenn ich deinen Code richtig verstehe, ist das int-Array der Länge Fünf die Entsprechung für folgende Klasse:

Java:
public class Tick {
    int epochSeconds;
    int open;
    int high;
    int low;
    int close;
    //Boilerplate bewusst weggelassen
}

epochSeconds ist bewusst als int gewählt, damit es dem int-Array entspricht.
Die Beispieldaten des TO haben Millisekunden-Auflösung, man würde also eher einen long oder eben einen andere entsprechenden Datentyp wählen (oder das nur als Millisekunden innerhalb des Tages darstellen, könnte auch möglich sein). Würde aber bei einem Vergleich von Array und Klasse dazu führen, dass man entweder ein long-Array nutzen müsste (wodurch das doppelt so groß wäre, was ein Nachteil fürs Array wäre), oder Array und Klasse hätten unterschiedliche Wertebereiche (was ein unfairer Vergleich wäre, weil nicht mehr fachlich gleich). Deshalb in beiden alles als int.

Aber zurück zur Frage: wäre das eine passende Repräsentation der Daten als Klasse?
 

mrBrown

Super-Moderator
Mitarbeiter
Ja, wäre es... Aber dann genügt es aus Platzgründen auch ein Array.
Okay, vergleichen wir doch mal den Platzbedarf (Werte sind für 32-Bit*):

* Objekte haben einen 8-Byte Header, dann kommen in diesem Fall fünf mal 4 Byte für die ints hinzu, macht insgesamt 28 Byte.

* Arrays haben eine 12-Byte Header, dazu dann fünf mal 4 Byte für die ints, macht insgesamt 32 Byte.

Beide Werte müssen noch auf ein Vielfaches von 8 aufgerundet werden, macht 32 Byte vs. 32 Byte - Platzgründe sprechen also nicht für ein Array.

Stimmst du mir da zu oder siehst du da Fehler in meiner Rechnung?
Quelle: https://wiki.openjdk.java.net/display/HotSpot/CompressedOops
Fehler gern korrigieren :)

32-Bit:

Der Header besteht aus zwei mal 4-Byte, beim Array zusätzlich 4-Byte für die Array-Länge.

64-Bit:

Objekte und Arrays haben jeweils zwei mal 8-Byte Header, beim Array zusätzlich 4 Byte für die Array-Länge und 4 Byte Padding.
Für Objekte macht 16 Byte für Header und 20 zusätzlich für Felder, mit Padding 40 Byte.
Für Arrays macht das 24 Byte Header, 20 für Daten, mit Pudding also 48 Byte.

64-Bit mit compressed oops:
Der Header besteht aus 8+4 Byte, die Array-Länge rutscht in die 4 Byte, die frei im Vergleich zu 64-Bit sind, das erste Feld des Objekts kann auch in die 4 freien Byte rutschen.
Macht also:
Für Arrays: 16 Byte + 20 für Daten, mit Padding 40 Byte
Für Objekte: 16 Byte (in denen das erste Feld schon enthalten ist) + 16 Byte (für die 4 restlichen Felder), also 32 Byte

Bleibt noch Performance in Bezug auf Zeit und Lesbarkeit als Möglichkeiten für den Vergleich, richtig?

epochSeconds; könnte man ja sogar noch "kürzen", also wenn alle Sekunden-Anzahlen größer gleich eines bestimmten Werts wären...
Kleiner als ein int wird man es aber nicht bekommen, bzw hätte es keinen wirklichen Vorteil (auf Seiten des int-Arrays braucht man sowieso ints, im Objekt könnte man den int bei gleicher Größe durch einen long ersetzen).
Oder wie würdest du das anders als mit einem int darstellen?
 

AndiE

Top Contributor
Ich bin da noch nicht so glücklich. Nehmen wir mal an, die einen Daten sind 2,5 Mega und die anderen 5000 Megabyte. Nehme ich mal weiter an, dass ich beim ersten 50 Byte brauche( rechnet sich leichter), dann sind das 50000 Datensätze, und das andere, da ja auch nu aus 3 int+DateTime bestehend(2*4)+8=16 Byte. Da sind dann ca. 3 Millionen Datensätze. In dem Falle sind es zwar nur 60 Datensätze je Tick(einer je Minute), aber die Beispielzahlen lassen auf bis zu 10000 Datensätze je Tick( 3 je Sekunde) schließen. Theoretisch ist das alles machbar, da die Begrenzung der Größe für Arrays bei 2 Milliarden liegt. Dennoch würde ich die Pufferung nicht an das System abgeben, sondern selbst machen.

Wieso?

Ich bin immer noch eher in der Auffassung verhaftet, dass man nur über Dinge iterieren sollte, die im Speicher sind. Jedenfalls bei Java. Es gibt zwar Streamdeitoren, die würde ich aber nicht typisch für das Java-Konzept sehen.
 

mrBrown

Super-Moderator
Mitarbeiter
3 int+DateTime bestehend(2*4)+8=16 Byte
Soll das eine Rechnung auf Java-Ebene sein? Da muss man die gänzlich anders berechnen, sie zB meinen Beitrag, der sich mit deinem überschnitten hat ;)



Dennoch würde ich die Pufferung nicht an das System abgeben, sondern selbst machen.
In welchem Beispiel siehst du Pufferung durchs System?
In meinem vielleicht impliziert durch den Stream, aber auf der Ebene würde man sowieso nicht selbst puffen. In dem anderen Beispiel könnte man es als Puffern bezeichnen, allerdings hat das nicht wirklich was mit Streaming zu tun, wenn alles in ein Array gelesen wird, bevor was anderes passiert.


Ich bin immer noch eher in der Auffassung verhaftet, dass man nur über Dinge iterieren sollte, die im Speicher sind. Jedenfalls bei Java. Es gibt zwar Streamdeitoren, die würde ich aber nicht typisch für das Java-Konzept sehen.
Alle InputStreams und Reader in Java funktionieren so - sie lesen nur jeweils einen begrenzten Teil und nicht ganze Dateien in einem Rutsch (außer die Datei ist klein genug, zB kleiner als der Puffer beim BufferedInputStream)
 

Fabse

Aktives Mitglied
Ich sage schon mal vielen Dank für die lebhafte Diskussion, da kann jeder etwas lernen ;)
Feedback von mir wird aber dauern, da ich aktuell nicht allzu viel zeit habe....
Aber das hindert euch natürlich nicht hier weiter zu diskutieren :)
 

mrBrown

Super-Moderator
Mitarbeiter
Überprüft doch einfach mal die Speichernutzung:
Und da kommt dann was bei raus?

Das Objekte in diesem Fall nicht mehr, eher weniger, Platz brauchen als Arrays, hab ich doch extra detailliert vorgerechnet.

Wenn dir das nicht reicht gibts auch noch 'nen Benchmark dazu (im wesentlichen dein Code, und int[] durch Tick ersetzt), kommt das bei raus:

Code:
Benchmark                                                  Mode  Cnt        Score        Error   Units
AktienBenchmark.intArray                                   avgt   20  1356949,644 ±  18142,629   ns/op
AktienBenchmark.intArray:·gc.alloc.rate                    avgt   20     1016,320 ±     17,345  MB/sec
AktienBenchmark.intArray:·gc.alloc.rate.norm               avgt   20  3401686,609 ±    151,942    B/op
AktienBenchmark.tick                                       avgt   20  1329285,886 ±   9449,018   ns/op
AktienBenchmark.tick:·gc.alloc.rate                        avgt   20     1039,001 ±     10,755  MB/sec
AktienBenchmark.tick:·gc.alloc.rate.norm                   avgt   20  3392785,908 ±    170,487    B/op

Mit Objekten also nicht nur theoretisch, sondern auch real besser.
Der Unterschied zwischen beiden sind (Fehler außer acht gelassen) in diesem Fall 8.900 Byte, was ziemlich genau dem rechnerischen Unterschied von 8896 Byte entspricht, die fehlenden 6 Byte sind der Messungenauigkeit geschuldet.


Und mit dem Benchmark hat man auch gleich die Laufzeiten verglichen, mit Objekten braucht weder mehr Speicherplatz noch mehr Zeit als mit Arrays :)
 

mrBrown

Super-Moderator
Mitarbeiter
Ich weiß gar nich was du da getestet hast, aber bestimmt nicht das richtige

Benchmark findest du hier: https://gitlab.com/mrBrown/benchmarks

Wie gesagt, entspricht deinem Code, nur das int[] durch Tick ersetzt wurde. Der Benchmark ist sicher nicht der beste, du darfst den also gerne verbessern :)
Für diese Problemstellung ist er aber vermutlich gut genug, da der Code in beiden nahezu äquivalent ist, und das Ergebnis weicht auch nicht vom erwarteten ab.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
R Dateigestützte Collection für große Datenmengen Allgemeine Java-Themen 5
G Große Datenmengen per JDBC Allgemeine Java-Themen 5
B Welcher Datentyp für sehr große Zahlenbereiche? Allgemeine Java-Themen 1
N Das große O berechnen Allgemeine Java-Themen 2
F Best Practice Große Anzahl an Objekten speichern und lesen Allgemeine Java-Themen 19
R Große Zahlen in Worten abkürzen Allgemeine Java-Themen 10
K Große JSON-Dateien schnell und effizient verarbeiten Allgemeine Java-Themen 16
K Große Mengen an Daten speichern Allgemeine Java-Themen 9
VfL_Freak Große und seltsame Probleme nach Java-Update auf V1.8.0_91 Allgemeine Java-Themen 3
E Best Practice Verdammt große Objekte Allgemeine Java-Themen 10
P Große Datenstruktur im Speicher halten Allgemeine Java-Themen 13
M Einfluss von Caching auf die Performance (große Arrays) Allgemeine Java-Themen 24
U Große Liste von Strings mit indiziertem Zugriff Allgemeine Java-Themen 31
P Datentypen Große Datenmenge Sortiert halten Allgemeine Java-Themen 12
D große Textdatei filtern Allgemeine Java-Themen 13
M Große Datei mit Regex durchsuchen Allgemeine Java-Themen 4
R POI große Exceldatei schreiben Allgemeine Java-Themen 7
K Scanner - große Textfile, nur 0 ab betim. Wert Allgemeine Java-Themen 4
trash Das große Problem: .jar Archiv Allgemeine Java-Themen 19
J Große Datei einlesen und gestückelt verarbeiten Allgemeine Java-Themen 4
I Große Datei am effektivsten/performantesten auslesen und auswerten? Allgemeine Java-Themen 6
S große CSV-Dateien Importieren. Beste Lösung ?! AWS,S3,Hadoop!? Allgemeine Java-Themen 4
P große double Zahlen und modulo Allgemeine Java-Themen 8
O Große Anzahl Bilder laden Allgemeine Java-Themen 7
A Mit RegEx große Dokumente erfassen Allgemeine Java-Themen 14
X Wie verdammt große Datein öffnen? Allgemeine Java-Themen 2
P Große Datenmenge wie speichern (HashMap? TreeMap?) Allgemeine Java-Themen 11
G Große XML-Dateien einlesen und auswerten . Allgemeine Java-Themen 2
P Performance: Ziehen ohne Zurücklegen (große Datenmenge) Allgemeine Java-Themen 10
I JNI - Große Daten übertragen Allgemeine Java-Themen 6
T Große Dateibestände löschen - Speicherproblem Allgemeine Java-Themen 20
S Große ArrayListen Allgemeine Java-Themen 8
S große Datei einlesen! Allgemeine Java-Themen 7
J Große Zahl (double) as text ausgeben? Allgemeine Java-Themen 2
S Kleines Eclipse Problem, große Wirkung Allgemeine Java-Themen 7
H Referenzen statt Objekte für große Speicherstrukturen Allgemeine Java-Themen 19
K Große Herausforderung Allgemeine Java-Themen 2
F Zu große Werte beim byteweisen Lesen mit BufferedReader.read Allgemeine Java-Themen 5
D Große Klasse - was fällt euch so ins Auge? Kritik bitte! Allgemeine Java-Themen 10
M Große Dateien laden Allgemeine Java-Themen 2
F Große Dateien schnell einlesen Allgemeine Java-Themen 14
B Gemeinsamkeiten großer Datenmengen Allgemeine Java-Themen 4
S Performante Visualisierung groẞer Datenmengen (ohne JFreeChart) Allgemeine Java-Themen 22
Fu3L Input/Output Brauche Rat bei effizienter Speicherung großer Datenmengen Allgemeine Java-Themen 21
S Datenbank Abfragen mit großen Datenmengen Allgemeine Java-Themen 22
A Großes Problem mit dem Lesen großer Datenmengen Allgemeine Java-Themen 16
T Daten effizient verwalten Allgemeine Java-Themen 4
H Sehr viele Threads effizient Verwalten Allgemeine Java-Themen 13
Dragonfire Datentypen Map effizient mit eigenem Key Allgemeine Java-Themen 71
T [RXTX] GPS-Maus (Comport) effizient auslesen Allgemeine Java-Themen 6
B Daten effizient ein- und auslagern Allgemeine Java-Themen 7
S Algorithmus Problem. Rechtecke effizient auf Spielfeld anordnen. Allgemeine Java-Themen 7
Ark Arkussinus effizient berechnen Allgemeine Java-Themen 12
T WeakHashMap: Wie "null" effizient abfangen? Allgemeine Java-Themen 5
J Datei Inhalt vergleichen (schnell & effizient!) Allgemeine Java-Themen 10

Ähnliche Java Themen

Neue Themen


Oben