Einleseroutine



  • Guten Abend,

    da Aufgrund eines anderen Beitrags (http://www.c-plusplus.net/forum/317975) von mir auf meine 'nicht so gute'
    Einleseroutine verwiesen wurde, wollte ich nun meine Einleseroutine 'sicherer und besser machen' Leider
    hapert es an einer Stelle:

    #ifndef __c_read_h__
    #define __c_read_h__
    
    typedef struct {
    	double x;
    	double y;
    } data2d;
    
    	data2d *c_read(FILE *file, int *length);
    
    #endif
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include "c_read.h"
    
    data2d *c_read(FILE *file, int *length)
    {
    
    	int index = 0;
    
    	data2d *xy = NULL;
    
    	double xval, yval;
    
    	xy = (data2d *) realloc (xy, 1*sizeof(data2d));
    	while(fscanf(file, "%lf %lf", &xval, &yval) == 2) {
    		xy = (data2d *) realloc(xy, sizeof(data2d)*1);
    		xy[index].x = xval;
    		xy[index].y = yval;
    		index++;
    	}
    
    	*length = index;
    
    	return xy;
    
    }
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include "c_read.h"
    
    int
    main (int argn, char **argv)
    {
    	FILE *file = NULL;
    	const char *fn = "data/pull-fx.xvg";
    	file = fopen(fn, "r");
    
    	if (file == NULL) {
    		printf("\nERROR: main() -> failed to open '%s'\n", fn);
    		printf("Program terminated\n\n");
    		exit(EXIT_FAILURE);
    	}
    
    	int length;
    
    	data2d *test = c_read(file, &length);
    
    	printf("length: %d\n", length);
    
    	fclose(file);
    
    	return 0;
    
    }
    

    Um genau zu sein, hapert es in der Funktion c_read in der Zeile 17. Entweder bin ich gerade zu müde, oder doof????

    Wäre froh, wenn mir jemand Abhilfe schaffen würde.

    Grüße,
    simsa



  • Für wieviel Elemente willst du da den Platz anfordern? Nur für eins?
    1*x bleibt x. Daher ist das *1 sinnlos.

    Zeile 15 brauchst du nicht.
    Bedenke auch, das realloc im Fehlerfall NULL zurück gibt. Dann ist der Verweis auf deine bisherigen Daten weg und du kommst an die Daten nicht mehr ran.

    Dein Verfahren, immer ein Element mehr anzufordern, ist nicht besonders effektiv.



  • DirkB schrieb:

    1*x bleibt x. Daher ist das *1 sinnlos.

    Jaja, die Spamschutzfrage war gerade 10*1 😃



  • Hallo,

    ja, stimmt Zeile 15 ist sinnlos. Ich habe sie vergessen zu entfernen...
    Ja, die *1 ist auch sinnlos. Ich hatte gedacht, der Speicher reicht nicht
    aus und habe *2, *3, und etc. benutzt

    Ich weiß nicht wieviele Zeilen die Datei hat, deswegen möchte ich es mit
    realloc und dem index machen, damit ich später weiß wieviele Elemente ich
    habe.

    In diesem Beispiel hat die Datei ca. 4560000 Zeilen. Ab der 4000 oder so bekomme ich Segmentation fault...

    Grüße,
    simsa



  • sorry, ich meinte einen Abbruch mit 'Memory map'

    das segmentation fault nach dem 4000 eintrag war bei einem anderen
    beispiel der fall...

    sorry, ich glaube, ich bin schon müde.



  • Was hast du denn jetzt stehen bei

    xy = (data2d *) realloc(xy, sizeof(data2d)*1);
    

    ?

    Ich hätte da wahrscheinlich

    xy = realloc(xy, 2*(index+1)*sizeof(*xy));
    

    hingeschrieben.



  • Hallo,

    danke für Deine Hilfe; wird aber mit

    xy = realloc(xy, 2*(index+1)*sizeof(*xy));
    

    bzw. mit der '2*' nicht doppelte Speicher angefordert?

    Ich habe jetzt die Zeile so umgeschrieben:

    xy = realloc(xy, (index+1)*sizeof(data2d));
    

    Ist das jetzt schlechter?

    Dann noch eine Frage: Da die Einleseroutine im letzten Beitrag
    nicht sauber war, ist diese Art nun besser bzw. leak-sicherer?

    Grüße,
    simsa



  • Du machst also 4560000 mal realloc. 😮

    Fordere immer doppelt soviel Speicher wie vorher an.
    (du musst dir merken, wieviel Speicher du hast und wenn der zuende geht doppelt soviel nachfordern.

    Und fang ruhig mit genug Speicher (für ein paar 1000 Zeilen) an.

    Ganz am Ende kannst du noch ein realloc mit der tatsächlichen Größe machen.



  • Guten Morgen,

    naja, wenn ich gleich für ein paar tausend Zeilen Speicher anfordere, dann wird
    es am Ende so sein, dass ein paar Array-Einträge undefeniert bleiben.

    Dafür müßte ich diesen Speicher wieder freigeben, weil ich den Speicher bzw.
    das allokieren optimal benutzen möchte.

    Ist es falsch bzw. blöd, wenn ich 4560000 mal realloc mache?

    Dann noch eine Frage Allgemein zum realloc; folgendes Beispiel:

    #include <stdio.h>
    #include <stdlib.h>
    
    int
    main (int argn, char *argv[])
    {
    
    	int i = 0, j = 10, *array;
    
    	/* array allokieren*/
    	array = (int *) calloc(j, sizeof(int));
    
    	for (i=0; i<j; i++)
    		array[i] = i + 100;
    
    	/* nun möchte ich das array um 7 Einträge erweitern */
    	array = (int *) realloc(array, 7*sizeof(int))       // Beispiel 1 oder
    	array = (int *) realloc(array, (j+7)*sizeof(int))   // Beispiel 2
    
    	... bla bla ...
    
    	return 0;
    
    }
    

    Wenn ich nun ein allokiertes Array erweitern möchte (z.B um 7 Einträge), ist
    dann Beispiel 1 oder Beispiel 2 korrekt?

    Ich dachte, dass man bei realloc die Größe des Blocks angibt (hier 7), die neu
    dazuzukommt.



  • simsa schrieb:

    naja, wenn ich gleich für ein paar tausend Zeilen Speicher anfordere, dann wird
    es am Ende so sein, dass ein paar Array-Einträge undefeniert bleiben.

    Ohhhh.
    Aber auch dafür gibt es eine Lösung:

    DirkB schrieb:

    Ganz am Ende kannst du noch ein realloc mit der tatsächlichen Größe machen.

    simsa schrieb:

    Ist es falsch bzw. blöd, wenn ich 4560000 mal realloc mache?

    *malloc ist relativ langsam, da der Rechner ja auch die ganze Verwaltung noch erledigen muss. Zudem fragmentierst du den freien Speicher.

    simsa schrieb:

    Ich dachte, dass man bei realloc die Größe des Blocks angibt (hier 7), die neu
    dazuzukommt.

    Falsch gedacht.
    Man kann realloc auch zum verkleinern von dem Speicher nutzen.
    Und wenn du dir die Deklaration von realloc ansiehst: void* realloc (void* ptr, size_t size); kannst du bei size sehen, dass dies vom Typ size_t (ein unsigned Typ) ist.
    Nachzulesen z.B. da: http://www.cplusplus.com/reference/cstdlib/realloc/
    Da steht dann auch was von "new size for the memory block".



  • Hallo DirkB,

    danke, du hast mir die Augen geöffnet; ich habe wohl beim hastigen lesen
    das "new" überlesen.

    Ich habe nun meine Einleseroutine fertig und wollte gerne von euch hören/
    wissen, ob es nun so 'besser' ist.

    Nach dem Einlesen, mache ich am Ende noch ein realloc, damit ich auch wirklich
    soviel Speicher verbrauche, was ich auch benötige.

    data2d *c_read(FILE *file, int *length) 
    { 
    
        int index = 0;
    	size_t array_index = 0;
    
        data2d *xy = NULL;  
        double xval, yval; 
    
        while(fscanf(file, "%lf %lf", &xval, &yval) == 2) {
    		if (index == array_index) {
    			array_index += 1000;
    			xy = (data2d *) realloc(xy, array_index*sizeof(data2d)); 
    		}
            xy[index].x = xval; 
            xy[index].y = yval; 
            index++; 
        }
    
    	array_index = index;	
    	xy = (data2d *) realloc(xy, array_index*sizeof(data2d)); 
    
        *length = index; 
    
        return xy; 
    
    }
    

    Gibt es irgendwo potentielle Memory-Leaks?

    Auf eure Meinung und Kritik bin ich gespannt.

    Beste Grüße,
    simsa



  • Hallo,

    mir wäre es sehr wichtig und wertvoll, wenn jemand mir seine Meinung äußern
    würde, ob der Code Memory-Leak-Frei ist bzw. besser ist als die vorherige
    'Einleseroutine' oder ob noch irgendwo potentielle Gefahren lauern.

    Ich möchte den Speicher optimal nutzen, weil ich anfangen muss ca. 12 TB Daten
    Stück für Stück einzulesen, auswerten und wieder 'menschenlesbar' ablegen muss.

    Dann noch eine Frage: Der Code ist ca. drei Jahren alt. Damals wollte ich z.B.
    auch wissen, wieviel Speicher der Pointer auf dem RAM belegt. Das kann man ja
    mit einem Pointer nicht so einfach machen. Gibt es nun dafür 'fertige' Funktionen
    in C?

    Beste Grüße,
    simsa



  • simsa schrieb:

    ..., wenn jemand mir seine Meinung äußern
    würde, ob der Code Memory-Leak-Frei ist bzw. besser ist als die vorherige
    'Einleseroutine' oder ob noch irgendwo potentielle Gefahren lauern.

    DirkB schrieb:

    Bedenke auch, das realloc im Fehlerfall NULL zurück gibt. Dann ist der Verweis auf deine bisherigen Daten weg und du kommst an die Daten nicht mehr ran.

    simsa schrieb:

    Ich möchte den Speicher optimal nutzen, weil ich anfangen muss ca. 12 TB Daten
    Stück für Stück einzulesen, auswerten und wieder 'menschenlesbar' ablegen muss.

    Was heißt denn Stück für Stück? Wie groß ist so ein Stück?
    Wieviel Daten musst du denn wirklich gleichzeitig verarbeiten?

    simsa schrieb:

    Dann noch eine Frage: Der Code ist ca. drei Jahren alt. Damals wollte ich z.B.
    auch wissen, wieviel Speicher der Pointer auf dem RAM belegt. Das kann man ja
    mit einem Pointer nicht so einfach machen. Gibt es nun dafür 'fertige' Funktionen
    in C?

    Die Größe eines Pointers gibt es mit sizeof(). Aber das ist nur die Größe des Pointers.
    Du möchtest aber Informationen über den Speicher haben, auf den dieser Pointer verweist.

    Du kannst ja mal im Standard nachsehen. Die Links findest du auf der ersten Seite vom Unterforum unter Linkliste für Neulinge
    Schau mal gleich bei C11 nach. Bis C99 gibt es das nicht.
    Oder auch http://www.cplusplus.com/reference/clibrary/



  • Hallo DirkB,

    danke für Deine Antwort, vorallem das mit realloc.

    Nun sieht der Code wie folgt aus:

    data2d *c_read(FILE *file, int *length) 
    { 
    
        int index = 0; 
        size_t array_index = 0; 
    
        data2d *xy = NULL;   
        double xval, yval; 
    
        while(fscanf(file, "%lf %lf", &xval, &yval) == 2) { 
            if (index == array_index) { 
                array_index += 1000; 
                xy = (data2d *) realloc(xy, array_index*sizeof(data2d));
                if (xy == NULL) {
                    printf("\nERROR: c_read() -> realloc failure.\n");
                    printf("Program terminated.\n\n");
                    exit(EXIT_FAILURE);
                }
            } 
            xy[index].x = xval; 
            xy[index].y = yval; 
            index++; 
        } 
    
        array_index = index;    
        xy = (data2d *) realloc(xy, array_index*sizeof(data2d));
        if (xy == NULL) {
            printf("\nERROR: c_read() -> realloc failure.\n");
            printf("Program terminated.\n\n");
            exit(EXIT_FAILURE);
        } 
    
        *length = index; 
    
        return xy; 
    
    }
    

    So, nun müsste alles doch so in Ordnung sein, oder sieht ihr noch wo
    anders Gefahren/Lücken/Leaks?

    Bin echt für jeden Kommentar und Tipp sehr sehr dankbar.

    Die binären Dateien sind unterschiedlich groß, z.B. von 1GB bis 50GB.
    Davon habe ich soviele, dass alle zusammen ca. 12TB sind.

    Da ich auf Arbeit nur 8GB Arbeitsspeicher habe, hätte ich ja bei Dateien,
    die kleiner sind als mein Arbeitsspeicher kein Problem. Bei den Dateien,
    die größer sind als mein Arbeitsspeicher, wollte ich so vorgehen, dass ich
    jeweils immer 7GB einlese, sie verarbeite und etc..., daher wollte ich
    gerne wissen, ob es so eine Funktion gibt, die angibt, wie groß der Speicher
    ist, worauf der Pointer zeigt. Danke für den Hinweis, ich werde mir es angucken.

    Beste Grüße,
    simsa



  • Du musst doch eh ein Verfahren machen, das nur einen Teil der Daten im Speicher hält.

    Deine Funktion c_read sollte kein exit machen sondern die bisherigen Daten zurückliefern und noch kenntlich machen, das noch nicht alle Daten gelesen wurden.

    Damit deine Daten nicht verloren gehen, musst du mit zwei Zeigern arbeiten.
    So wie du es machst, gehen die Daten immer noch verloren. (Das exit löst das zwar in deinem Fall)

    Funktionen die das Programm im Fehlerfall beenden sind schlechter Stil.

    Wieviel Daten brauchst du denn wirklich im Speicher?
    Haben die ersten Datensätze noch einen Bezug zu den letzten?
    Wenn 7GB von 50GB reichen, gehen dann auch 2GB oder 2MB oder 2kB?



  • Hallo DirkB,

    danke für Deine schnelle Antwort.

    DirkB schrieb:

    Deine Funktion c_read sollte kein exit machen sondern die bisherigen Daten zurückliefern und noch kenntlich machen, das noch nicht alle Daten gelesen wurden.

    Damit deine Daten nicht verloren gehen, musst du mit zwei Zeigern arbeiten.
    So wie du es machst, gehen die Daten immer noch verloren. (Das exit löst das zwar in deinem Fall)

    Mit zwei Pointern arbeiten? Das kenne ich beim Einlesen nicht so; wäre Dir
    sehr dankbar, wenn Du mir es am Beispiel meiner 'Einleseroutine' zeigen würdest.

    DirkB schrieb:

    Funktionen die das Programm im Fehlerfall beenden sind schlechter Stil.

    Ja, das weiß ich auch. Aber, es würde meine Auswertung 'verschlechtern'. Z.B.
    wenn ich nicht alle Daten eingelesen bekomme bzw. nur einen Teil, dann ist das
    Ergebnis/der Mittelwert meiner Auswertung schlecht. Deswegen ist es in dieser
    Hinsicht besser, das Programm zu beenden und nachdenken, warum es nicht
    geklappt.

    DirkB schrieb:

    Wieviel Daten brauchst du denn wirklich im Speicher?
    Haben die ersten Datensätze noch einen Bezug zu den letzten?
    Wenn 7GB von 50GB reichen, gehen dann auch 2GB oder 2MB oder 2kB?

    Das hängt mit der Antwort von oben zusammen. Wenn ich die 50GB in 7GB aufteile
    dann habe ich zum Schluss acht Zahlen. Über diese acht Zahlen muss ich dann
    wieder mitteln um nur eine Zahl zu bekommen. Wenn ich es in 2MB oder 2kB auf-
    teile, dann habe ich tausende Dateien und der Mittelwert von den tausenden
    Dateien wäre viel schlechter. Das habe ich schon bei einer Datei mit 4GB aus-
    probiert und festgestellt, dass so der Mittelwert schlechter wird (leider).

    Für meine weiteren Auswertungen brauche ich den Mittelwert. Falls ich mit
    einem schlechten Mittelwert weiterarbeite, dann werden meine restlichen
    Auswertungen auch schlechter. Es ist leider so.

    Beste Grüße,
    simsa



  • simsa schrieb:

    Mit zwei Pointern arbeiten? Das kenne ich beim Einlesen nicht so; wäre Dir
    sehr dankbar, wenn Du mir es am Beispiel meiner 'Einleseroutine' zeigen würdest.

    data2d *xy  = NULL;  
        data2d *tmp = NULL;  
        double xval, yval;
    
        while(fscanf(file, "%lf %lf", &xval, &yval) == 2) {
            if (index == array_index) {
                array_index += 1000;
                tmp = (data2d *) realloc(xy, array_index*sizeof(data2d));
                if (tmp == NULL) {
    // Die Daten sind hier immer noch über xy erreichbar
                    printf("\nERROR: c_read() -> realloc failure.\n");
                    printf("Program terminated.\n\n");
                    exit(EXIT_FAILURE); 
                } else {
                  xy = tmp;
                }
            }
            xy[index].x = xval;
            xy[index].y = yval;
            index++;
        }
    

    simsa schrieb:

    Irgendetwas über Mittelwert

    Für den arithmetischen Mittelwert braucht man nur den aktuellen Mittelwert (besser wäre die Summe der Zahlen bisher) und die Anzahl der Zahlen bisher.
    Man liest die Zahlen nacheinander ein und summiert sie gleich auf.
    Da musst du nicht alle Zahlen im Speicher haben.

    Was willst du genau berechnen?
    Evtl. nutzt du den falschen Ansatz.



  • Hallo DirkB,

    ich simuliere im kanonischen Ensemble Vielteilchen-Systeme mit einem Simulations-
    Softwarepaket. Die Software, die ich benutze, ist eine ziemlich weit verbreiterte
    Software in meinem Forschungsbereich.

    Aus der Software bekomme ich, wie erwähnt, binäre Dateien. Die binären Dateien
    lese ich aus. Die ausgelesenen Dateien verarbeite ich dann.

    Nun zum:

    Was willst du genau berechnen?

    Also, wenn ich die Daten einlese, dann mache ich zunächst eine Fouriertrafo.
    Je mehr Punkte ich habe, umso 'besser' ist die Kurve, die ich erhalte. Die
    Kurve hat dann Maximas und Minimas. Ich mittele dann über die Maximas.

    So, wenn ich nun tausende 2MB oder 2kB Dateien habe, dann passiert es, dass
    die Maximas risiege Sprünge haben, was dann mir einen 'schlechten' Mittelwert
    gibt. Das liegt auch daran, dass die Rohdaten selber ziemlich verrauscht sind
    aus der Simulation, aber das ist eine andere Geschichte... Wenn die Daten so
    ca. 5GB bis 7GB sind, dann habe ich auch eine gute Statistik bzgl. der ein-
    gelesenen Dateien, so dass die Sprünge nicht so 'krass' sind...

    Ich bin kein Programmier, ich habe mir das programmieren im Studium selber
    beigebracht. Daher bin ich über jede Kritik und Meinung sehr dankbar, weil
    ich dadurch ausschließen kann, dass keine physikalischen Fehler passieren,
    falls das Ergebnis nicht so ist, wie ich es dachte. Deshalb ist mir es auch
    wichtig, dass die Einleseroutine sauber funktioniert.

    Danke für Dein Programmcode. Das habe ich nun verstanden; aber ich habe es
    lieber, wenn das Programm beendet wird, weil ich sonst keine guten 'Mittelwerte'
    bekomme.

    Wäre dann mein Einleseroutine immernoch schlecht?
    Was würde man ansonsten machen?

    Viele Grüße,
    simsa



  • Also die Frage ist doch: brauchst du alle Daten gleichzeitig im Speicher oder nicht.
    Falls ja, ist die Funktion soweit OK, bei irgendwas >4GB kann es aber u.U. zu Integer-Überläufen kommen, deshalb besser unsigned long long (ab C99) verwenden, auch ist der Blockfaktor 1000 bei diesen Größenordnungen zu klein.
    U.U. hast du sowieso ein Speicherplatzproblem bei >4GB Daten und 32 Bit-Programmen.
    Nehme deshalb besser einen 64-Bit Compiler.

    simsa schrieb:

    Aus der Software bekomme ich, wie erwähnt, binäre Dateien. Die binären Dateien
    lese ich aus.

    Mit fscanf kann man keine Binärdaten auslesen, nur char-Ströme entsprechend interpretieren.



  • Hallo Wutz,

    Danke für deine Antwort.

    Ja, ich brauche die Daten im Speicher; denn nach der einen 'Auswertung' kommt
    dann die zweite, dritte und und und ... (im selben Programm).

    Kurz gesagt, ich brauche die Daten solange im Speicher, bis meine Auswertung
    bzw. Programm fertig ist.

    Danke auch dafür, dass Du dir die Einleseroutine angeguckt hast. Ich werde
    dann die meine 'Routine' (vom 13:56:14 30.06.2013) verwenden und unsigned long
    long verwenden.

    Beste Grüße,
    simsa


Anmelden zum Antworten