2012-10-06 26 views
29

Tengo un problema con algún programa, he buscado fallas de segmentación, por lo que no las entiendo bastante bien, lo único que sé es que presumiblemente estoy intentando acceder a algún recuerdo no deberia El problema es que veo mi código y no entiendo lo que estoy haciendo mal.falla de segmentación: 11

#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); 
} 

Gracias por su tiempo.

+0

¿En qué punto ocurre la segfault? –

+1

^Pruebe usar Valgrind para obtener el número de línea donde ocurre la segfault. En general, es bastante obvio una vez que lo has reducido a una línea; si no, publica qué línea es y te podemos ayudar. –

+3

viendo su código, primero su compilador debe haber fallado en la segmentación ... – perilbrain

Respuesta

62

Esta declaración:

double F[1000][1000000]; 

ocuparía 8 * 1000 * 1000000 bytes en un sistema x86 típico. Esto es aproximadamente 7,45 GB. Lo más probable es que su sistema se esté quedando sin memoria cuando intente ejecutar su código, lo que genera un error de segmentación.

+0

gracias, eso fue todo. – Ariaramnes

1

¿En qué sistema se está ejecutando? ¿Tiene acceso a algún tipo de depurador (gdb, depurador de Visual Studio, etc.)?

Eso nos daría información valiosa, como la línea de código donde el programa falla ... Además, la cantidad de memoria puede ser prohibitiva.

Además, ¿puedo recomendarle que reemplace los límites numéricos por definiciones con nombre?

Como tal:

#define DIM1_SZ 1000 
#define DIM2_SZ 1000000 

Uso aquellos siempre que lo desee para referirse a los límites de dimensión matriz. Ayudará a evitar errores de tipeo.

0

Ejecute su programa con valgrind de vinculado a efence. Esto le indicará dónde se está desreferenciando el puntero y probablemente solucione su problema si soluciona todos los errores de los que le informan.

29

Su matriz ocupa aproximadamente 8 GB de memoria (1,000 x 1,000,000 x sizeof (double) bytes). Eso podría ser un factor en tu problema. Es una variable global en lugar de una variable de pila, por lo que puede estar bien, pero aquí se están limitando los límites.

Escribir esa cantidad de información en un archivo llevará un tiempo.

No comprueba si el archivo se abrió correctamente, lo que también podría ser un problema (si falla, es muy probable que se produzca un error de segmentación).

Realmente debe introducir algunas constantes con nombre para 1,000 y 1,000,000; ¿Qué representan?

También debe escribir una función para hacer el cálculo; podría usar una función inline en C99 o posterior (o C++). La repetición en el código es insoportable de contemplar.

También debe utilizar la notación C99 para main(), con el tipo de retorno explícito (y preferiblemente void de la lista de argumentos cuando no se está utilizando argc o argv):

int main(void) 

por curiosidad , Tomé una copia de su código, cambié todas las ocurrencias de 1000 a ROWS, todas las ocurrencias de 1000000 a COLS, y luego creé enum { ROWS = 1000, COLS = 10000 }; (reduciendo así el tamaño del problema por un factor de 100).Hice algunos cambios menores para compilar limpiamente bajo mi conjunto preferido de opciones de compilación (nada serio: static en frente de las funciones, y la matriz principal; file se convierte en local a main; verifique el error fopen(), etc.).

Luego creé una segunda copia y creé una función en línea para hacer el cálculo repetido, (y una segunda para hacer los cálculos del subíndice). Esto significa que la expresión monstruosa solo se escribe una vez, lo que es muy deseable ya que garantiza la coherencia.

#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); 
} 

Este programa escribe en P2.txt en lugar de P1.txt. Ejecuté ambos programas y comparé los archivos de salida; la salida fue idéntica. Cuando ejecuté los programas en una máquina mayormente inactiva (MacBook Pro, Intel Core i7 a 2.3 GHz, 16 GiB 1333 MHz RAM, Mac OS X 10.7.5, GCC 4.7.1), obtuve un tiempo razonable pero no del todo coherente:

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 

Sin embargo, casi todo ese tiempo se gasta en E/S de disco. Reduje el disco I/O para sólo la última fila de datos, por lo que el bucle externo de E/S for convertí:

for (int i = COLS - 1; i < COLS; i++) 

los tiempos fueron muy reducido y mucho más consistente:

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 

La simplificación en el código de tener la expresión espantosa escrita una sola vez es muy beneficiosa, me parece a mí. Definitivamente tendré que mantener ese programa mucho mejor que el original.

Cuestiones relacionadas