J'ai un problème avec un programme, j'ai cherché des erreurs de segmentation, mais je ne les comprends pas très bien. La seule chose que je sache, c'est que j'essaie probablement d'accéder à une mémoire que je ne devrais pas. Le problème est que je vois mon code et ne comprends pas ce que je fais mal.
#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);
}
Merci pour votre temps.
Cette déclaration:
double F[1000][1000000];
occuperait 8 * 1000 * 1000000 octets sur un système x86 typique. C'est environ 7,45 Go. Il est probable que votre système manque de mémoire lorsque vous essayez d'exécuter votre code, ce qui entraîne une erreur de segmentation.
Votre matrice occupe environ 8 Go de mémoire (1 000 x 1 000 000 x taillede (double) octets). Cela pourrait être un facteur dans votre problème. Il s’agit d’une variable globale plutôt que d’une variable de pile; vous êtes donc OK, mais vous repoussez les limites ici.
Écrire autant de données dans un fichier va prendre un certain temps.
Vous ne vérifiez pas que le fichier a été ouvert avec succès, ce qui pourrait également être une source de problèmes (s'il échouait, une erreur de segmentation serait très probable).
Vous devriez vraiment introduire des constantes nommées pour 1 000 et 1 000 000; que représentent-ils?
Vous devriez également écrire une fonction pour faire le calcul; vous pouvez utiliser une fonction inline
dans C99 ou une version ultérieure (ou C++). La répétition dans le code est insoutenable à voir.
Vous devez également utiliser la notation C99 pour main()
, avec le type de retour explicite (et de préférence void
pour la liste d'arguments lorsque vous n'utilisez pas argc
ou argv
):
int main(void)
Par curiosité oisive, j'ai pris une copie de votre code, changé toutes les occurrences de 1000 en ROWS, toutes les occurrences de 1000000 en COLS, puis créé enum { ROWS = 1000, COLS = 10000 };
(réduisant ainsi la taille du problème d'un facteur 100). J'ai apporté quelques modifications mineures afin que la compilation se fasse proprement sous mon ensemble d'options de compilation préféré (rien de grave: static
devant les fonctions, et le tableau principal; file
devient un local à main
; erreur vérifier la fopen()
, etc.).
J'ai ensuite créé une deuxième copie et créé une fonction en ligne pour effectuer le calcul répété (et une seconde pour effectuer des calculs en indice). Cela signifie que l'expression monstrueuse n'est écrite qu'une seule fois - ce qui est hautement souhaitable, car elle garantit la cohérence.
#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);
}
Ce programme écrit sur P2.txt
au lieu de P1.txt
. J'ai exécuté les deux programmes et comparé les fichiers de sortie; la sortie était identique. Lorsque j’exécutais les programmes sur un ordinateur quasiment inactif (MacBook Pro, Intel Core i7 à 2,3 GHz, 16 GiB 1333 MHz, Mac OS X 10.7.5, GCC 4.7.1), j’ai eu raison, mais pas timing totalement cohérent:
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
Cependant, presque tout ce temps est consacré aux E/S du disque. J'ai réduit les E/S du disque à la toute dernière ligne de données. La boucle externe E/S for
est ainsi devenue:
for (int i = COLS - 1; i < COLS; i++)
les timings ont été considérablement réduits et beaucoup plus cohérents:
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
Il me semble que la simplification du code consistant à écrire l'expression horrible une seule fois est très bénéfique. Je préférerais certainement de loin maintenir ce programme plutôt que l'original.
Sur quel système utilisez-vous? Avez-vous accès à une sorte de débogueur (gdb, le débogueur de Visual Studio, etc.)?
Cela nous donnerait des informations précieuses, telles que la ligne de code où le programme se bloque ... De plus, la quantité de mémoire peut être prohibitive.
De plus, puis-je vous recommander de remplacer les limites numériques par des définitions nommées?
En tant que tel:
#define DIM1_SZ 1000
#define DIM2_SZ 1000000
Utilisez-les chaque fois que vous souhaitez faire référence aux limites de dimension du tableau. Cela aidera à éviter les erreurs de frappe.