Ich habe ein Problem mit einem Programm, ich habe nach Segmentierungsfehlern gesucht, verstehe sie nicht ganz, das einzige, was ich weiß, ist, dass ich vermutlich versuche, auf einen Speicher zuzugreifen, auf den ich nicht zugreifen sollte. Das Problem ist, dass ich meinen Code sehe und nicht verstehe, was ich falsch mache.
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define lambda 2.0
#define g 1.0
#define Lx 100
#define F0 1.0
#define Tf 10
#define h 0.1
#define e 0.00001
FILE *file;
double F[1000][1000000];
void Inicio(double D[1000][1000000]) {
int i;
for (i=399; i<600; i++) {
D[i][0]=F0;
}
}
void Iteration (double A[1000][1000000]) {
long int i,k;
for (i=1; i<1000000; i++) {
A[0][i]= A[0][i-1] + e/(h*h*h*h)*g*g*(A[2][i-1] - 4.0*A[1][i-1] + 6.0*A[0][i-1]-4.0*A[998][i-1] + A[997][i-1]) + 2.0*g*e/(h*h)*(A[1][i-1] - 2*A[0][i-1] + A[998][i-1]) + e*A[0][i-1]*(lambda-A[0][i-1]*A[0][i-1]);
A[1][i]= A[1][i-1] + e/(h*h*h*h)*g*g*(A[3][i-1] - 4.0*A[2][i-1] + 6.0*A[1][i-1]-4.0*A[0][i-1] + A[998][i-1]) + 2.0*g*e/(h*h)*(A[2][i-1] - 2*A[1][i-1] + A[0][i-1]) + e*A[1][i-1]*(lambda-A[1][i-1]*A[1][i-1]);
for (k=2; k<997; k++) {
A[k][i]= A[k][i-1] + e/(h*h*h*h)*g*g*(A[k+2][i-1] - 4.0*A[k+1][i-1] + 6.0*A[k][i-1]-4.0*A[k-1][i-1] + A[k-2][i-1]) + 2.0*g*e/(h*h)*(A[k+1][i-1] - 2*A[k][i-1] + A[k-1][i-1]) + e*A[k][i-1]*(lambda-A[k][i-1]*A[k][i-1]);
}
A[997][i] = A[997][i-1] + e/(h*h*h*h)*g*g*(A[0][i-1] - 4*A[998][i-1] + 6*A[997][i-1] - 4*A[996][i-1] + A[995][i-1]) + 2.0*g*e/(h*h)*(A[998][i-1] - 2*A[997][i-1] + A[996][i-1]) + e*A[997][i-1]*(lambda-A[997][i-1]*A[997][i-1]);
A[998][i] = A[998][i-1] + e/(h*h*h*h)*g*g*(A[1][i-1] - 4*A[0][i-1] + 6*A[998][i-1] - 4*A[997][i-1] + A[996][i-1]) + 2.0*g*e/(h*h)*(A[0][i-1] - 2*A[998][i-1] + A[997][i-1]) + e*A[998][i-1]*(lambda-A[998][i-1]*A[998][i-1]);
A[999][i]=A[0][i];
}
}
main() {
long int i,j;
Inicio(F);
Iteration(F);
file = fopen("P1.txt","wt");
for (i=0; i<1000000; i++) {
for (j=0; j<1000; j++) {
fprintf(file,"%lf \t %.4f \t %lf\n", 1.0*j/10.0, 1.0*i, F[j][i]);
}
}
fclose(file);
}
Vielen Dank für Ihre Zeit.
Diese Erklärung:
double F[1000][1000000];
würde auf einem typischen x86-System 8 * 1000 * 1000000 Bytes belegen. Das sind etwa 7,45 GB. Es besteht die Möglichkeit, dass Ihrem System beim Versuch, Ihren Code auszuführen, nicht genügend Arbeitsspeicher zur Verfügung steht, was zu einem Segmentierungsfehler führt.
Ihr Array belegt ungefähr 8 GB Speicher (1.000 x 1.000.000 x Größe von (doppelten) Bytes). Das könnte ein Faktor für dein Problem sein. Es ist eher eine globale Variable als eine Stapelvariable, also sind Sie vielleicht in Ordnung, aber Sie stoßen hier an Grenzen.
Das Schreiben so vieler Daten in eine Datei wird eine Weile dauern.
Sie überprüfen nicht, ob die Datei erfolgreich geöffnet wurde, was ebenfalls eine Fehlerquelle sein könnte (wenn dies fehlschlägt, ist ein Segmentierungsfehler sehr wahrscheinlich).
Sie sollten wirklich einige benannte Konstanten für 1.000 und 1.000.000 einführen; was repräsentieren sie?
Sie sollten auch eine Funktion schreiben, um die Berechnung durchzuführen; Sie könnten eine verwenden inline
Funktion in C99 oder höher (oder C++). Die Wiederholung im Code ist quälend anzusehen.
Sie sollten auch die C99-Notation für verwenden main()
mit dem expliziten Rückgabetyp (und vorzugsweise void
für die Argumentliste, wenn Sie sie nicht verwenden argc
oder argv
):
int main(void)
Aus reiner Neugier habe ich eine Kopie Ihres Codes genommen, alle Vorkommen von 1000 in ROWS geändert, alle Vorkommen von 1000000 in COLS geändert und dann erstellt enum { ROWS = 1000, COLS = 10000 };
(wodurch die Problemgröße um den Faktor 100 reduziert wird). Ich habe ein paar kleinere Änderungen vorgenommen, damit es unter meinen bevorzugten Kompilierungsoptionen sauber kompiliert wird (nichts Ernstes: static
vor den Funktionen und dem Hauptarray; file
wird ein lokaler zu main
; Fehler überprüfen Sie die fopen()
etc.).
Ich habe dann eine zweite Kopie erstellt und eine Inline-Funktion erstellt, um die wiederholte Berechnung durchzuführen (und eine zweite, um tiefgestellte Berechnungen durchzuführen). Das bedeutet, dass der monströse Ausdruck nur einmal ausgeschrieben wird – was sehr wünschenswert ist, da es Konsistenz gewährleistet.
#include <stdio.h>
#define lambda 2.0
#define g 1.0
#define F0 1.0
#define h 0.1
#define e 0.00001
enum { ROWS = 1000, COLS = 10000 };
static double F[ROWS][COLS];
static void Inicio(double D[ROWS][COLS])
{
for (int i = 399; i < 600; i++) // Magic numbers!!
D[i][0] = F0;
}
enum { R = ROWS - 1 };
static inline int ko(int k, int n)
{
int rv = k + n;
if (rv >= R)
rv -= R;
else if (rv < 0)
rv += R;
return(rv);
}
static inline void calculate_value(int i, int k, double A[ROWS][COLS])
{
int ks2 = ko(k, -2);
int ks1 = ko(k, -1);
int kp1 = ko(k, +1);
int kp2 = ko(k, +2);
A[k][i] = A[k][i-1]
+ e/(h*h*h*h) * g*g * (A[kp2][i-1] - 4.0*A[kp1][i-1] + 6.0*A[k][i-1] - 4.0*A[ks1][i-1] + A[ks2][i-1])
+ 2.0*g*e/(h*h) * (A[kp1][i-1] - 2*A[k][i-1] + A[ks1][i-1])
+ e * A[k][i-1] * (lambda - A[k][i-1] * A[k][i-1]);
}
static void Iteration(double A[ROWS][COLS])
{
for (int i = 1; i < COLS; i++)
{
for (int k = 0; k < R; k++)
calculate_value(i, k, A);
A[999][i] = A[0][i];
}
}
int main(void)
{
FILE *file = fopen("P2.txt","wt");
if (file == 0)
return(1);
Inicio(F);
Iteration(F);
for (int i = 0; i < COLS; i++)
{
for (int j = 0; j < ROWS; j++)
{
fprintf(file,"%lf \t %.4f \t %lf\n", 1.0*j/10.0, 1.0*i, F[j][i]);
}
}
fclose(file);
return(0);
}
Dieses Programm schreibt an P2.txt
Anstatt von P1.txt
. Ich habe beide Programme ausgeführt und die Ausgabedateien verglichen; die Ausgabe war identisch. Als ich die Programme auf einem größtenteils im Leerlauf befindlichen Computer (MacBook Pro, 2,3 GHz Intel Core i7, 16 GiB 1333 MHz RAM, Mac OS X 10.7.5, GCC 4.7.1) ausführte, erhielt ich ein einigermaßen, aber nicht vollständig konsistentes Timing:
Original Modified
6.334s 6.367s
6.241s 6.231s
6.315s 10.778s
6.378s 6.320s
6.388s 6.293s
6.285s 6.268s
6.387s 10.954s
6.377s 6.227s
8.888s 6.347s
6.304s 6.286s
6.258s 10.302s
6.975s 6.260s
6.663s 6.847s
6.359s 6.313s
6.344s 6.335s
7.762s 6.533s
6.310s 9.418s
8.972s 6.370s
6.383s 6.357s
Fast die gesamte Zeit wird jedoch für Platten-I/O aufgewendet. Ich habe die Festplatten-I/O auf die allerletzte Datenzeile reduziert, also die äußere I/O for
Schleife wurde:
for (int i = COLS - 1; i < COLS; i++)
Die Timings wurden erheblich reduziert und sehr viel konsistenter:
Original Modified
0.168s 0.165s
0.145s 0.165s
0.165s 0.166s
0.164s 0.163s
0.151s 0.151s
0.148s 0.153s
0.152s 0.171s
0.165s 0.165s
0.173s 0.176s
0.171s 0.165s
0.151s 0.169s
Die Vereinfachung im Code durch das einmalige Ausschreiben des grässlichen Ausdrucks scheint mir sehr vorteilhaft zu sein. Ich würde dieses Programm sicherlich viel lieber pflegen müssen als das Original.
Auf welchem System arbeitest du? Haben Sie Zugriff auf eine Art Debugger (gdb, Debugger von Visual Studio usw.)?
Das würde uns wertvolle Informationen liefern, wie die Codezeile, wo das Programm abstürzt… Außerdem kann die Menge an Speicher untragbar sein.
Darf ich Ihnen außerdem empfehlen, die numerischen Grenzen durch benannte Definitionen zu ersetzen?
Als solche:
#define DIM1_SZ 1000
#define DIM2_SZ 1000000
Verwenden Sie diese immer dann, wenn Sie sich auf die Array-Dimensionsgrenzen beziehen möchten. Es hilft, Tippfehler zu vermeiden.
Führen Sie Ihr Programm mit aus Valgrind von verlinkt Verteidigung. Das wird Ihnen sagen, wo der Zeiger dereferenziert wird, und höchstwahrscheinlich Ihr Problem beheben, wenn Sie alle Fehler beheben, von denen sie Ihnen erzählen.
An welchem Punkt tritt der Segfault auf?
– Jeff Mercado
6. Oktober 2012 um 19:10 Uhr
^ Versuchen Sie, Valgrind zu verwenden, um die Zeilennummer zu erhalten, in der der Segfault auftritt. Es ist im Allgemeinen ziemlich offensichtlich, wenn Sie es auf eine Zeile eingegrenzt haben. Wenn nicht, posten Sie, um welche Zeile es sich handelt, und wir können helfen.
– 1”
6. Oktober 2012 um 19:11 Uhr
Wenn Sie Ihren Code sehen, muss Ihr Compiler zunächst einen Segmentierungsfehler aufweisen …
– Gefahrenhirn
6. Oktober 2012 um 19:12 Uhr
Es kompiliert, es sagt Segmentierungsfehler, wenn ich es ausführe, ./Program
– Ariramnes
6. Oktober 2012 um 19:14 Uhr
Ihr Programm wird segfault, wenn es nicht erstellen kann P1.txt. Sie sollten das System immer fopen (und andere Routinen, die fehlschlagen können) für Fehler.
– Jim Balter
6. Oktober 2012 um 20:06 Uhr