Crowd computations typically involve three phases. The first is the initialization of the process group; in the case of node-only computations, dissemination of group information and problem parameters, as well as workload allocation, is typically done within this phase. The second phase is computation. The third phase is collection results and display of output; during this phase, the process group is disbanded or terminated.
The master-slave model is illustrated below, using the well-known
Mandelbrot
set computation which is representative of the class of
problems termed ``embarrassingly''
parallel . The computation
itself involves applying a recursive function to a collection of
points in the complex plane until the function values either
reach a specific value or begin to diverge. Depending upon
this condition, a graphical representation of each point in the plane
is constructed. Essentially, since the function outcome depends
only on the starting value of the point (and is independent of
other points), the problem
can be partitioned into
completely independent portions, the algorithm applied to each, and
partial results combined using simple combination schemes. However,
this model permits dynamic load balancing,
thereby allowing the processing elements to
share the workload unevenly. In this and subsequent examples within
this chapter, we only show a skeletal form of the algorithms, and
also take syntactic liberties with the PVM routines in the interest
of clarity. The control structure of the master-slave class of
applications is shown in Figure .
{Master Mandelbrot algorithm.} {Initial placement} for i := 0 to NumWorkers - 1 pvm_spawn(<worker name>) {Start up worker i} pvm_send(<worker tid>,999) {Send task to worker i} endfor {Receive-send} while (WorkToDo) pvm_recv(888) {Receive result} pvm_send(<available worker tid>,999) {Send next task to available worker} display result endwhile {Gather remaining results.} for i := 0 to NumWorkers - 1 pvm_recv(888) {Receive result} pvm_kill(<worker tid i>) {Terminate worker i} display result endfor {Worker Mandelbrot algorithm.} while (true) pvm_recv(999) {Receive task} result := MandelbrotCalculations(task) {Compute result} pvm_send(<master tid>,888) {Send result to master} endwhile
The master-slave example described above involves no communication
among the slaves. Most crowd computations of any complexity do need
to communicate among the computational processes; we illustrate the
structure of such applications using a node-only example for
matrix multiply using Cannon's algorithm [2]
(programming details
for a similar algorithm are given in another chapter).
The matrix-multiply example, shown
pictorially in Figure multiplies matrix subblocks locally, and
uses row-wise multicast of matrix A subblocks in conjunction
with column-wise shifts of matrix B subblocks.
Figure: General crowd computation
{Matrix Multiplication Using Pipe-Multiply-Roll Algorithm} {Processor 0 starts up other processes} if (<my processor number> = 0) then for i := 1 to MeshDimension*MeshDimension pvm_spawn(<component name>, . .) endfor endif forall processors Pij, 0 <= i,j < MeshDimension for k := 0 to MeshDimension-1 {Pipe.} if myrow = (mycolumn+k) mod MeshDimension {Send A to all Pxy, x = myrow, y <> mycolumn} pvm_mcast((Pxy, x = myrow, y <> mycolumn),999) else pvm_recv(999) {Receive A} endif {Multiply. Running totals maintained in C.} Multiply(A,B,C) {Roll.} {Send B to Pxy, x = myrow-1, y = mycolumn} pvm_send((Pxy, x = myrow-1, y = mycolumn),888) pvm_recv(888) {Receive B} endfor endfor