tar
your files to a file called hw2.tar
and email it as an attachment to mao@cs.fsu.edu, along with a cc to asriniva@cs.fsu.edu. Make sure that you do not include the executable, any object files, or core dump file!
tar xvf hw2.tar
make
hw2 P
(where P is the number of processes, as explained below)
[At some point in time, we will send a SIGINT (^C). We may send this mulitple times.]
hw2
.
Monte Carlo computations are particularly efficient to run in such a parallel environment, since the communication requirements are low. In these computations, typically, each process carries out identical work, but uses a different random number sequence to get a different result. The results from all the processes are then finally combined, to yield the answer.
P
, which is the number of processes that will work together to estimate pi.
We have provided a file, pi.c,
which defines function double EstimatePi(long
NumberOfSimulations, int ProcessRank)
. It uses the
ProcessRank
to create a unique random number sequence,
and returns an estimate of pi using NumberOfSimulations
simulations. The interface to pi.c is provided by the header file pi.h
.
The larger the number of simulations, the more accurate the answer is. Instead of one process performing a large number of simulations, we can have multiple processes performing a smaller number of simulations "simultaneously". Of course, since we are not using a parallel machine, you will not actually observe a significant improvement in performance.
Your program will determine P
from the command line
argument, and will fork P-1
other processes (either directly or indirectly). This set of
P
processes will repeatedly estimate pi, using
increasingly larger numbers of simulations, as described below, until the user decides to
terminate the computation by sending a SIGINT to the parent process.
Each process should be assigned a unique processes rank in [0,P-1]. (You
need to figure out a way to do this.) Each process will then set the
number of simulations at 1000000 (one million), and then call
EstimatePi(NumberOfSimulations, ProcessRank)
to get its estimate of
pi.
Pi is then estimated (over the entire set of processes) by averaging the estimates from each process. This is performed in two steps. First the value of pi from each process is added (which we call a reduction operaton), as described below.
The reduction operation requires communication between processes, and you should use pipes to perform this. A simple way of performing the reduction is to have each process send its value to the parent, and then have the parent add up the values. However, this will take O(P) time even on a parallel machine, and so is considered inefficient. A more efficient algorithm uses a tree structured computation, and takes O(log P) time. (Since we are not on a parallel machine, you will not notice an improvement in speed; however, you should use the latter algorithm. If you find it difficult, then you may use the simpler algorithm, but will lose a few points for that.) You can read about the efficient algorithm from page 38-39 (section 2.3.2) of Designing and Building Parallel Programs, by Ian Foster, available online at: http://www-unix.mcs.anl.gov/dbpp. Please make sure that you handle the case where the number of processes is not a power of two.
Next, the sum computed above is divided by the number of processes, to get the estimate. This estimate is output to stdout.
All the processes then double the value of the number of
simulations (to 2000000) and repeat the above process. They keep
repeating this process, doubling the number of simulations and then
outputing the new estimate. (If doubling the number of simulations will cause the value to exceed LONG_MAX
, then they will not double the number of simulations, but just repeat, with the same number of simulations.)
When the user is satisfied that pi is sufficiently accurate, the user will send a SIGINT to the parent. The parent will not terminate immediately. Instead, the next estimate of pi is computed and output, and then the parent causes all the children to terminate, and also terminates itself. Note that you may need to block SIGINT in a major portion of your computation, in order to accomplish this.
(ii) Please do not compile pi.c with -ansi -pedantic flags.