/* See: http://en.wikipedia.org/wiki/Power_series The Taylor series for the exponential function e^x: e^z = lim n|->inf \Sum_1^inf z^n / n! This is NOT a proper parallel solution to the above problem. It only sketches the communication for the specific input below, ie. input n=2 on 3 processors. Compile: mpicc -DDEBUG -DSLOW -lm -o power_par power_par.c Run: mpirun -n 3 power_par 2 */ #include #include #include #include #define EPSILON 1e-6 /* prototypes */ double power_e(long z, long from, long to); // compute e^z long pow_int(long z, long n); // compute z^n long fact(long n); // compute n! #if defined(SLOW) # warning "SLOW operations enabled" /* a very slow algorithm, to make it more compute-intensive */ long add_slow(long m, long n) { long sum, i; for (sum=m, i=1; i<=n; sum+=1, i++) { } return sum; } long mult_slow(long m, long n) { long sum, i; for (sum=0, i=1; i<=n; sum=add_slow(sum,m), i++) { } return sum; } /* a really silly implementation of square, to make it more compute-intensive */ long sqr(long n) { return mult_slow(n,n); } /* factorial: n! */ long fact(long n) { long i, prod; for (i=1, prod=1; i<=n; prod=mult_slow(prod,i), i++) { }; return prod; } /* power over integers: z^n */ long pow_int(long z, long n) { long i, prod; for (i=1, prod=1; i<=n; prod=mult_slow(prod,z), i++) { }; return prod; } #else /* the proper algorithm */ /* square computation */ long sqr(long n) { return n*n; } /* factorial: n! */ long fact(long n) { long i, prod; for (i=1, prod=1; i<=n; prod=prod*i, i++) { }; return prod; } /* power over integers: z^n */ long pow_int(long z, long n) { if (n==0) return 1; if (n==1) return z; if (n%2==0) return sqr(pow_int(z,n/2)); else return z*sqr(pow_int(z,n/2)); } #endif /* exponential function */ double power_e(long z, long from, long to) { long n; double sum, old_sum, eps; for (n=from, sum=0.0, eps=1.0; n<=to ; n++) { #if defined(DEBUG) fprintf(stderr, "IT %d\t", n); #endif old_sum = sum; sum += ((double)pow_int(z,n)) / ((double) fact(n)); eps = sum - old_sum; #if defined(DEBUG) fprintf(stderr, "SUM %1.9f OLD_SUM %1.9f EPS %1.9f\n", sum, old_sum, eps); #endif } return sum; } int main (int argc, char **argv) { int p, id; long n; double z, zz; double elapsed_time; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &p); MPI_Comm_rank(MPI_COMM_WORLD, &id); if (id == 0) { /* master processor */ if (argc<2) { fprintf(stderr, "Usage: power ... computes e^n, using a Taylor series"); exit(1); } n = atol(argv[1]); fprintf(stderr, "Computing e^%d ...\n", n); // start the timer MPI_Barrier(MPI_COMM_WORLD); elapsed_time = - MPI_Wtime(); /* SPECIAL CASE: INPUT n=2, and 2 workers */ /* Q: how shall we split up the interval in general? */ long from1=0, to1=7, from2=8, to2=14; double res, res1, res2, res_check; MPI_Status status; MPI_Send(&n, 1, MPI_LONG, 1, 0, MPI_COMM_WORLD); MPI_Send(&from1, 1, MPI_LONG, 1, 0, MPI_COMM_WORLD); MPI_Send(&to1, 1, MPI_LONG, 1, 0, MPI_COMM_WORLD); MPI_Send(&n, 1, MPI_LONG, 2, 0, MPI_COMM_WORLD); MPI_Send(&from2, 1, MPI_LONG, 2, 0, MPI_COMM_WORLD); MPI_Send(&to2, 1, MPI_LONG, 2, 0, MPI_COMM_WORLD); MPI_Recv(&res1, 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &status); MPI_Recv(&res2, 1, MPI_DOUBLE, 2, 0, MPI_COMM_WORLD, &status); fprintf(stderr, "FROM PE1 res1=%1.9f\n", res1); fprintf(stderr, "FROM PE2 res2=%1.9f\n", res2); /* Q: how can we determine in general that this is close enough to the solution? */ res = res1+res2; /* Q: what if this is not close enough? */ // stop the timer elapsed_time += MPI_Wtime(); res_check = exp((double)n); printf("e^%d = %1.9f (expected %1.9f)\nElapsed time: %f secs\n", n, res, res_check, elapsed_time); } else { // worker MPI_Status status; long from, to; double res; // start the timer MPI_Barrier(MPI_COMM_WORLD); // elapsed_time = - MPI_Wtime(); MPI_Recv(&n, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD, &status); MPI_Recv(&from, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD, &status); MPI_Recv(&to, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD, &status); fprintf(stderr, "[%d] from=%d, to=%d\t", id, from, to); res = power_e(n, from, to); fprintf(stderr, "res=%1.9f\n", res); MPI_Send(&res, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); } MPI_Finalize(); return 0; }