Große Dateien laden, OutOfMemoryException

  • VB.NET
  • .NET (FX) 4.0

Es gibt 25 Antworten in diesem Thema. Der letzte Beitrag () ist von Tukuan.

    Große Dateien laden, OutOfMemoryException

    Hallo Forum,

    nach längerem probieren, lesen, versuchen usw. wende ich mich an euch...

    Mein Projekt liest eine fast 600 MB große Datei ein, in der Messdaten enthalten sind.
    Die Date beinhaltet 6 Messwerte je mit einer Datenlänge von 24.160.000 Double Messpunkten.

    Ist die Datei eingelesen, habe ich zugriff auf das Datenarry. Diese Arrays müssen nun in ein anderes Datenformat (imc) umgewandelt werden. Hierzu steht die Funtion imcChannels(i).AppendDataDouble des Datenformatobjektes zur Verfügung.
    Beim Kopieren der Daten wirft das System nun eine OutOfMemoryException.

    VB.NET-Quellcode

    1. Try
    2. imcChannels(i).AppendDataDouble(data.Length, CType(data, System.Array))
    3. Catch ex As OutOfMemoryException
    4. MsgBox(ex.ToString)
    5. End Try


    Wenn ich übrigens kleine Dateien nehme (1/10) funktioniert, alles perfekt. Wo die genaue Größe liegt, bei dem es nicht funktioniert, habe ich noch nicht testen können.

    Nun habe ich schon gelesen, dass man dem Prozess mehr Speicher zur verfügung stellen kann:

    VB.NET-Quellcode

    1. Mem = Process.GetCurrentProcess()
    2. min = 1000000000
    3. max = 2100000000
    4. If SetProcessWorkingSetSize(Mem.Handle, min, max) Then
    5. MsgBox(Mem.ToString & ": min: " & min & " bytes; max: " & max & " bytes")
    6. End If


    Obwohl ich schon fast das Maximum angegeben habe (2.100.000.000 Bytes), kommt die Fehlermeldung trotzdem.

    Kann mir jemand weiterhelfen?
    Vielen Dank schon mal im Voraus...
    Gruß
    Tukuan
    Musst du denn alle Daten in einem Stück einlesen?
    Ich würde die Daten in kleinere Blöcke aufteilen und dann verarbeiten.

    Tukuan schrieb:

    Kann mir jemand weiterhelfen?
    Falls Dein Projekt unter 32 Bit läuft: Stell es mal um auf x64.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Slice: Hab ich auch schon dran gedacht... Muss mal schauen, wie das gehen könnte...

    @ Rod: :/ Also Plattform steht auf Aktiv (x86) daran kann ich auch nichts ändern.
    Bei den Erweiterten Compiliereinstellungen habe ich die Ziel-CPU nun auf x64 gestellt. Nun funktioniert aber eine von den Dlls nicht mehr, das mir das Datenarry zur Verfügung stellt. :(

    Tukuan schrieb:

    Nun funktioniert aber eine von den Dlls nicht mehr
    Woher kommt denn die DLL?
    Kannst Du da eine 64-Bit-DLL bekommen?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Tukuan schrieb:

    Messsoftware
    Ist es von denen gewollt, dass die solch gigantische Datenmengen halten?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Es ist durchaus möglich noch größere Datenmengen zu erzeugen. Deren Software funktioniert ja auch tadellos. Wohl auch mit noch größeren Datenmengen. Deren SW führt die Konvertierung auch durch. Aber dazu muss man die Messung stoppen. Und das geht für unseren Anwendungszweck leider nicht.
    Diese dll gehört wohl eher nicht zu derem Standartwerk. Will sagen: Diese ist wohl nur für eine paar Leute gedacht, die nicht die Standardanwendungen haben. So wie wir X(
    Ich habe noch ein wenig rumprobiert. Wie das getrennte Auslesen der Daten gehen soll, weiß ich leider nicht. Habe diesen Code nur übernommen und weiß nicht wie er genau funktioniert. Bin nur froh, dass er es tut :D :

    VB.NET-Quellcode

    1. Dim samples_cnt As Int64 = PlatformInvokeDll.DWGetScaledSamplesCount(d7dChannels(i).index)
    2. Dim data As Double() = New Double(CInt(samples_cnt - 1)) {}
    3. Dim p_data As IntPtr = Marshal.AllocHGlobal(CInt(samples_cnt) * 8)
    4. Dim timestamp As Double() = New Double(CInt(samples_cnt - 1)) {}
    5. Dim p_timestamp As IntPtr = Marshal.AllocHGlobal(CInt(samples_cnt) * 8)
    6. If PlatformInvokeDll.DWGetScaledSamples(d7dChannels(i).index, CInt(0), CInt(samples_cnt), p_data, p_timestamp) = DWStatus.DWSTAT_OK Then
    7. Marshal.Copy(p_data, data, 0, CInt(samples_cnt))
    8. ...


    Das Schreiben nur einzelner Blöcke in das Zielarry funktioniert zwar, aber ich bekomme dann an anderen Stellen eine OutOfMemory Exception.

    Viele voll Dank voller Grüße

    Tukuan schrieb:

    an anderen Stellen
    Da solltest Du mal das Gesamtkonzept überarbeiten. DWGetScaledSamples() scheint den Pointer auf ein bestimmtes Datensegment zu berechnen, mit Marshal.Copy() holst Du Daten aus dem unmanaged in den managed Bereich.
    Wenn Du mit einer Copy-Operation fertig bist, musst Du den Pointer wieder freigeben. Hast Du dies implementiert?

    VB.NET-Quellcode

    1. Dim p_data As IntPtr = Marshal.AllocHGlobal(CInt(samples_cnt) * 8)
    2. ' p_data benutzen
    3. Marshal.FreeHGlobal(p_data)

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Nein.

    VB.NET-Quellcode

    1. Marshal.FreeHGlobal(p_data)
    nutze ich zurzeit nicht...
    Habe es gerade mal getestet. Es wird besser (den ersten Kanal schreibt er nun), aber leider nicht gut:
    - beim 2. Kanal/Messwert gibts die Exception und dann schmiert das Program ganz ab. Also das Debuggen wird ohne Fehlermeldung beendet.

    Da solltest Du mal das Gesamtkonzept überarbeiten.

    Was meinst du damit? "nur" dieses FreeHGlobal oder komplett anders?

    Tukuan schrieb:

    Gesamtkonzept
    in welcher "Häppchen"-Größe Du Deine Daten abholst und verarbeitest.
    Das FreeHGlobal ist zwingend erforderlich, löst aber ggf. Dein Problem bereits.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hatte FreeHGlobal übersehen. Das war bereits implementiert. Ich habe nur die Position verändert.

    Ok. Kannst du mir damit ein wenig helfen? Es überschreitet leider ein wenig meine Programmierkenntnisse.

    Zum Sourcecode muss ich sagen, das er als C# Sourcecode vorlag. Ein Online-Tool hat ihn in VB.net übersetzt.
    Hier ist die Struktur vom DWChannel

    VB.NET-Quellcode

    1. <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> Public Structure DWChannel
    2. Public index As Integer
    3. <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=100)> Public name As String
    4. <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=20)> Public unit As String
    5. <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=200)> Public description As String
    6. Private color As UInteger
    7. Private array_size As Integer
    8. End Structure


    So bekomme ich die Kanäle:

    VB.NET-Quellcode

    1. Dim d7dChannels As DWChannel() = New DWChannel(NumberOfChannels - 1) {}
    2. d7dDLLStatus = PlatformInvokeDll.DWGetChannelList(d7dChannels)


    Und so sieht es zurzeit aus, wenn ich die Daten einlesen will (den Timestamp benötige ich nicht):

    VB.NET-Quellcode

    1. For i As Integer = 0 To NumberOfChannels - 1
    2. Dim samples_cnt As Int64 = PlatformInvokeDll.DWGetScaledSamplesCount(d7dChannels(i).index)
    3. Dim data As Double() = New Double(CInt(samples_cnt - 1)) {}
    4. Dim p_data As IntPtr = Marshal.AllocHGlobal(CInt(samples_cnt) * 8)
    5. Dim timestamp As Double() = New Double(CInt(samples_cnt - 1)) {}
    6. Dim p_timestamp As IntPtr = Marshal.AllocHGlobal(CInt(samples_cnt) * 8)
    7. If PlatformInvokeDll.DWGetScaledSamples(d7dChannels(i).index, CInt(0), CInt(samples_cnt), p_data, p_timestamp) = DWStatus.DWSTAT_OK Then
    8. Marshal.Copy(p_data, data, 0, CInt(samples_cnt))
    9. ...; mach was...
    10. Marshal.FreeHGlobal(p_data)
    11. Marshal.FreeHGlobal(p_timestamp)
    12. end if


    Ich weiß nicht, wie man die Daten einzelt abholen könnte. Es wird doch alles durch dir Funktion DWGetScaledSamples geparst.
    Achso... :S

    VB.NET-Quellcode

    1. Marshal.Copy(p_data, data, 0, CInt(samples_cnt))
    Hiermit holt man sich ja die einzelnen Daten rein...
    Also nicht von 0 bis samples_cnt sondern nur z.B. 5 Mio Daten holen und wegschreiben und die nächsten 5 Mio Daten holen und wegschreiben...
    Probiere ich gleich mal...

    Tukuan schrieb:

    als C# Sourcecode vorlag
    Warum arbeitest Du nicht mit C# weiter?
    Zur Datenübergabe an unmanaged Code gugst Du hier.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wollte mir eigentlich keine dll schrieben (hab ich auch noch nie gemacht) die eine andere dll steuert. Erscheint mir ein wenig zu kompliziert.
    Und: Hätte ich dann nicht die gleichen Probleme? Nur halt in die dll ausgelagert?
    Ich seh den Vorteil noch nicht so richtig...

    VB.NET-Quellcode

    1. Dim counter As Integer
    2. Dim length As Integer = 5000000
    3. Dim samples As Long = samples_cnt
    4. Dim dNew(length - 1) As Double
    5. Do While samples > length
    6. Marshal.Copy(p_data, dNew, counter + length, length)
    7. imcChannels(i).AppendDataDouble(length, CType(dNew, System.Array))
    8. counter += 1
    9. samples -= length
    10. Loop

    Das Kopieren einzelnen Blöcke erweist sich doch als Schwieriger... In der Marshal.Copy Zeile gibt es so nun eine System.ArgumentOutOfRangeException ... :(

    Tukuan schrieb:

    Wollte mir eigentlich keine dll schrieben
    Sollst Du auch nicht. Möglicherweise ist die Übersetzung des Automaten nicht ganz korrekt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    weil die Daten nicht

    WhitePage schrieb:

    zeilenweise
    vorliegen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Das Format scheint nicht ganz trivial zu sein, das müsste ggf. dynamisch angepasst werden. :/
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!