Fatal Error bei großem Array

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Antworten
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14659
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Fatal Error bei großem Array

Beitrag von Jan »

Ich lese eine 44 MB große Ttextdatei ein. Die einzelnen Zeilen werden dabei in ein Array eingelesen. Bei ca. 1,9-2,0 Mio. Arrayelementen stürzt das Programm mit einem XppFatal ab. Die sieht so aus:

Code: Alles auswählen

FATAL ERROR LOG 
Not recoverable Error!
SYS Thread-ID: 1144 
Module: MOM
Error Codes: EH: 1006 Sub: 0(0) OS: 0 XPP: 15
Call Stack of Thread 1 (392):
MAIN(199)
Call Stack of GUI Thread (580):
Call Stack of Thread 3 (1032):
Call Stack of Thread 4 (1144):
GEDCOMEINLESEN2(248)
(B)GEDCOMEinlesen1(89)
@XBPPUSHBUTTON@I@HANDLEEVENT(968)
GEDCOMEINLESEN1(149)
File: E:\Programme\MeinProgramm\MeinProgramm.exe
TimeStamp: 20090918 21:48
End of FATAL ERROR LOG.
Was ist da los?

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12910
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 46 Mal

Re: Fatal Error bei großem Array

Beitrag von AUGE_OHR »

hi,
Jan hat geschrieben:Ich lese eine 44 MB große Ttextdatei ein. Die einzelnen Zeilen werden dabei in ein Array eingelesen. Bei ca. 1,9-2,0 Mio. Arrayelementen stürzt das Programm mit einem XppFatal ab. Die sieht so aus:

Code: Alles auswählen

FATAL ERROR LOG 
Not recoverable Error!
SYS Thread-ID: 1144 
Module: MOM
Error Codes: EH: 1006 Sub: 0(0) OS: 0 XPP: 15
schon mal in den Taskmanager und Speicheranzeige geguckt ?

EH: 1006 ==> Xbase++ Sub-System Error Code, means: "Too many memory objects. Check if garbage collector is blocked."
Sub: 0(0) ==> OS Error Code Decimal (and hexadecimal)
OS: 0 ==> OS Error Code Decimal, means: No Error
XPP: 15 ==> Xbase++ General Error Code, means: "Not enough memory or swapping space available."
gruss by OHR
Jimmy
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14659
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von Jan »

Hallo Jimmy,

ja, scheint in irgendeiner Weise ein Speicherproblem zu sein. Denn wenn ich die Zeile mit dem Arrayelemant anhängen und Füllen mit den Inhalten rausnehme, dann läuft das sauber durch.

Ich habe 2 GB Arbeitsspeicher, dazu noch die Auslagerungsdatei. Das muß doch reichen. Im Prozessmanager habe ich auch nichts ungewöhnliches gefunden, alles im grünen Bereich. Die Handles werden zwar hochgezählt, aber sehr langsam und nicht in einem bedenklichen Bereich.

Merkwürdig das ganze...

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16517
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von Martin Altmann »

Hallo Jan,
nicht wirklich merkwürdig!
Suche mal ein wenig im Forum - wirst viele Themen dazu finden!
Du musst einen großen Bereich an Arrayelementen auf einmal allokieren und diese einzeln füllen. Dann solltest Du weiter kommen (wenngleich Du wahrscheinlich trotzdem wieder einen Absturz erfahren wirst - dann jedoch erst einiges später (also nachdem mehr Zeilen gelesen worden)).

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Markus Walter
Programmier-Gott
Programmier-Gott
Beiträge: 1018
Registriert: Di, 24. Jan 2006 10:22
Wohnort: Saarland

Re: Fatal Error bei großem Array

Beitrag von Markus Walter »

Hallo Jan,

ich hatte auch schon verschiedene Probleme, große Dateien in Arrays (zeilenweise) einzulesen. Auch das viel genannte vorherige dimensionieren (statt aadd()) hat da nicht geholfen, war in einigen Fällen sogar schlechter. Ich hatte an den support ein entsprechendes Sample geschickt und das Ganze auch Steffen in Köln gezeigt, aber leider nichts mehr davon gehört. Obwohl ich das Problem mit einem "Einfachst-Code" und passender Datei geliefert hatte.

Ich hatte viel mit sleep() und unterschiedlicher "Array-Dimensionierung" experimentiert und auch unterschiedliche Ergebnisse. Interessanterweise stürtze das Ganze immer früher ab, je mehr ich den Code vereinfacht hatte, d. h. das einfache Einlesen und Aufteilen der Datei in einen Array schmierte viel früher ab, als im "Orginal-Code" wo zwischendurch noch andere Aktionen erfolgten (in DBF schreiben, ...).

Wi gesagt, leider habe ich von Alaska nichts mehr gehört...
Gruß
Markus

Mitglied der XUG Saarland-Pfalz
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14659
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von Jan »

Hallo Martin und Markus,

ich habe mal gleich von vorneherein das Array mit 500.000 Zeilen angelegt. Totalabsturz beim Einlesen an gleicher Stelle. Hat also nichts gebracht.

Ich habe mir im Moment damit beholfen, daß ich das alles ein eine dbf einlesen. Aber die ist dann gleich mal 1,3 GB groß (ich lege die Feldlänge prophylaktisch mit 500 Zeichen an, wovon meist nur 10-100 Zeichen gebraucht werden), und das geht natürlich auch alles wesentlich langsamer. Ich würde das also sehr gerne wieder als Array durchlaufen lassen.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14659
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von Jan »

Markus,

ich habe das Problem mal an Alaska weitergegeben. Mal sehen, was die dazu sagen.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von brandelh »

Jan hat geschrieben:Ich habe mir im Moment damit beholfen, daß ich das alles ein eine dbf einlesen. Aber die ist dann gleich mal 1,3 GB groß (ich lege die Feldlänge prophylaktisch mit 500 Zeichen an, wovon meist nur 10-100 Zeichen gebraucht werden), und das geht natürlich auch alles wesentlich langsamer. Ich würde das also sehr gerne wieder als Array durchlaufen lassen.
Jan
da hast verbrauchst du halt auch ganz schön RAM ... hast du mal versucht die Datei im Ganzen in den RAM zu laden ?
Ich habe das schom mit 1 GB Dateien und Memoread() gemacht, aber insgesamt rentiert sich das nicht.
Besser du liest die Datei gleich zeilenweise ein (in z.B. 4 KByte Blocks) und teilst das dann in die passende DBF auf.
In meiner Funktion nutze ich dazu noch die alte Clipper / Alaska PRG: FILEIO.PRG (mit Anpassung, dass chr(26) und CRLF nie geliefert werden). Aber es sollte auch fopen() und fReadStr() funktionieren, allerdings wäre eine Pufferung schneller.
Alles in ein Array zu laden und dann 300 oder 400 MB Strings zu haben ist aber sicher nicht schneller ;-)
Gruß
Hubert
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12910
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 46 Mal

Re: Fatal Error bei großem Array

Beitrag von AUGE_OHR »

Jan hat geschrieben:ich habe mal gleich von vorneherein das Array mit 500.000 Zeilen angelegt. Totalabsturz beim Einlesen an gleicher Stelle. Hat also nichts gebracht.
wow da "kracht" es schon viel früher bei mir.

bei 2 GB RAM "ohne" Swapdisk ist bei mir bei ca. 830MB Dateigrösse der RAM Speicher "verbraucht"

dieser Code "schreibt" zuerst eine grosse Datei (wenn nicht vorhanden) und "liest" die dann ein

Code: Alles auswählen

#include "Fileio.ch"
#include "os.ch"
#include "common.ch"

#DEFINE CRLF   CHR(13)+CHR(10)

#define  F_HANDLE   1
#define  F_POS      2
#define  F_LASTREC  3

PROCEDURE Main(cFile)
*LOCAL cFile    := "E:\GIGA\BIGFILE.TXT"
LOCAL nHandle  := 0
LOCAL i        := 1
LOCAL Imax     := 500000
LOCAL cText    := ""
LOCAL nLen     := 0
LOCAL nbyte    := 0
LOCAL nSum     := nbyte
LOCAL nTimeON  := 0
LOCAL nTimeOFF := 0
LOCAL xMemo    := SPACE(1024)

   SET ALTER TO GIGALOG.TXT
   SET ALTER ON

   IF FILE(cFile)

   ELSE
      nHandle  := FCreate(cFile, FC_NORMAL )
      IF nHandle == -1
         ? "Fehler beim Erzeugen der Datei:", FError()
      ELSE
         nTimeON   := SECONDS()
         i := 1
         FOR i := 1 TO 98
             cText := cText + "Inhalt FELD"+STRZERO(i,2)+" mit Delimiter und länger"+";"
         NEXT
         cText := cText + "Inhalt FELD"+STRZERO(99,2)+" mit Delimiter und länger"+CRLF
         nLen  := LEN(cText)
         nTimeOFF    := SECONDS()
         ? LTrim(STR(nTimeOFF-nTimeON))+" sec."

         SET ALTER OFF

         nTimeON   := SECONDS()
         i := 1
         FOR i := 1 TO iMax
            nbyte := FWrite( nHandle, cText, nLen )
            nSum := nSum+nbyte
            ? i,nbyte,nSum
         NEXT
         FClose( nHandle )
         nTimeOFF    := SECONDS()

         SET ALTER ON
         ? LTrim(STR(nTimeOFF-nTimeON))+" sec."
         SET ALTER OFF
      ENDIF
   ENDIF

   IF FILE(cFile)
      nTimeON   := SECONDS()
      //
      // NO Chance with 1.5GB ???
      //
*     xMemo    := MEMOREAD(cFile)

      xMemo    := MYMEMOREAD(cFile)

      nTimeOFF    := SECONDS()
      SET ALTER ON
      ? LTrim(STR(nTimeOFF-nTimeON))+" sec."
      SET ALTER OFF

   ENDIF

   SET ALTER TO

RETURN

FUNCTION MYMEMOREAD(cFile)
LOCAL aBig     := {}
LOCAL aFile
LOCAL cZeile   := ""
LOCAL nBisEOF  := 4000                           // bei 4KB Block
LOCAL nTimeON  := 0
LOCAL nTimeOFF := 0
LOCAL nSum     := 0
LOCAL nCounter := 0
LOCAL nMenge   := 10                             // 10 S„tze auf ein mal
LOCAL nMem     := Memory(MEM_VIRT_AVAIL)
LOCAL nStart   := 1
LOCAL nlast    := 0

   SET ALTER ON
   ? cfile,"Memory "+STR(nMem)
   aFile := F_Use(cFile)   // Datei ”ffnen
   ? F_Bof( aFile )        // Ergebnis: .T.

   //
   // diese Routine zielt auf "gleich" Satzl„nge ab,
   // do muss man die jedesmal "berechnen"
   //
   cZeile   := F_Read( aFile, nBisEOF )    // lange Zeile lesen
   nBisEOF  := AT(CRLF,cZeile)

   IF nBisEOF > 0
      nBisEOF := nBisEOF +2                      // CRLF
      ? LTRIM(STR(nBisEOF))+" byte zu lesen pro/Satz"

      nBisEOF := nBisEOF

   ELSE
      MSGBOX("reicht nicht")
      QUIT
   ENDIF

   //
   // wieder zurck auf Start
   //
   F_GoTop( aFile )

   SET ALTER OFF
   nTimeON   := SECONDS()

   DO WHILE ! F_Eof( aFile )    // Jeweils "Block" lesen
      //
      // aktuelle Position speichern
      //
      nlast    := F_Skip( aFile, 0 )
      //
      // Block lesen
      //
      cZeile   := F_Read( aFile, (nBisEOF * nMenge) ) // Daten "Block" von "nMenge" S„tzen lesen
      //
      // am Ende nur noch Satzweise
      //
      IF LEN(cZeile) <> (nBisEOF * nMenge)
         F_Goto( aFile, nlast )
         nMenge := 1
      ELSE
         //
         // nun den Block in Sa„tze zerlegen
         //
         i := 1
         FOR i := 1 TO nMenge
            cTeil := SUBSTR(cZeile,nStart,(i * nBisEOF))
            AADD(aBig, cTeil)
            nCounter++
            nSum   := nSum + LEN(cTeil)
            nStart := i * nBisEOF
         NEXT
      ENDIF
      //
      // das mach es langsam, aber wir wollen ja was sehen
      //
      IF ((nCounter) % 1000) == 0
         nMem  := Memory(MEM_VIRT_AVAIL)
         nTimeOFF := SECONDS()
         SET ALTER ON
         ? nCounter,nMem,nSum, LTrim(STR(nTimeOFF-nTimeON))+" sec."
         SET ALTER OFF
         //
         // not enought Memory
         //
         IF Memory(MEM_VIRT_AVAIL) < 10000
*            MSGBOX("Memory only "+STR(nMem))
            F_GoBottom( aFile )
         ENDIF
      ENDIF
   ENDDO                        // erreicht ist

   FClose( aFile[F_HANDLE] )

   nTimeOFF    := SECONDS()

   IF Memory(MEM_VIRT_AVAIL) < 10000
      MSGBOX("Memory only "+STR(nMem))
      SET ALTER ON
      ? LTrim(STR(nTimeOFF-nTimeON))+" sec. bis einlesen in aBIG Array"
      SET ALTER OFF
   ELSE
      SET ALTER ON
      ? LTrim(STR(nTimeOFF-nTimeON))+" sec. zum einlesen in aBIG Array"
      SET ALTER OFF
   ENDIF

   nSum     := 0
   nMenge   := LEN(aBig)
   SET ALTER ON
   ? "Array hat "+LTRIM(STR(nMenge))+" Elemente"
   SET ALTER OFF

   nTimeON   := SECONDS()
   FOR i := 1 TO nMenge
*      ? aBig[i]
      nBisEOF  := LEN(aBig[i])
      nSum     := nSum +nBisEOF
   NEXT
   nTimeOFF    := SECONDS()
   SET ALTER ON
   ? "Items "+STR(nMenge)+" Mbyte "+STR(nSum/1000/1000)
   ? LTrim(STR(nTimeOFF-nTimeON))+" sec."
   SET ALTER OFF

RETURN aBig

** Datei öffnen
FUNCTION F_Use( cFileName )
   LOCAL aFile := { 0, 0, 0 }

   aFile[ F_HANDLE ] := FOpen( cFileName, FO_READWRITE )

   IF FError() == 0

      aFile[ F_LASTREC ] := FSeek( aFile[F_HANDLE], 0 , FS_END )
      FSeek( aFile[F_HANDLE], 0 , FS_SET )
   ENDIF
RETURN aFile

** Zum Dateianfang gehen
FUNCTION F_GoTop( aFile )
   aFile[ F_POS ] := FSeek( aFile[F_HANDLE], 0 , FS_SET )
RETURN NIL

** Zum Dateiende gehen
FUNCTION F_GoBottom( aFile )
   aFile[ F_POS ] := FSeek( aFile[F_HANDLE], 0 , FS_END )
RETURN NIL

** Dateizeiger um nBytes "skippen"
FUNCTION F_Skip( aFile, nBytes )
   aFile[ F_POS ] := FSeek( aFile[F_HANDLE], nBytes, FS_RELATIVE )
RETURN  aFile[ F_POS ]

FUNCTION F_Goto( aFile, nBytes )
DEFAULT nBytes TO 1
   aFile[ F_POS ] := FSeek( aFile[F_HANDLE], nBytes, FS_SET )
RETURN  aFile[ F_POS ]


** Zeichen einlesen
FUNCTION F_Read( aFile, nBytes )
   LOCAL cBuffer := Space( nBytes )
   nBytes := FRead( aFile[F_HANDLE], @cBuffer, nBytes )
   aFile[ F_POS ] += nBytes
RETURN  Left( cBuffer, nBytes )

** BOF abfragen
FUNCTION F_Bof( aFile )
RETURN  aFile[ F_POS ] == 0

** EOF abfragen
FUNCTION F_Eof( aFile )

RETURN  aFile[ F_POS ] == aFile[ F_LASTREC ]

*
*eof
*
damit kann du ja mal testen ob es an der Grösse oder "deiner" Datei liegt
gruss by OHR
Jimmy
Dieter
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 237
Registriert: Do, 14. Aug 2008 14:59
Wohnort: Straelen
Hat sich bedankt: 2 Mal
Danksagung erhalten: 3 Mal

Re: Fatal Error bei großem Array

Beitrag von Dieter »

Hallo Jan,

du hast geschrieben:
Ich habe 2 GB Arbeitsspeicher, dazu noch die Auslagerungsdatei.
Die Bezeichnung Auslagerungsdatei im Windows-Taskmanager ist leider irreführend. Der Wert der hier erscheint ist der verbrauchte physikalische Speicher zusammen mit der Auslagerungsdatei.
WinTaskManager.jpg
WinTaskManager.jpg (165.08 KiB) 5012 mal betrachtet
Im Beispiel ist die Auslagerungsdatei 2 GB = 2097864 kB. Noch verfügbarer physikalischer Speicher besteht in Höhe von 924656 kB, da insgesamt 3 GB Speicher vorhanden sind. Auf die Größe der Auslagerungsdatei würde ich mich nicht wirklich verlassen, insbesondere bei Einsatz von Array-Technik.
Da Alaska nicht verantwortlich für das Speichermanagement ist, das macht ja wohl der Garbage-Collektor, werden die sich wohl auch zu keinem Statement hinreißen lassen. Da hilft wohl nur ein Speicherriegel, der ja nicht mehr allzu teuer ist. Wenn das nicht mehr ausreicht, dann hilft nur noch ein 64-Bit Betriebssystem und ein 64-Bit-xbase. Mit einem 64-Bit-Windows müsste xBase (32-Bit) eigentlich mit 4GB Speicher zurecht kommen (physikalischer Speicher auf dem Rechner mindestens 6 GB). Hat das schon mal jemand getestet??
Viele Grüße

Dieter

Was man nicht versteht, besitzt man nicht.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von brandelh »

Hi,

soweit ich mich erinnere liegt die maximale Speichermenge je Prozess bei 2 GByte.

Ich meine man sollte den Weg über so riesige Arrays komplett verlassen und besser die Teilchen "häppchenweise"
optimiert einlesen. Ich arbeite an einer Klasse, die Textdateien als Objekt verwaltet, das die normalen DBF Befehle als Methoden
nutzt. Intern wird dazu dann eine Kombination aus fopen, fseek, fread und fwrite verwendet, wobei immer Blöcke (4k)
geladen werden. Allerdings weiß ich nicht wann ich damit fertig werde ...

Ich stelle mir folgendes vor:

Code: Alles auswählen

oTxt := HBText(cFileName,cAnsiOemW,IsFixed,lReadOnly,lExclusive) 
oTxt:FileName()
oTxt:FilePath()
oTxt:SaveTo()  // hier könnte man auch über einen Filter die Zieldatei verkleinern ...
oTxt:GoTop()
oTxt:GoBotom()
oTxt:GoEof()
oTxt:GetData([nLine])  // bestimmte Zeile lesen
oTxt:Skip(nLines)
oTxt:SetData([nLine])
oTxt:RecNo()
oTxt:GoTo(nLine)
oTxt:SetFilter(bFilter)  // Codeblock auch in Textform -> bFilter := &(bFilter)
oTxt:AppendData(cRecord) // freies Format !
oTxt:SetVarNames(aStru) // Array mit Strukturinfos bei festen Zeilenlängen.
oTxt:?VariablenName? // VariablenName wie in SetVarNames(aStru) definiert -> :set/getNoIVar ...
oTxt:deleteFile()  // warum nicht
oTxt:renameFile() ...
noch viel Arbeit liegt vor mir, aber ich bin schon jetzt gespannt, wie die Laufzeit meiner
großen Textimporte sich verändert, wenn ich nicht mehr Zeile für Zeile einlese sondern diese Klasse nutze ;-)
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14659
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von Jan »

Es gab ja inzwischen eine ganze Reihe von Kommentaren und Tipps. Dafür vorab ersteinmal vielen Dank.

Ich habe halt Alaska ein Beispiel geschickt, in dem das Problem reproduzierbar ist. Da muß ich noch mal nachhaken, denn die erste Antwort von Andreas scheint mir zu sagen, daß er den Anhang mit dem Beispiel nicht gesehen hat.

Aber abgesehen davon, ein paar Anmerkungen zu meinem ursprünglichen Problem:

- Ich lese die Originaldatei (in diesem speziellen Fall 44 MB groß) komplett ein, und arbeite die dann Satzweise ab. Jeder Satz ist ein neues Element im 1dimensionalen Array.
- Jeder Satz ist unterschiedlich, hat auch unterschiedliche Bedeutungen, die durch vorgestellte Tags erläutert werden
- Ich habe das deswegen gemacht, damit ich im Array schnell arbeiten kann, denn die Daten müssen danach noch Datenbanksatzweise aufgearbeitet werden.

Inzwischen bin ich aber von diesem Weg ab. Ich lese direkt in eine Datenbank ein. Dafür schreibe ich gerade die komplette Logik um. Damit komme ich ganz gut voran, auch wenn ich da schon wieder einen XppFatal habe :evil: Dazu mache ich gleich noch ein neues Thema auf.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von brandelh »

Hallo Jan,

also 33 MB sind kein Problem, mit dieser Funktion (OEM oder ANSI Kodierung) habe ich schon größere Arrays eingelesen:

Code: Alles auswählen

*-------------------------------------------------------------------------------
FUNCTION TxtFile2Array(cDateiName,cConvert) // TXT einlesen und zeilenweise in Array einlesen
                                            // cConvert: NIL / empty - nichts tun
                                            //           O2A         - Oem 2 Ansi
                                            //           A2O         - Ansi 2 Oem
   local cDateiInhalt, aZeilen := {}, nVon, nBis, cTxt

   DEFAULT cConvert to ""

   nVon := 1
   nBis := 0

   if file(cDateiName)
      cDateiInhalt := memoread(cDateiName)
      if ! empty(cDateiInhalt)
         cConvert := upper(alltrim(cConvert))
         do case
            case cConvert == "O2A"
                 cDateiInhalt := ConvToAnsiCP(cDateiInhalt)
            case cConvert == "A2O"
                 cDateiInhalt := ConvToOemCP(cDateiInhalt)
         endcase
         do while .t.
            nBis := at(CRLF,cDateiInhalt,nVon)
            if nBis = 0    // Ende der Datei wurde erreicht, letzten Satz nicht vergessen
               cTxt := substr(cDateiInhalt,nVon)
               nVon := len(cDateiInhalt)+1
            else
               cTxt := substr(cDateiInhalt,nVon,nBis-nVon)
               nVon := nBis+2
            endif
            cTxt := strTran(cTxt,CRLF,"")                // entferne die CRLF am Zeilenende
            aadd(aZeilen,cTxt)
            if nVon > len(cDateiInhalt)
               exit
            endif
         enddo
      endif
   endif
return aZeilen
Sie verwendet die Stringspeicheroptimierung aus einem Dokument von Alaska (also String nicht umkopieren sondern VON BIS Werte merken ... das geht recht schnell. Welches Verfahren endgültig schneller ist hängt von vielen Gegebenheiten ab.
Ich nutze dies Funktion normalerweise für Vorgaben von Listboxen etc.
Gruß
Hubert
Benutzeravatar
Markus Walter
Programmier-Gott
Programmier-Gott
Beiträge: 1018
Registriert: Di, 24. Jan 2006 10:22
Wohnort: Saarland

Re: Fatal Error bei großem Array

Beitrag von Markus Walter »

Hallo Hubert,

ich verwende eine ähnliche Funktion, um große Strings anhand von Tokens aufzuteilen (in einen Array). Und das seit langem und in vielen Fällen. Aber wenn der Array richtig groß wird (in kurzer Zeit), macht Xbase irgendwann die Grätsche. Bei mir ging es konkret um eine Datanorm-Datei mit 150MB, die zu einem mehrdimensionalen Array mit rund 1,4 Mio "Zeilen" (je Zeile zwischen 10 und 20 "Felder") führt.

Ich weiß, ist "viel zu viel", aber ich dachte in meinem nicht mehr ganz jugendlichen Leichtsinn, dass das für ein 32bit-System kein Problem sein sollte.

Ich habe dann den Algorithmus so umgestellt, dass ich einen Array nur aus Zeilen baue (eben die 1,4 Mio), dann Zeile für Zeile vorgehe und jede einzeln wiederum in einen Array aus "Feldern" aufteile. Wenn ich dazwischen dann genügend anderen Code ausführe (in DBF schreiben, was überprüfen, ...), dann geht das Ganze auch (ausser dass nachher 400MB Speicher mehr belegt sind, die nicht mehr freigegeben werden).

Wenn ich aber nur folgendes mache:

Code: Alles auswählen

aZeilen := wToken(cGroßerString, CRLF)
nAnz := len(aZeilen)
for i := 1 to nAnz
  aSpalten := wToken(aZeilen[i], ";")
next
dann fliegt mir das Ganze um die Ohren (teilweise schon nach 15000 Sätzen). Ein sleep (mit verschiedendsten Werten getestet) in der for-Schleife hilft nut bedingt (ich komme dann in der Regel weiter, aber auch nicht sehr viel weiter).

Nur wenn sich in der for-Schleife anderer Code abspielt, funktioniert es...

In der wToken-Funktion habe ich mit unterschiedlichen "Array-Erzeugungs-Methoden" gearbeitet (aadd, fest dimensionieren, bei jedem i dimensionieren, ...). Kurioserweise hat aadd am besten funktioniert (da ist die Schleife am weitesten gekommen).

Wie bereits geschrieben, habe ich das Testprogramm (12 Zeilen + 23 Zeilen für die wToken-Funktion) inkl. Daten an Alaska geschickt und habe das Ganze in Köln auch Steffen gezeigt, aber im Prinzip nichts mehr davon gehört.
Gruß
Markus

Mitglied der XUG Saarland-Pfalz
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16517
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von Martin Altmann »

Hallo Markus,
Markus Walter hat geschrieben:Kurioserweise hat aadd am besten funktioniert (da ist die Schleife am weitesten gekommen).
wirklich kurios ist das eigentlich nicht. Dabei wird im Prinzip das gesamte Array intern umkopiert - so zu arbeiten ist am uneffektivsten (naja, fast), da extrem zeitaufwändig!
Und genau das ist hier hilfreich - aus diesem Grund hat der GC genug Zeit, zwischendurch auch mal aufzuräumen...

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15697
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Fatal Error bei großem Array

Beitrag von brandelh »

Hi,

ein mehrdimensionales Xbase++ Array vergrößert natürlich die Probleme.
Ich lese mit meiner Funktion ja auch nur Textzeilen in ein einfaches Array ein.

Im aufrufenden Programm wird dann der Inhalt jeder Zeile ausgewertet und verarbeitet (dbf, drucken etc.)
Ich hatte zu Clipperzeiten so eine ähnliche Funktion die diese Arbeit ohne ein Array (Größenbeschränkung)
erledigt hat, nachdem memoline() einfach zu langsam wurde für längere Texte.

In Xbase++ habe ich das dann auf Arrays umgestellt, aber es gibt da natürlich Grenzen ;-)
Insbesondere auf Rechnern mit gegrenztem Hauptspeicher (Netbook, Virtueller-Webserver etc.)
Gruß
Hubert
Benutzeravatar
Markus Walter
Programmier-Gott
Programmier-Gott
Beiträge: 1018
Registriert: Di, 24. Jan 2006 10:22
Wohnort: Saarland

Re: Fatal Error bei großem Array

Beitrag von Markus Walter »

Hallo Hubert,

das mit dem mehrdimensionalen Array habe ich ja auch schnell aufgegeben. Ich verwende im o. a. Beispiel ja zwei eindim. Arrays (aZeilen mit ca. 1.4 Mio Elementen) und aSpalten mit zwischen 10 und 20 Elementen.

Am verfügbaren Speicher lag es in meinem Fall nicht (zumindest nicht in dem in Windows verfügbaren). Wenn dann eher im "Xbase"-Speicher.
Gruß
Markus

Mitglied der XUG Saarland-Pfalz
Antworten