Allocazione memoria

di il
7 risposte

Allocazione memoria

Salve sono uno studente di Fisica e mi sono appena iscritto a questo forum,
colgo l'occasione per rigraziarvi della cortese attenzione.
Ho scritto un algoritmo di calcolo scientifico in c. Non sono molto pratico di programmazione, per cui ho preferito usare una logica top-down. Il massimo della programmazione modulare da me usata, è stata definire qualche funzione che alleggerisse il codice pricipale.
Il codice da me scritto è molto oneroso dal punto di vista della memoria, oltre che del tempo di calcolo. Giusto per far capire di cosa si tratta, ho implementato l'algoritmo Runge-Kutta per un sistema fisico a molti corpi di N (27) particelle, soggette ad un potenziale radiale.
Capita che ad un certo punto del programma (al primo passo dell'algoritmo), un array si annulli senza alcuna apparente spiegazione . Credo che ciò sia dovuto ad un'errata allocazione della memoria. Potete consigliarmi come definire in modo più efficiente le variabili, per piacere?
È consigliabile usare una logica di programmazione orientata agli oggetti ? non sono molto pratico con le struct...
posto la parte di codice in cui definisco le variabili:



#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <malloc.h>
#define N 8 //lato del cubo
#define l 0.9 //coefficiente distanza relativa posizioni iniziali

double r0=5.336; //i valori dei parametri si riferscono all'atomo di sodio
double D=-0.06334;
double a=0.58993;
double m=23;
double t=0.001;//unità temporale
double gx[N],gy[N],gz[N];

double acc(double, double, double);//questa funzione calcola l'accelerazione tra due punti
double potenziale(double);//questa funzione calcola il potenziale tra due punti
void sum_acc(double x[] , double y[], double z[],double gx[],double gy[], double gz[]);
//la funzione predendente calcola l'accelerazione gz, gy, gz per tutti i punti.
void main(void)


{
int i=0,j=0,p=0;
double ii,dummy,r,b;
for (i=0;i<N;i++)
{
gx[i]=0;
gy[i]=0;
gz[i]=0;
}


const int M=0.001*pow(t,-1);////////////////////////////////////////////////////////////////////////////////////////////////////

//condizioni iniziali: costruisco il reticolo cubico di lato N
//l'ordine con cui sono costruiti i punti del cubo rispetta l'ordine con cui vengono costruite le matrici in matlab
//in questo modo è possibile utilizzare i dati ottenuti dal programma in c in una matrice matlab per graficarli.
 double x[N],y[N],z[N],xp[N],yp[N],zp[N];
double **X,**Y,**Z; //matrici NxM, create per registrare le posizioni degli N atomi in tutti gli M passi
double vx[N],vy[N],vz[N];
double **Vx,**Vy,**Vz;

//allocazione dinamica della memoria
X=(double **)malloc(N * sizeof(double *));
Y=(double **)malloc(N * sizeof(double *));
Z=(double **)malloc(N * sizeof(double *));
Vx=(double **)malloc(N * sizeof(double *));
Vy=(double **)malloc(N * sizeof(double *));
Vz=(double **)malloc(N * sizeof(double *));
for(i=0;i<N;i++)
{
X[i]=(double *)malloc(M * sizeof(double));
Y[i]=(double *)malloc(M * sizeof(double));
Z[i]=(double *)malloc(M * sizeof(double));
Vx[i]=(double *)malloc(M * sizeof(double));
Vy[i]=(double *)malloc(M * sizeof(double));
Vz[i]=(double *)malloc(M * sizeof(double));
}
for(i=0;i<N;i++)
{
   ii=i+1;
   z[i]=ceil(ii/pow(N,0.666666667));
   dummy=ii-(z[i]-1)*pow(N,0.666666667);
   x[i]=round(dummy-floor(dummy/pow(N,0.333333333))*pow(N,0.333333333));
   b=(int)x[i];
   if(b==0) 
   {
   x[i]=pow(N,0.333333333);
   y[i]=dummy/pow(N,0.333333333);
   } 
   else
   {
   y[i]=floor(dummy/pow(N,0.333333333))+1;  
  
   }
  
   x[i]=l*r0*x[i];
   y[i]=l*r0*y[i];
   z[i]=l*r0*z[i];
   vx[i]=0;
   vy[i]=0;
   vz[i]=0;
   X[i][0]=x[i];
   Y[i][0]=y[i];
   Z[i][0]=z[i];
   Vx[i][0]=vx[i]; 
   Vy[i][0]=vy[i];
   Vz[i][0]=vz[i];

}


//inizializzo l'energia potenziale, cinetica e meccanica
double V[M],Ec[M],E[M],rg[M];


//utilizzo file per registrare dati, necessari alla rappresentazione grafica del risultato
FILE *fileX,*fileY,*fileZ,*fileVx,*fileVy,*fileVz, *fileE,*fileEc,*fileV;
fileX = fopen("RKdatiX.txt","wt");
fileY = fopen("RKdatiY.txt","wt");
fileZ = fopen("RKdatiZ.txt","wt");
fileVx = fopen("RKdatiVx.txt","wt");
fileVy = fopen("RKdatiVy.txt","wt");
fileVz = fopen("RKdatiVz.txt","wt");
fileE = fopen("RKdatiE.txt","wt");
fileEc = fopen("RKdatiEc.txt","wt");
fileV = fopen("RKdatiV.txt","wt");
//algoritmo Runge-kutta

double cx[4][2][N],cy[4][2][N],cz[4][2][N];


7 Risposte

  • Re: Allocazione memoria

    Al momento, per quello che devi fare, la programmazione OOP non ti serve a nulla.

    Devi implementare l'algoritmo in modo pulito:

    1) il codice che hai postato non e' sufficiente per capire che cosa c'e' che non va: da una prima analisi, sembra sia tutto corretto
    2) non serve che posti il codice completo fino a che non metti a posto quello attuale
    3) per metterlo a posto la prima cosa che devi fare e': non si scrive codice mediante copia/incolla
    3.1) visto che devi allocare diverse matrici, implementa una funzione che alloca una singola matrice, e ritorna il relativo puntatore
    3.2) quindi le tue variabili X,..Vz verranno inizializzate mediante la chiamata a questa funzione.
    4) stesso discorso per il resto del codice

    Fondamentalmente, devi ridurre la dimensione del codice, riorganizzandolo in piccole funzioni gestibili.

    Lo so che non e' il tuo mestiere, ma il nostro, ma un lavoro fatto male, e' fatto male, ed infatti non ti funziona

    ...

    Se durante questa operazione di rifattorizzazione non trovi il bug, riposti il tutto e si vede.

    Attenzione: affiche' il forum (io o qualcun altro) possa darti una mano, serve non solo che il programma sia compilabile, ma anche i dati di ingresso, per fare le prove!

    O alleghi i file dati, o, moooolto meglio, scrivi un main che chiama la tua funzione di ricerca della soluzione.

    Non serve un sistema di equazioni con 1.000.000 di elementi!!!
    Devi progettare il caso di test minimo ma sufficiente per generare il problema.

    Nota/1: la realizzazione dei casi di test e' un concetto di cui dovresti essere abbondantemente padrone !!!!

    Nota/2: Aaaah, se avessi studiato Fisica .... sigh, sigh, sob, sob!

    Nota/3: a proposito, perche' non usi una libreria gia' pronta? Ad esempio GNU Scientific Library



    In questo modo ottieni due effetti positivi:

    1) sai per certo che l'implementazione e' corretta
    2) chi ha implementato il codice molto probabilmente ha usato delle tecniche di ottimizzazione del calcolo, ad esemio sfruttando il supporto al calcolo vettoriale del processore, realizzando cosi' un'implementazione decisamente piu' efficiente.
  • Re: Allocazione memoria

    Perdonami ma dove ti da errore, nell'allocazione o nell'apertura dei file?
    Perché usi wt e non w, quale modalità di scrittura del file?
  • Re: Allocazione memoria

    Grazie per gli ottimi consigli, li seguirò (mi metto subito al lavoro).
    Il codice compila. Il problema è che non fa ciò per cui è stato progettato. Ho già provato a fare un test, perchè ho scritto un programma che fa la stessa cosa con un altro algoritmo, quindi conosco i risultati, ad ogni passo.
    Sono andato ad analizzare variabile per variabile, stampando sullo schermo il valore della variabile dopo l'esecuzione di ogni comando del codice. Sono sicuro che l'algoritmo è corretto. Solo che ad un certo punto una variabile si annulla senza motivo. Allora ho provato ad inserire "a mano" in quel punto del codice il valore corretto della variabile. In conseguenza di ciò, in quel punto il programma va in segmentation fault.

    Non so perchè ho usato wt al posto di w .
  • Re: Allocazione memoria

    Controlla bene l'uso degli indici (se e' una matrice ci devono essere 2 indici), e il fatto che gli indici non escano dal range di validita' (se il vettore e' di N elementi, l'indice va da 0.. a N-1, NON da 1 a N o da 0 N). Inoltre di non aver scambiato le righe con le colonne!

    La frase una variabile si annulla dal punto di vista informatico vuol dire che il suo valore diventa 0.
    Ma qui' non si sa quale sia la variabile, perche' di variabili ce ne sono tante e nemmeno di struttura semplice (sono vettori di vettori!).

    Comunque, segmentation fault vuol dire che si sta' cercando di andare a scrivere in una zona della memoria del programma non accessibile. Sintomo di indici gestiti male.
  • Re: Allocazione memoria

    Ho provato a creare la seguente funzione per allocare la memoria, ma mi dà segmentation fault.
    
    double** alloca(int M)
    {
      int i; 
     double **X;
    X=(double **)malloc(N * sizeof(double *));
    for(i=0;i<N;i++)
    {
    X[i]=(double *)malloc(M * sizeof(double));
    }
     return X;
    }
    
    La variabile che si annulla è il vettore x[N], che è allocato staticamente. Pubblico una parte del codice per cercare di far capire meglio:
    all'inizio di questa parte il vettore ha il valore che deve avere.
    
    for(i=0;i<N;i++)
       {
         cx[3][1][i]=t*gx[i];
         cx[3][2][i]=t*(vx[i]+0.5*cx[2][1][i]);
         cy[3][1][i]=t*gy[i];
         cy[3][2][i]=t*(vy[i]+0.5*cy[2][1][i]);
         cz[3][1][i]=t*gz[i];
         cz[3][2][i]=t*(vz[i]+0.5*cz[2][1][i]);
       }
    	 
    
    subito dopo questo ciclo for, il valore di tutto il vettore si annulla, nonostante all'interno del ciclo questa variabile non sia proprio chiamata.
  • Re: Allocazione memoria

    Cx/cy/cz sono definiti come:

    double cx[4][2][N], ...

    e li usi come:

    cx[3][2][k] = ...

    Il secondo indice puo' essere SOLO 0 o 1, NON 2.

    Non e' che per caso, stai ragionando da fisico e usi gli indici che iniziano da 1?
    Mi pare che i Fortran gli indici iniziano da 1. In C sempre da 0!

    Mi sa che stai sovvrascrivendo lo stack, con conseguenti comportamenti imprevedibili!
    Come quello che descrivi

    Il tuo primo bug pestifero (c'e' un'altro termine per descriverlo che inizia con 'b' )


    Nota/1: e' una pessima pratica quella di dipendere da variabili globali. Alla funzione alloca dovresti passare anche N come parametro, non solo M.

    In qualche modo, ogni funzione dovrebbe essere completamente descritta solo dai suoi parametri e dalla sua implementazione.

    Certo, PIGreco puo' essere definito in modo globale, ma in questo caso N e' un parametro. Nessuno impedisce di cambiarlo.

    Altra cosa: invece di avere tre vettori, per x,y,z, prova a pensare al concetto di dato in 3D.
    Potresti usare una struttura del tipo:
    
    typedef struct d3d {
       double x,y,z;
    } d3d;
    ...
    d3d c[4][2][N], ...
    ...
    c[3][1][i].x = ...
    
    Cosi riduci ad 1/3 il numero di oggetti che manipoli, senza perdita di performance, e migliori la leggibilita'


    Nota/2: se ti e' piu' comodo ragionare con gli indici che iniziano da 1, c'e' un trucco semplice:

    le tue strutture dati devono avere N+1 elementi

    in questo modo il tuo indice puo' andare da 1 a N.

    Sprechi un po' di spazio (quello relativo all'indice 0), ma chissene....
  • Re: Allocazione memoria

    Ti ringrazio, ora il programma funziona!
    sono proprio contento, ora cercherò di migliorare il codice.
Devi accedere o registrarti per scrivere nel forum
7 risposte