OpenMP #pragma omp parallel for

时间:2015-11-15 21:24:23

标签: multithreading parallel-processing openmp multicore pragma

我正在尝试改进我的C源代码以并行执行。我有一个四核CPU,所以我认为4是一个很好的线程(一个用于CPU)运行我的程序真的比顺序代码优化更快。

但它不起作用。没有OpenMP的代码需要11分钟才能执行,并且并行代码超过11分钟。我报告了我的所有来源,但并行代码仅在getBasin()函数中。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <omp.h>

#define BLD "\x1B[1m"
#define RED "\x1B[31m"
#define GRN "\x1B[32m"
#define RST "\x1B[0m"

struct point{
  double x;
  double v;
  double t;
  double E0;
  double E;
  double dE;
} typedef point;

struct parametri {
    double gamma;
    double epsilon;
    double dt;
    double t_star;
    double t_basin;
    double t_max;
    double x0;
    double v0;
    int choice;
    int alg[1];
    double dx;
} typedef parametri;


// Prototipi delle funzioni
void Setup();
void getParams(parametri *pars);
void getError(point *xv, parametri *pars, int *i, int k, int *n_passi, double *xn1, double *vn1);
void getBasin(point *xv, parametri *pars, int *h, int *n_passi, double *xn1, double *vn1);

double f(double x, double v, parametri *pars);

void algEulero(point *xv, parametri *pars, double *xn1, double *vn1);
void algEuleroCromer(point *xv, parametri *pars, double *xn1, double *vn1);
void algPuntoDiMezzo(point *xv, parametri *pars, double *xn1, double *vn1);
void algVerlet(point *xv, parametri *pars, double *xn1, double *vn1);
void algRungeKutta2(point *xv, parametri *pars, double *xn1, double *vn1);

int main(void) {

    // Inizializzo il display per l'utente. Maggiori informazioni vedere funzione Setup();
    Setup();

    // Dichiaro e recupero i parametri richiesti per il corretto funzionamento del programma;
    parametri pars;
    getParams(&pars);

    // Dichiaro e ricavo i parametri essenziali, annesse variabili di supporto;
    int i, n_passi = pars.t_max/pars.dt;
    double dt0, xn1, vn1, t_star = 5.;
    point xv;

    // Imposto i parametri iniziali;
    xv.x = pars.x0;
    xv.v = pars.v0;
    xv.E0 = 0.5*(xv.v)*(xv.v) - (xv.x)*(xv.x)/8. + (xv.x)*(xv.x)*(xv.x)*(xv.x)/4.;
    xv.E = xv.E0;
    xv.dE = 0;
    xv.t = 0;
    pars.t_star = 5;
    pars.t_basin = 60;
    dt0 = pars.dt;

    // Formato dell'output;
    printf ("t\tx(t)\tv(t)\tE(t)\tdE\n");

    if ((pars.choice == 1) || (pars.choice == 3) || (pars.choice == 4)) { // L'utente ha deciso di affrontare il primo/terzo/quarto esercizio;

        // Avverto l'utente che il processo sta per iniziare. Può risultare inutile su tempi brevi, ma efficace su tempi molto lunghi;
        fprintf(stderr, "\nAvvio integrazione numerica... ");       

        if (pars.alg[0] == 1) { // L'utente ha selezionato l'algoritmo di Eulero;
            for (i=0; i<n_passi; i++){
                algEulero(&xv, &pars, &xn1, &vn1);
            }
        } else if (pars.alg[0] == 2) { // L'utente ha selezionato l'algoritmo di EuleroCromer;
            for (i=0; i<n_passi; i++){
                  algEuleroCromer(&xv, &pars, &xn1, &vn1);
            }
        } else if (pars.alg[0] == 3) { // L'utente ha selezionato l'algoritmo di PuntoDiMezzo;
            for (i=0; i<n_passi; i++){
                algPuntoDiMezzo(&xv, &pars, &xn1, &vn1);
            }
        } else if (pars.alg[0] == 4) { // L'utente ha selezionato l'algoritmo di Verlet;
            for (i=0; i<n_passi; i++){
                algVerlet(&xv, &pars, &xn1, &vn1);
            }
        } else if (pars.alg[0] == 5) { // L'utente ha selezionato l'algoritmo di RungeKutta di ordine 2;
            for (i=0; i<n_passi; i++) { 
                algRungeKutta2(&xv, &pars, &xn1, &vn1);
            }
        }

        // Algoritmo terminato;
        fprintf(stderr, "[%s%sDONE%s]\n\n", BLD, GRN, RST);

    } else if (pars.choice == 2) { // L'utente ha deciso di affrontare il secondo esercizio;

        // Seleziono il secondo algoritmo da confrontare
        do {
            fprintf(stderr, "> Selezionare il secondo algoritmo:\n");
            fprintf(stderr, "\t>> [1] Eulero\n");
            fprintf(stderr, "\t>> [2] EuleroCromer - RungeKutta (1 ordine)\n");
            fprintf(stderr, "\t>> [3] PuntoDiMezzo\n");
            fprintf(stderr, "\t>> [4] Verlet\n");
            fprintf(stderr, "\t>> [5] RungeKutta (2 ordine)\n");
            fprintf(stderr, "\t>> ");
            scanf("%d", &pars.alg[1]);
        } while (( pars.alg[1] <= 0 ));

        // Avverto l'utente che il processo sta per iniziare. Può risultare inutile su tempi brevi, ma efficace su tempi molto lunghi;
        fprintf(stderr, "\nAvvio calcolo errori... ");

        // Eseguo lo studio degli errori d'integrazione mediante il primo e secondo algoritmo scelto, rispettivamente nei file error_1.dat, error_2.dat;
        getError(&xv, &pars, &i, 0, &n_passi, &xn1, &vn1);

        // Resetto le variabili e richiamo l'algoritmo per calcolare gli errori;
        pars.dt = dt0;
        n_passi = pars.t_max/pars.dt;
        xv.x = pars.x0;
        xv.v = pars.v0;
        xv.E = xv.E0;
        xv.dE = 0;
        xv.t = 0;

        getError(&xv, &pars, &i, 1, &n_passi, &xn1, &vn1);

        // Processo terminato;
        fprintf(stderr, "\n\nStato: [%s%sDONE%s]\n\n", BLD, GRN, RST);

    } else if (pars.choice == 5) { // L'utente ha deciso di affrontare il quinto esercizio;

        // Avverto l'utente che il processo sta per iniziare. Può risultare inutile su tempi brevi, ma efficace su tempi molto lunghi;
        fprintf(stderr, "\nAvvio calcolo griglia... ");

        getBasin(&xv, &pars, &i, &n_passi, &xn1, &vn1);

        // Processo terminato;
        fprintf(stderr, "[%s%sDONE%s]\n\n", BLD, GRN, RST);

    } else { // L'utente non ha selezionato un esercizio valido;
        fprintf(stderr, "\n[%s%sFAILED%s] Esercizio non disponibile.\n", BLD, RED, RST);
        exit(EXIT_FAILURE);
    }

    return 0;
}


void Setup() {

    fprintf(stderr, "\nAnalisi numerica di un'equazione differenziale\n");
    fprintf(stderr, "==============================================\n\n");

}

void getParams(parametri *pars) {

    do {
        fprintf(stderr, "> Inserire un valore per gamma      : ");
        scanf("%lf", &pars->gamma);
    } while (pars->gamma < 0);

    do {
        fprintf(stderr, "> Inserire un valore per epsilon    : ");
        scanf("%lf", &pars->epsilon);
    } while (pars->epsilon < 0);

    do {
        fprintf(stderr, "> Inserire un valore per dt         : ");
        scanf("%lf", &pars->dt);
    } while (pars->dt < 0);

    do {
        fprintf(stderr, "> Inserire un valore per tmax t.c.  :\n");
        fprintf(stderr, "  >> (tmax > 5) per I eserc.\n");
        fprintf(stderr, "  >> (tmax > 60) per V eserc.       : ");
        scanf("%lf", &pars->t_max);
    } while (pars->t_max < 0);

    do {
        fprintf(stderr, "> Inserire un valore per x(0)       : ");
        scanf("%lf", &pars->x0);
    } while (pars->x0 < -1);

    do {
        fprintf(stderr, "> Inserire un valore per v(0)       : ");
        scanf("%lf", &pars->v0);
    } while (pars->v0 < -1);

    do {
        fprintf(stderr, "> Selezionare l'esercizio richiesto :\n");
        fprintf(stderr, "\t>> [1] Esercizio 1\n");
        fprintf(stderr, "\t>> [2] Esercizio 2\n");
        fprintf(stderr, "\t>> [3] Esercizio 3\n");
        fprintf(stderr, "\t>> [4] Esercizio 4\n");
        fprintf(stderr, "\t>> [5] Esercizio 5\n\n");

        fprintf(stderr, "\t>> ");
        scanf("%d", &pars->choice);
    } while (( pars->choice <= 0 ));

    do {
        fprintf(stderr, "> Selezionare l'algoritmo voluto    :\n");
        fprintf(stderr, "\t>> [1] Eulero\n");
        fprintf(stderr, "\t>> [2] EuleroCromer - RungeKutta (1 ordine)\n");
        fprintf(stderr, "\t>> [3] PuntoDiMezzo\n");
        fprintf(stderr, "\t>> [4] Verlet\n");
        fprintf(stderr, "\t>> [5] RungeKutta (2 ordine)\n\n");
        fprintf(stderr, "\t>> ");
        scanf("%d", &pars->alg[0]);
    } while (( pars->alg[0] <= 0 ));

}

void getError(point *xv, parametri *pars, int *i, int k, int *n_passi, double *xn1, double *vn1) {
    void (*algF)(point *, parametri *, double *, double *);
    int j, n = 0;
    FILE *fp;

    // Questo if controlla se il codice sta eseguendo lo studio degli errori per il primo o secondo algoritmo (pars->alg[k]);
    if (k == 0) {
        fp = fopen("error_1.dat", "w+");
    } else {
        fp = fopen("error_2.dat", "w+");
    }

    // Assegno il puntatore corretto a seconda dell'algoritmo scelto;
    if (pars->alg[k] == 1) {
        algF = algEulero;
    } else if (pars->alg[k] == 2) {
        algF = algEuleroCromer;
    } else if (pars->alg[k] == 3) {
        algF = algPuntoDiMezzo;
    } else if (pars->alg[k] == 4) {
        algF = algVerlet;
    } else if (pars->alg[k] == 5) {
        algF = algRungeKutta2;
    } else {
        fprintf(stderr, "\n[%s%sFAILED%s] E' stato selezionato un algoritmo non valido.\n", BLD, RED, RST);
        exit(EXIT_FAILURE);
    }

    // Informo l'utente che il processo sta iniziando;
    fprintf(stderr, "\n>> Avvio %d algoritmo... ", k+1);

    // Formattazione dell'output del file contenente gli errori;
    fprintf(fp, "dt\tE(t*)\tE(0)\tdE/E(0)\t# passi\ti\tt\n");

    for (j=0; j<8; j++) {

        for ((*i)=0; (*i)<(*n_passi); (*i)++){
            (*algF)(xv, pars, xn1, vn1);

            if (((pars->t_star - pars->dt/2. <= xv->t) && (xv->t >= pars->t_star + pars->dt/2.)) && (n == 0)) {
                fprintf(fp, "%+.14lf\t%+.14lf\t%+.14lf\t%+.14lf\t%+.14d\t%+.14d\t%+.14lf\n", pars->dt, xv->E, xv->E0, (xv->E - xv->E0)/xv->E0, (*n_passi), (*i), xv->t);
                n = 1;
            }
        }

        // Resetto le variabili per rilanciare l'algoritmo con dt/2^j
        n = 0;
        xv->t = 0;
        xv->x = pars->x0;
        xv->v = pars->v0;
        pars->dt = pars->dt/2.;
        (*n_passi) = pars->t_max/pars->dt;
        (*xn1) = 0;
        (*vn1) = 0;
        xv->E = xv->E0;
    }

    fclose(fp);

    fprintf(stderr, "[%s%sDONE%s]", BLD, GRN, RST);
}

void getBasin(point *xv, parametri *pars, int *h, int *n_passi,  double *xn1, double *vn1) {

    // Dichiaro e setto i parametri che delimitano la griglia;
    point xv1;
    pars->dx = 0.01;
    xv1.x = -1;
    xv1.v = -1;

    // Dichiaro le variabili necessarie per il bacino d'attrazione;
    int i, j, i_max = 2./pars->dx, j_max = 2./pars->dx;
    void (*algF)(point *, parametri *, double *, double *);
    FILE *fp = fopen("basin.dat", "w+");

    // Assegno il puntatore corretto a seconda dell'algoritmo scelto;
    if (pars->alg[0] == 1) {
        algF = algEulero;
    } else if (pars->alg[0] == 2) {
        algF = algEuleroCromer;
    } else if (pars->alg[0] == 3) {
        algF = algPuntoDiMezzo;
    } else if (pars->alg[0] == 4) {
        algF = algVerlet;
    } else if (pars->alg[0] == 5) {
        algF = algRungeKutta2;
    } else {
        fprintf(stderr, "\n[%s%sFAILED%s] E' stato selezionato un algoritmo non valido.\n", BLD, RED, RST);
        exit(EXIT_FAILURE);
    }

    // Formattazione output file basin.dat;
    fprintf(fp, "x(0)\tx'(0)\tx(t*)\tv(t*)\n");

    omp_set_num_threads(4);

    #pragma omp parallel for
    // Eseguo il for della griglia sull'asse x';
    for (j=0; j<=j_max; j++) {      

        // Eseguo il for della griglia sull'asse x;
        for (i=0; i<=i_max; i++) {
            fprintf(fp, "%lf\t%lf\t", xv1.x, xv1.v);

            xv->x = xv1.x;
            xv->v = xv1.v;

            // Eseguo l'integrazione numerica
            for ((*h)=0; (*h)<(*n_passi); (*h)++) {
                (*algF)(xv, pars, xn1, vn1);

                // Entro in t = t*, stampo v(t*) ed esco dal ciclo;
                if ((pars->t_basin - pars->dt/2. <= xv->t) && (xv->t >= pars->t_basin + pars->dt/2.)) {
                    fprintf(fp, "%lf\t%lf\n", xv->x, xv->v);
                    break;
                }
            }

            xv1.x += pars->dx;
            xv->t = 0;
            (*xn1) = 0;
            (*vn1) = 0;
        }

        // Resetto la x e incremento la x';
        xv1.x = -1;
        xv1.v += pars->dx;
    }

}

double f(double x, double v, parametri *pars) {
    return 0.25*x - x*x*x + (pars->gamma - pars->epsilon*(x*x))*v;
}

void algEulero(point *xv, parametri *pars, double *xn1, double *vn1){
  printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE);
  *xn1 = xv->x + (xv->v)*(pars->dt);
  *vn1 = xv->v + f(xv->x, xv->v, pars)*(pars->dt);

  xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.;
  xv->dE = fabs(xv->E-xv->E0)/xv->E0;

  xv->t += (pars->dt);
  xv->x = *xn1;
  xv->v = *vn1;
}

void algEuleroCromer(point *xv, parametri *pars, double *xn1, double *vn1){
  printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE);
  xv->v = xv->v + f(xv->x, xv->v, pars)*(pars->dt);
  xv->x = xv->x + (xv->v)*(pars->dt);

  xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.;
  xv->dE = fabs(xv->E-xv->E0)/xv->E0;

  xv->t += (pars->dt);
}

void algPuntoDiMezzo(point *xv, parametri *pars, double *xn1, double *vn1) {
  printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE);
  *vn1 = xv->v + f(xv->x, xv->v, pars)*(pars->dt);
  xv->x = xv->x + (0.5*(xv->v + (*vn1)))*(pars->dt);

  xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.;
  xv->dE = fabs(xv->E-xv->E0)/xv->E0;

  xv->t += (pars->dt);
  xv->v = *vn1;
}

void algVerlet(point *xv, parametri *pars, double *xn1, double *vn1) {
  printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE);
  *xn1 = xv->x + xv->v*pars->dt + 0.5*(f(xv->x, xv->v, pars))*pars->dt*pars->dt;
  *vn1 = xv->v + 0.5*(f(xv->x, xv->v, pars) + f((*xn1), xv->v, pars))*pars->dt;

  xv->x = *xn1;
  xv->v = *vn1;

  xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.;
  xv->dE = fabs(xv->E-xv->E0)/xv->E0;

  xv->t += (pars->dt);
}


void algRungeKutta2(point *xv, parametri *pars, double *xn1, double *vn1) {
      printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE);
      *xn1 = xv->x + xv->v*pars->dt + 0.5*f(xv->x, xv->v, pars)*pars->dt*pars->dt;
      *vn1 = xv->v + f(xv->x + 0.5*xv->v*pars->dt, xv->v + 0.5*f(xv->x, xv->v, pars)*pars->dt, pars)*pars->dt;

      xv->x = *xn1;
      xv->v = *vn1;

      xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.;
      xv->dE = fabs(xv->E-xv->E0)/xv->E0;

      xv->t += (pars->dt);
}

------------------编辑-----------------

亲爱的吉尔斯, 我解释一下我的程序是做什么的。该程序旨在用数字解决具有不同算法的微分方程(algEulero,algEuleroCromer,algPuntoDiMezzo,algVerlet,algRungeKutta2)。它工作正常。物理方程是d ^ 2x / dt ^ 2 = f(x,v,gamma,epsilon)。这个f()正是你在我的代码中找到的f()。我的简单C程序的“大”问题是他非常慢:当我选择5'练习(pars.choice == 5)时,程序将完全执行:

1)使用pars.dx增量计算(在getBasin()中的两个)[-1,1] x [-1,1]的区域; 2)在x和y上的每个增量将启动算法,该算法用数字解决具有for的两个起始条件(x,y)的微分方程。当算法达到asynthotic t *(pars.t_basin)时,他将在basin.dat中写出x(t *)和v(t *)的输出。 3)(x,y)将改变并再次转到第(1)点。

现在,您可以使用以下参数进行测试:0.83,4,0.01,62,1,1,5,5。

我测试了你的代码,但并不比我的顺序代码快(例如超过11分钟)。为了改善它:

1)basin.dit输出的顺序是无关紧要的,因为我将根据3'和4'列值(Gnuplot上的图像)绘制空间(x,y)着色点。 2)为什么你还在函数getBasin()之前创建线程,而不仅仅是for()的两个?这不应该重复,对于每个线程,getBasin()?

抱歉我的英语和并行编程不好,我正在努力改进它在线阅读教程。

1 个答案:

答案 0 :(得分:4)

嗯,代码中明显的主要问题是并行版本(非常)错误。您可以在import java.io.*; import java.util.*; import java.util.ArrayList; /*is a collection of Appointment objects. As such, the class must include * a data structure to store an arbitrary number of Appointments*/ public class ApptBook implements Iterable { private ArrayList<Appointment> list; private Date startRange,endRange; public ApptBook(Date _startRange, Date _endRange) { ArrayList ls =new ArrayList(); endRange=_endRange; startRange=_startRange; } public void printAppointments(Date start, Date end) { startRange=start; endRange=end; Collections.sort(list); System.out.println("Result list:"); for(Appointment counter: list){ System.out.println(counter.toString()); } } public void saveToFile() throws FileNotFoundException, IOException { OutputStream f = new FileOutputStream("apptbook.dat"); OutputStreamWriter writer = new OutputStreamWriter(f); BufferedWriter out = new BufferedWriter(writer); int i; for(i=0;i<list.size();i++) { out.write("##\n"); out.write(list.get(i).forFile()); out.write("#\n"); } out.close(); } public void LoadFromFile() throws FileNotFoundException, IOException { InputStream f = new FileInputStream("apptbook.dat"); InputStreamReader reader = new InputStreamReader(f); BufferedReader in = new BufferedReader(reader); String str; while ((str = in.readLine()) != null) { if(str.equals("##"))//start read new object { //read date: str = in.readLine(); String []param=str.split(" "); //public Date(int _month, int _day, int _year) // <year> <month> <day> Date start=new Date(Integer.parseInt(param[1]),Integer.parseInt(param[2]),Integer.parseInt(param[0])); //read time: str = in.readLine(); String []paramTime=str.split(" "); Time time=new Time(Integer.parseInt(paramTime[0]),Integer.parseInt(paramTime[1])); int duration; str=in.readLine(); duration=Integer.parseInt(str); str=in.readLine(); Appointment newApp=new Appointment(start, time, duration, str); addAppt(newApp); str=in.readLine();//read # } } in.close(); } //should add a to this ApptBook, provided that a does not overlap //with an Appointment that is already stored. public boolean addAppt(Appointment a) { //check for overlap: int cursor; boolean isOverlap=false; for(cursor = 0;cursor<list.size();cursor++) if(a.overlaps(list.get(cursor))) { isOverlap=true; break; } if(!isOverlap) { list.add(a); } return isOverlap; } public boolean removeAppt(Date d, Time t) { throw new UnsupportedOperationException("removal not implemented"); } //@Override public Iterator iterator() { return new ApptBookIterator(list,startRange,endRange); } // Inner class example private class ApptBookIterator implements Iterator { /* ApptBookIterator */ private int cursor; private Date startRange; private Date endRange; ArrayList<Appointment> list; public ApptBookIterator(ArrayList<Appointment> _list,Date _startRange,Date _EndRange) { list=_list; startRange=_startRange; endRange=_EndRange; //find first in range: boolean isFind=false; for(cursor = 0;cursor<list.size();cursor++) { Appointment temp=list.get(cursor); if(temp.isInDateRange(startRange,endRange)) { isFind=true; break; } } if(!isFind) cursor=-1; } public boolean hasNext() { if(cursor==-1) return false; boolean isFind=false; for(int i=cursor;i<list.size();i++) { Appointment temp=list.get(i); if(temp.isInDateRange(startRange,endRange)) { isFind=true; break; } } if(!isFind) return false; return true; } public Integer next() { if(this.hasNext()) { for(;cursor<list.size();cursor++) { Appointment temp=list.get(cursor); if(temp.isInDateRange(startRange,endRange)) break; } } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException("Not supported yet."); } } } 区域之外定义所有变量,但不要声明其中任何变量parallel(甚至不是循环索引)。

此外,由于您将private函数的所有参数作为指针传递,因此将其声明为私有变得更加棘手。

但是,对代码的快速分析表明,尽管这些参数作为指针传递,但实际上在退出例程时并不关心它们的值。此外,看起来您尝试并行化的循环没有数据依赖性(尽管我没有对此进行全面的全面检查)。我找到的唯一明确的依赖关系是你打开的输出文件,在保持顺序顺序的同时并行更新是很棘手的...

因此,为了修复代码的并行化,我就这样做了:

  1. 通过值而不是引用将参数传递给getBasin()。这将生成一些额外的数据副本,但由于这些数据需要声明getBasin(),因此无论如何都需要一份副本。
  2. 将来自private区域内的getBasin()的电话封闭。实际上,我觉得这样做比较简单,而不是处理函数本身内部的数据私有化和IO问题。
  3. 每个线程打开一个输出文件的私有副本,名为&#34; basinXX.dat&#34;用&#34; XX&#34;作为当前线程的id,左边填充为0.这些文件将包含与当前线程对应的全局输出的份额。让我们希望这适合你。否则,处理打印的顺序可能会有点棘手。
  4. 在函数内部使用单个孤立parallel指令来并行循环。
  5. 有了这个,代码应该(希望)扩展得更好。但是,由于您没有指出用于计算的输入参数,我无法测试它,无论是正确性还是性能。所以它可能会悲惨地失败......

    无论如何,这是我提出的:

    omp for

    修改

    使用您提供的新输入参数,我能够测试代码,结果是:

    1. 我在给你的代码中有一个小错误(用于打开输出文件,使用#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <omp.h> #define BLD "\x1B[1m" #define RED "\x1B[31m" #define GRN "\x1B[32m" #define RST "\x1B[0m" struct point{ double x; double v; double t; double E0; double E; double dE; } typedef point; struct parametri { double gamma; double epsilon; double dt; double t_star; double t_basin; double t_max; double x0; double v0; int choice; int alg[1]; double dx; } typedef parametri; // Prototipi delle funzioni void Setup(); void getParams(parametri *pars); void getError(point *xv, parametri *pars, int *i, int k, int *n_passi, double *xn1, double *vn1); void getBasin(point xv, parametri pars, int h, int n_passi, double xn1, double vn1); double f(double x, double v, parametri *pars); void algEulero(point *xv, parametri *pars, double *xn1, double *vn1); void algEuleroCromer(point *xv, parametri *pars, double *xn1, double *vn1); void algPuntoDiMezzo(point *xv, parametri *pars, double *xn1, double *vn1); void algVerlet(point *xv, parametri *pars, double *xn1, double *vn1); void algRungeKutta2(point *xv, parametri *pars, double *xn1, double *vn1); int main(void) { // Inizializzo il display per l'utente. Maggiori informazioni vedere funzione Setup(); Setup(); // Dichiaro e recupero i parametri richiesti per il corretto funzionamento del programma; parametri pars; getParams(&pars); // Dichiaro e ricavo i parametri essenziali, annesse variabili di supporto; int i, n_passi = pars.t_max/pars.dt; double dt0, xn1, vn1, t_star = 5.; point xv; // Imposto i parametri iniziali; xv.x = pars.x0; xv.v = pars.v0; xv.E0 = 0.5*(xv.v)*(xv.v) - (xv.x)*(xv.x)/8. + (xv.x)*(xv.x)*(xv.x)*(xv.x)/4.; xv.E = xv.E0; xv.dE = 0; xv.t = 0; pars.t_star = 5; pars.t_basin = 60; dt0 = pars.dt; // Formato dell'output; printf ("t\tx(t)\tv(t)\tE(t)\tdE\n"); if ((pars.choice == 1) || (pars.choice == 3) || (pars.choice == 4)) { // L'utente ha deciso di affrontare il primo/terzo/quarto esercizio; // Avverto l'utente che il processo sta per iniziare. Può risultare inutile su tempi brevi, ma efficace su tempi molto lunghi; fprintf(stderr, "\nAvvio integrazione numerica... "); if (pars.alg[0] == 1) { // L'utente ha selezionato l'algoritmo di Eulero; for (i=0; i<n_passi; i++){ algEulero(&xv, &pars, &xn1, &vn1); } } else if (pars.alg[0] == 2) { // L'utente ha selezionato l'algoritmo di EuleroCromer; for (i=0; i<n_passi; i++){ algEuleroCromer(&xv, &pars, &xn1, &vn1); } } else if (pars.alg[0] == 3) { // L'utente ha selezionato l'algoritmo di PuntoDiMezzo; for (i=0; i<n_passi; i++){ algPuntoDiMezzo(&xv, &pars, &xn1, &vn1); } } else if (pars.alg[0] == 4) { // L'utente ha selezionato l'algoritmo di Verlet; for (i=0; i<n_passi; i++){ algVerlet(&xv, &pars, &xn1, &vn1); } } else if (pars.alg[0] == 5) { // L'utente ha selezionato l'algoritmo di RungeKutta di ordine 2; for (i=0; i<n_passi; i++) { algRungeKutta2(&xv, &pars, &xn1, &vn1); } } // Algoritmo terminato; fprintf(stderr, "[%s%sDONE%s]\n\n", BLD, GRN, RST); } else if (pars.choice == 2) { // L'utente ha deciso di affrontare il secondo esercizio; // Seleziono il secondo algoritmo da confrontare do { fprintf(stderr, "> Selezionare il secondo algoritmo:\n"); fprintf(stderr, "\t>> [1] Eulero\n"); fprintf(stderr, "\t>> [2] EuleroCromer - RungeKutta (1 ordine)\n"); fprintf(stderr, "\t>> [3] PuntoDiMezzo\n"); fprintf(stderr, "\t>> [4] Verlet\n"); fprintf(stderr, "\t>> [5] RungeKutta (2 ordine)\n"); fprintf(stderr, "\t>> "); scanf("%d", &pars.alg[1]); } while (( pars.alg[1] <= 0 )); // Avverto l'utente che il processo sta per iniziare. Può risultare inutile su tempi brevi, ma efficace su tempi molto lunghi; fprintf(stderr, "\nAvvio calcolo errori... "); // Eseguo lo studio degli errori d'integrazione mediante il primo e secondo algoritmo scelto, rispettivamente nei file error_1.dat, error_2.dat; getError(&xv, &pars, &i, 0, &n_passi, &xn1, &vn1); // Resetto le variabili e richiamo l'algoritmo per calcolare gli errori; pars.dt = dt0; n_passi = pars.t_max/pars.dt; xv.x = pars.x0; xv.v = pars.v0; xv.E = xv.E0; xv.dE = 0; xv.t = 0; getError(&xv, &pars, &i, 1, &n_passi, &xn1, &vn1); // Processo terminato; fprintf(stderr, "\n\nStato: [%s%sDONE%s]\n\n", BLD, GRN, RST); } else if (pars.choice == 5) { // L'utente ha deciso di affrontare il quinto esercizio; // Avverto l'utente che il processo sta per iniziare. Può risultare inutile su tempi brevi, ma efficace su tempi molto lunghi; fprintf(stderr, "\nAvvio calcolo griglia... "); #pragma omp parallel num_threads( 4 ) getBasin(xv, pars, i, n_passi, xn1, vn1); // Processo terminato; fprintf(stderr, "[%s%sDONE%s]\n\n", BLD, GRN, RST); } else { // L'utente non ha selezionato un esercizio valido; fprintf(stderr, "\n[%s%sFAILED%s] Esercizio non disponibile.\n", BLD, RED, RST); exit(EXIT_FAILURE); } return 0; } void Setup() { fprintf(stderr, "\nAnalisi numerica di un'equazione differenziale\n"); fprintf(stderr, "==============================================\n\n"); } void getParams(parametri *pars) { do { fprintf(stderr, "> Inserire un valore per gamma : "); scanf("%lf", &pars->gamma); } while (pars->gamma < 0); do { fprintf(stderr, "> Inserire un valore per epsilon : "); scanf("%lf", &pars->epsilon); } while (pars->epsilon < 0); do { fprintf(stderr, "> Inserire un valore per dt : "); scanf("%lf", &pars->dt); } while (pars->dt < 0); do { fprintf(stderr, "> Inserire un valore per tmax t.c. :\n"); fprintf(stderr, " >> (tmax > 5) per I eserc.\n"); fprintf(stderr, " >> (tmax > 60) per V eserc. : "); scanf("%lf", &pars->t_max); } while (pars->t_max < 0); do { fprintf(stderr, "> Inserire un valore per x(0) : "); scanf("%lf", &pars->x0); } while (pars->x0 < -1); do { fprintf(stderr, "> Inserire un valore per v(0) : "); scanf("%lf", &pars->v0); } while (pars->v0 < -1); do { fprintf(stderr, "> Selezionare l'esercizio richiesto :\n"); fprintf(stderr, "\t>> [1] Esercizio 1\n"); fprintf(stderr, "\t>> [2] Esercizio 2\n"); fprintf(stderr, "\t>> [3] Esercizio 3\n"); fprintf(stderr, "\t>> [4] Esercizio 4\n"); fprintf(stderr, "\t>> [5] Esercizio 5\n\n"); fprintf(stderr, "\t>> "); scanf("%d", &pars->choice); } while (( pars->choice <= 0 )); do { fprintf(stderr, "> Selezionare l'algoritmo voluto :\n"); fprintf(stderr, "\t>> [1] Eulero\n"); fprintf(stderr, "\t>> [2] EuleroCromer - RungeKutta (1 ordine)\n"); fprintf(stderr, "\t>> [3] PuntoDiMezzo\n"); fprintf(stderr, "\t>> [4] Verlet\n"); fprintf(stderr, "\t>> [5] RungeKutta (2 ordine)\n\n"); fprintf(stderr, "\t>> "); scanf("%d", &pars->alg[0]); } while (( pars->alg[0] <= 0 )); } void getError(point *xv, parametri *pars, int *i, int k, int *n_passi, double *xn1, double *vn1) { void (*algF)(point *, parametri *, double *, double *); int j, n = 0; FILE *fp; // Questo if controlla se il codice sta eseguendo lo studio degli errori per il primo o secondo algoritmo (pars->alg[k]); if (k == 0) { fp = fopen("error_1.dat", "w+"); } else { fp = fopen("error_2.dat", "w+"); } // Assegno il puntatore corretto a seconda dell'algoritmo scelto; if (pars->alg[k] == 1) { algF = algEulero; } else if (pars->alg[k] == 2) { algF = algEuleroCromer; } else if (pars->alg[k] == 3) { algF = algPuntoDiMezzo; } else if (pars->alg[k] == 4) { algF = algVerlet; } else if (pars->alg[k] == 5) { algF = algRungeKutta2; } else { fprintf(stderr, "\n[%s%sFAILED%s] E' stato selezionato un algoritmo non valido.\n", BLD, RED, RST); exit(EXIT_FAILURE); } // Informo l'utente che il processo sta iniziando; fprintf(stderr, "\n>> Avvio %d algoritmo... ", k+1); // Formattazione dell'output del file contenente gli errori; fprintf(fp, "dt\tE(t*)\tE(0)\tdE/E(0)\t# passi\ti\tt\n"); for (j=0; j<8; j++) { for ((*i)=0; (*i)<(*n_passi); (*i)++){ (*algF)(xv, pars, xn1, vn1); if (((pars->t_star - pars->dt/2. <= xv->t) && (xv->t >= pars->t_star + pars->dt/2.)) && (n == 0)) { fprintf(fp, "%+.14lf\t%+.14lf\t%+.14lf\t%+.14lf\t%+.14d\t%+.14d\t%+.14lf\n", pars->dt, xv->E, xv->E0, (xv->E - xv->E0)/xv->E0, (*n_passi), (*i), xv->t); n = 1; } } // Resetto le variabili per rilanciare l'algoritmo con dt/2^j n = 0; xv->t = 0; xv->x = pars->x0; xv->v = pars->v0; pars->dt = pars->dt/2.; (*n_passi) = pars->t_max/pars->dt; (*xn1) = 0; (*vn1) = 0; xv->E = xv->E0; } fclose(fp); fprintf(stderr, "[%s%sDONE%s]", BLD, GRN, RST); } void getBasin(point xv, parametri pars, int h, int n_passi, double xn1, double vn1) { // Dichiaro e setto i parametri che delimitano la griglia; point xv1; pars.dx = 0.01; xv1.x = -1; xv1.v = -1; // Dichiaro le variabili necessarie per il bacino d'attrazione; int i, j, i_max = 2./pars.dx, j_max = 2./pars.dx; void (*algF)(point *, parametri *, double *, double *); char fname[13]; sprintf( fname, "bassin%02d.dat", omp_get_thread_num() ); FILE *fp = fopen( fname, "w+"); // Assegno il puntatore corretto a seconda dell'algoritmo scelto; if (pars.alg[0] == 1) { algF = algEulero; } else if (pars.alg[0] == 2) { algF = algEuleroCromer; } else if (pars.alg[0] == 3) { algF = algPuntoDiMezzo; } else if (pars.alg[0] == 4) { algF = algVerlet; } else if (pars.alg[0] == 5) { algF = algRungeKutta2; } else { fprintf(stderr, "\n[%s%sFAILED%s] E' stato selezionato un algoritmo non valido.\n", BLD, RED, RST); exit(EXIT_FAILURE); } // Formattazione output file basin.dat; fprintf(fp, "x(0)\tx'(0)\tx(t*)\tv(t*)\n"); #pragma omp for // Eseguo il for della griglia sull'asse x'; for (j=0; j<=j_max; j++) { // Eseguo il for della griglia sull'asse x; for (i=0; i<=i_max; i++) { fprintf(fp, "%lf\t%lf\t", xv1.x, xv1.v); xv.x = xv1.x; xv.v = xv1.v; // Eseguo l'integrazione numerica for (h=0; h<n_passi; h++) { (*algF)(&xv, &pars, &xn1, &vn1); // Entro in t = t*, stampo v(t*) ed esco dal ciclo; if ((pars.t_basin - pars.dt/2. <= xv.t) && (xv.t >= pars.t_basin + pars.dt/2.)) { fprintf(fp, "%lf\t%lf\n", xv.x, xv.v); break; } } xv1.x += pars.dx; xv.t = 0; xn1 = 0; vn1 = 0; } // Resetto la x e incremento la x'; xv1.x = -1; xv1.v += pars.dx; } } double f(double x, double v, parametri *pars) { return 0.25*x - x*x*x + (pars->gamma - pars->epsilon*(x*x))*v; } void algEulero(point *xv, parametri *pars, double *xn1, double *vn1){ //printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE); *xn1 = xv->x + (xv->v)*(pars->dt); *vn1 = xv->v + f(xv->x, xv->v, pars)*(pars->dt); xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.; xv->dE = fabs(xv->E-xv->E0)/xv->E0; xv->t += (pars->dt); xv->x = *xn1; xv->v = *vn1; } void algEuleroCromer(point *xv, parametri *pars, double *xn1, double *vn1){ //printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE); xv->v = xv->v + f(xv->x, xv->v, pars)*(pars->dt); xv->x = xv->x + (xv->v)*(pars->dt); xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.; xv->dE = fabs(xv->E-xv->E0)/xv->E0; xv->t += (pars->dt); } void algPuntoDiMezzo(point *xv, parametri *pars, double *xn1, double *vn1) { //printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE); *vn1 = xv->v + f(xv->x, xv->v, pars)*(pars->dt); xv->x = xv->x + (0.5*(xv->v + (*vn1)))*(pars->dt); xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.; xv->dE = fabs(xv->E-xv->E0)/xv->E0; xv->t += (pars->dt); xv->v = *vn1; } void algVerlet(point *xv, parametri *pars, double *xn1, double *vn1) { //printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE); *xn1 = xv->x + xv->v*pars->dt + 0.5*(f(xv->x, xv->v, pars))*pars->dt*pars->dt; *vn1 = xv->v + 0.5*(f(xv->x, xv->v, pars) + f((*xn1), xv->v, pars))*pars->dt; xv->x = *xn1; xv->v = *vn1; xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.; xv->dE = fabs(xv->E-xv->E0)/xv->E0; xv->t += (pars->dt); } void algRungeKutta2(point *xv, parametri *pars, double *xn1, double *vn1) { //printf("%.14g\t%.14g\t%.14g\t%.14g\t%.14g\n", xv->t, xv->x, xv->v, xv->E, xv->dE); *xn1 = xv->x + xv->v*pars->dt + 0.5*f(xv->x, xv->v, pars)*pars->dt*pars->dt; *vn1 = xv->v + f(xv->x + 0.5*xv->v*pars->dt, xv->v + 0.5*f(xv->x, xv->v, pars)*pars->dt, pars)*pars->dt; xv->x = *xn1; xv->v = *vn1; xv->E = 0.5*(xv->v)*(xv->v) - (xv->x)*(xv->x)/8. + (xv->x)*(xv->x)*(xv->x)*(xv->x)/4.; xv->dE = fabs(xv->E-xv->E0)/xv->E0; xv->t += (pars->dt); } 代替"fname"
    2. 您的代码花费了大部分时间({1}}。
    3. 修好这两个,然后编译:

      fname

      我在 2.12s

      的双核笔记本电脑上运行它

      代码已更新。请自己试试。