Entwickler-Ecke

Basistechnologien - sehr große Variable


m.keller - Mi 22.04.15 15:42
Titel: sehr große Variable
Hallo ihr Programmiererfreaks,

ich habe ein großes Problem in C# und suche recht lange schon nach einer Lösung.

Ich brauche eine Variable die mir ca. double [400 Millionen] darstellt wo ich meine Werte einfügen kann.
Leider bekomme ich alleine beim initialisieren "OutOfMemmoryException"
Es muss doch irgendwie einen weg geben eine Variable teilweise noch größer zu bekommen oder etwa nicht?

Habt ihr eine Idee unter welchem Stichwort ich da suchen muss oder was ich verwenden kann?

Danke schon ein mal im Voraus.


Th69 - Mi 22.04.15 16:07

Hallo,

brauchst du wirklich so ein großes Array am Stück? Oder reicht auch ein Dictionary<intdouble>, wo du eben nur die benötigten Werte einträgst?

Ansonsten: verwendest du ein 32- oder 64-bit System (denn 400 Mio * sizeof(double) = 400 Mio * 8 = 3.2 GB)? Außerdem gibt es standardmäßig in .NET ein Größenlimit von 2GB, welches du aber mit .NET 4.5 ausschalten kannst, s. With .NET 4.5, 10 years memory limit of 2 GB is over [http://bhrnjica.net/2012/07/22/with-net-4-5-10-years-memory-limit-of-2-gb-is-over/].


m.keller - Mi 22.04.15 16:24

Danke dir.

mmhhh mir ist jede art recht die sau schnell ist.
Wir wollen es erst ein mal in c# testen (vermutlich viel zu langsam), aber es sollte recht leicht in c++ zu übersetzen sein ansonsten hätte ich auch listen oder so nehmen können.

Die api von wo ich die Werte bekomme ist leider noch eine 32 bit und ich setze ein 64 bit Software auf wo diese eingebunden ist.

Gut zu wissen das man das Limit ausschalten kann. Warum macht man ein Limit in eine Programmsprache?


Ralf Jansen - Mi 22.04.15 17:27

Zitat:
Warum macht man ein Limit in eine Programmsprache?


Spekulation meinerseits, weil es meist aus Dummheit geschieht einen so großen kontinuierlichen Speicherbereich anzufordern? Und die meisten Entwickler glauben wenn es der Compiler/die Laufzeitumgebung nicht verhindert muß es ja was intelligentes sein ;) Eine gute Entwicklungsumgebung/Sprache sollte aktiv verhindern das ein Programmierer Unfug betreibt. Der c++ Weg den Programmierer als mündigen annähern allwissenden Universalgelehrten anzusehen dem man alle Freiheiten und alle Mittel an die Hand gibt führt dann eben zu entsprechenden Problemen. Wen ich da mal Stroustrup zitieren darf "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off. ". Gute Sprachen sollten eher Foolproof sein.

In C# ist es übrigens relativ leicht sich ein Klasse zu schreiben der einen Indexer bereithält und auf anderen Speicherbereiche(andere Klassen wie zum Beispiel Streams) umlenkt. Wenn du vor allem sequentiellen Zugriffe hast wäre das sogar ohne irgendwas über dein konkretes Projekt zu wissen mit allerhöchster Wahrscheinlichkeit die zu präferierende Lösung.


jfheins - Mi 22.04.15 20:36

user profile iconm.keller hat folgendes geschrieben Zum zitierten Posting springen:
Warum macht man ein Limit in eine Programmsprache?


Mein Tipp wäre jetzt gewesen, dass das historisch so gewachsen ist. Im Jahre 2000 hat man halt mal einen Int32 für das Feld "Objektgröße" hergenommen und fertig. Irgendwann gab es dann auch genug RAM, sodass das auch tatsächlich eine Limitierung war.

Also entweder das Limit ausschalten (würde ich wohl machen), oder die Lösung für .net 4 und kleiner: Die Daten aufteilen. Also wenn du 400 Mio Werte hast, tust du die unteren 200 Mio in das eine Array und die oberen 200 Mio eben in ein anderes.


m.keller - Do 23.04.15 08:06

Danke euch, naja das mit dem Indexer hatte ich mir auch schon gedacht.
Aber das in einen Stream zu leiten, ist das nicht viel zu langsam?
Ich bekomme in der Sekunde ca. 4 Millionen Werte die wollen gespeichert und analysiert werden.


Horst_H - Do 23.04.15 09:48

Hallo,

wenn ich bedenke, wie schnell p196 von user profile iconjfheins war, ist doch C# sehr schnell.
Und 4e6*8 Byte = 32 Mb/s auf eine Festplatte wegzuschreiben sollte auch ohne PCIe-SSD möglich sein.Im Cache von Windows ist dann ein Vielfaches davon möglich.Ein stream ist sicher noch schneller aber die Speichergrenzen bei 64-Bit wirst Du wohl kennen.
Oder Memory-Mapped-Files http://www.codeproject.com/Articles/138290/Programming-Memory-Mapped-Files-with-the-NET-Frame

Ich weiß aber nicht, wie aufwändig die Daten verarbeitet werden, ob wirklich alle zeitgleich im Zugriff sein müsssen oder eben abschnittweise und dass man dann nur die Ergebnisse dieser Abschnitte zusammenführt.

Gruß Horst


Ralf Jansen - Do 23.04.15 09:54

Zitat:
Aber das in einen Stream zu leiten, ist das nicht viel zu langsam?


Es kommt drauf an was du für einen Zugriff du auf die Daten brauchst. Wenn du Daten annimmst, anguckst und wegschreibst, rein sequentieller Vorgang, sollte das ausreichend schnell sein. Wenn du gleichzeitig random Zugriff auf die bereits abgelegten Daten brauchst wird es kompliziert das performant zu halten. Wenn die Datenstruktur größer als der verfügbare physische Speicher (egal ob wegen einem künstlichem Limit oder einfach wegen fehlender physischer Verfügbarkeit) ist das dann aber eher eine Sprach/Framework-unabhängig Problematik.


m.keller - Do 23.04.15 10:16

puh.. das scheint ein sehr komplexe Sache zu werden.

Eine Kurze Beschreibung. die Daten hole ich in einem Thread so schnell es geht ab.
Diese lege ich in einem Array in einer anderen Klasse ab (es ist ein art Ringpuffer dafür vorgesehen, leider zu klein), von dort hole ich mit einem anderen Thread blockweise die Werte ab und verarbeite diese. ab da an könnte ich die Menge an werte reduzieren. aber vorher brauche ich alle mögliche um die Block-Auswertung zu gestalten.

Ich hatte schon überlegt ob ich mir in der Klasse ein Logik ein baue die es ermöglicht mehrere Arrays hintereinander zu bekommen.
Problem ist aber, dann bin ich nicht flexible genug von der Größe, lege ich drei Variablen an bin ich darauf beschränkt.

Das Array ist schon recht komplex double[,] (double[Standort,Wert]) somit könnte es auch sein wenn Standort wächst wird es ziemlich schnell ziemlich Groß.

Gruß


Ralf Jansen - Do 23.04.15 11:27

Dann ist wohl eher hilfreich die Blöcke richtig (frühzeitig) so zu schneiden das man handhabbare Blöcke bekommt die auch auch als solcher Block weiterverarbeitet werden können. Der Gedanke mit Streams ist dann vermutlich eher weniger hilfreich oder wenn dann nur eine sekundäre Überlegung wert wenn du tatsächlich einen Buffer zwischen Empfang und Weiterverarbeitung brauchst der potentiell Größer als der physisch verfügbare Ram ist.

Nebenbei in einem Jagged Array (double[][]) wäre jede Dimension ein eigenes Array mit jeweils eigenem Speicherlimit im Gegensatz zu einem mehrdimensionalen Array double[,]. Wieviele Werte sind den in der Werte Dimension?


m.keller - Do 23.04.15 12:02

sobald die ersten Daten vorhanden sind hole ich mir Passende Blöcke und gebe den Puffer wieder in dem Bereich frei. Damit es etwas Handelbar bleibt.

Die Dimension Werte entspricht den geforderten 400 Millionen, die Dimension Standort könnte bis zu 16 annehmen.
Und das ist schon ein Haufen Daten.
Und so wie ich Vermute müssen die schleifen zum Analysieren verdammt schnell sein um den Puffer frei zu bekommen.


Horst_H - Do 23.04.15 13:09

Hallo,

da stellt sich die Frage, ob bei 400e6 Daten pro Standort==100 Sekunden und nur 4e6 Daten/s diese überhaupt in Echtzeit verarbeitet werden müssen.Mach doch für jeden Standort einen stream und thread für deren Verarbeitung.Aber das hängt natürlich von der Art der Verarbeitung ab, ob die Daten pro Standort verarbeitet werden können.Dazu gibt es ja keinen Einblick.

Gruß Horst


m.keller - Do 23.04.15 13:35

richtig 100 Sekunden wären die maximale Vorratshaltung der Daten. Ich bekomme die Daten aus der api für jeden Standort hintereinander. (wert 1[Standort 1,2,3,4...] Wert 2[Standort 1,2,3,4...]

Sie sollten so Echtzeit nahe bearbeitet und angezeigt werden wie möglich.


Horst_H - Do 23.04.15 14:26

Hallo,

alle 1/4e6 -> alle 250 ns ein neuer Wert. Da kann eine heutige CPU schon einiges rechnen, wenn Daten schnell greifbar sind, (250* CPUfreqInGhz) = CPU_Takte.
Die Darstellung kann aber sehr viel Zeit kosten.
Aber, was da gerechnet werden soll, können wir immer nur noch raten ;-)

Gruß Horst


m.keller - Do 23.04.15 14:56

z.b. soll die Frequenz ermittelt werden und zyklisch Angezeigt werden


Horst_H - Do 23.04.15 17:14

Hallo,

also etwas mit FFT.Ob sotwas http://lomont.org/Software/Misc/FFT/LomontFFT.html schneller ist, wer weiß.Versuch macht klug.
Kann man nicht eine C-DLL einbinden?
http://fftw.org/ freeware vom MIT ist doch recht fix und nutzt auch SSE2 http://www.fftw.org/speed/CoreDuo-3.0GHz-icc64/
aber was bedeuten dabei die MFlops?
Für Windows die dll: http://fftw.org/install/windows.html

Gruß Horst


OlafSt - Fr 24.04.15 11:23

Nachdem wir nun langsam aber sicher dem TE aus der Nase gezogen haben, was tatsächlich passiert, ergibt sich doch ein anderes Bild. IMHO ist es hier eher entscheidend, wieviele der empfangenen Meßwerte in einen Block gehören. Nehmen wir 100 Meßwerte, so lassen die sich doch problemlos in ein Array mit 100 Elementen stecken. Die Meßwerte zu empfangen dauert 25µs.
In einem parallel laufenden Thread wird die FFT berechnet. Wenn diese Berechnung > 25µs dauert, wird es spannend; denn dann sollte man vllt. mit Queues arbeiten und mehrere FFT-Threads rechnen lassen (jeden auf einem eigenen CPU-Core), bis eben die 25µs erreicht sind. Ist dies nicht möglich, ist die Kiste zu langsam, denn egal wie man es dreht: Du kriegt die Daten nicht schnell genug "verrechnet", ergo wird jeder nur denkbare Buffer irgendwann volllaufen.


Horst_H - Sa 25.04.15 07:56

Hallo,

man kann ja auch nur ein Teil der Daten zur Visualisierung benutzen, schliesslich reicht ja 50 Hz für eine flüssige Darstellung. Wenn man 2048 Punkte nimmt, falls man das überhaupt braucht, sind die ja in 512 µs da.Aber in 25 ms sicher, je nach CPU natürlich, verrechnet.
Also Daten kontinuirlich wegschreiben und eben immer wieder ein kleines Häppchen zur Darstellung verarbeiten.
Was interessant wäre, ob es FFT gibt, wo man an einem Ende etwas Werte hinzufügt und am anderen wegnimmt.

Gruß Horst


m.keller - Mo 27.04.15 08:48

Vielen dank für die ganzen Antworten.

ihr habt mich ein stück weiter gebracht.
Ich werde mir die Klasse so abändern, dass ich die Variablen maximal 2 GB groß mache.
Dann bin ich unabhängiger.

Alles andere bedarf vermutlich eine menge test um die Abhängigkeit von CPU so wie alle anderen Elemente zu untersuchen und das Optimale heraus zu finden.

Danke

Gruß


m.keller - Mi 29.04.15 09:42

So nun habe ich mal versucht es mit einem Jagged Array versucht.
Leider bekomme ich dort aber nur eine Initialisierung von ca. 80 Millionen hin.
Was läuft da falsch?

Es müsste doch laut Ralf so sein dass ich mindestens die 200 Millionen hin bekommen würde.

200 Millionen x 8 byte = 1,6 GB sollte doch dann möglich sein

1 GB --> 1073741824 byte
2 GB --> 2147483648 byte

doubel besteht aus 8 byte

2147483648 / 8 = 268435456

Andersherum könnte ich maximal 268435456 werte speichern oder?

Sind die ganzen Pointer die im Hintergrund verwendet werden so wie sonstige Deklarationen für das Array so groß, dass ich nur ca. 1/4 von der Größe verwenden kann?


Ralf Jansen - Mi 29.04.15 10:27

Hast du daran gedacht den Prozess explizit auf 64Bit zu setzen? Visual Studio preferiert 32bit wenn du debuggst und das Projekt als Plattform Any CPU benutzt.


m.keller - Mi 29.04.15 11:22

vorerst mache ich es erst ein mal auf 32 bit.
Somit kann ich die 2 GB eh nicht umgehen.

Die api die ich einbinde gibt es nur als 32 bit, somit bin ich gezwungen alles auf 32 bit zu halten bis ich eine neue Version bekomme.


Ralf Jansen - Mi 29.04.15 13:58

Nun ja dann bist du (im Standardfall) schon an die max 2GB für den gesamten Prozess gebunden und in dem Memorybereich mußt du dann auch noch einen entsprechend großen zusammenhängenden Speicher finden um ein so großes Array anfordern zu können. Da solltest du schon sehr viele viel kleinere Arrays verwenden um nicht in Fragmentierungsprobleme zu laufen um überhaupt diese 2GB annähernd ausnutzen zu können.

Wenn du den Speicher wirklich optimal ausnutzen willst und einen großen Speicherbereich häufiger neu erzeugen willst solltest du über das Verhalten des Large Object Heap [https://msdn.microsoft.com/en-us/magazine/cc534993.aspx] Bescheid wissen udn über die Implikationen draus ob man ein größes bzw. kleines Object anfordert.


m.keller - Di 02.05.17 18:31

Um das Thema mal zu beenden ;)

Ich habe die Software aufgeteilt.

Der Teil mit der 32 bit Api hält die Daten nicht mehr vor, sondern sendet diese als Pakete über eine WCF-Schnittelle.

Auf der anderen Seite werden die Daten in der eigentlichen Software gesammelt und mit einem weiteren Thread verarbeitet.

Danke euch.