| file: mandelbrot_mw.py |
| 1 | #################################################### |
| 2 | # Calculate Mandelbrot set and save it as a bmp image |
| 3 | # |
| 4 | # Parallel version uses master-worker approach, |
| 5 | # with master node at rank zero; the work units |
| 6 | # distributed to workers consist of a single |
| 7 | # image row. It needs at least two MPI nodes to work. |
| 8 | # |
| 9 | #################################################### |
| 10 | |
| 11 | import mpi |
| 12 | import bmp |
| 13 | |
| 14 | # maximal number of iterations to compute a pixel |
| 15 | MAX_ITER = 256 |
| 16 | |
| 17 | # pixel computation function |
| 18 | def pixel(c): |
| 19 | z = 0 |
| 20 | for i in range(MAX_ITER): |
| 21 | z = z*z+c |
| 22 | if abs(z) >= 2.0: |
| 23 | return i |
| 24 | return MAX_ITER |
| 25 | |
| 26 | # image dimensions |
| 27 | nx = 1024 |
| 28 | ny = 1024 |
| 29 | |
| 30 | workers = [] |
| 31 | for i in range(mpi.size): |
| 32 | workers.append(0) |
| 33 | |
| 34 | if mpi.rank == 0: |
| 35 | # "master" node: |
| 36 | |
| 37 | # number of jobs being processed and the next image row to compute |
| 38 | jobs = 0 |
| 39 | row = 0 |
| 40 | |
| 41 | # initialize list of image rows |
| 42 | image = [] |
| 43 | for i in range(ny): |
| 44 | image.append(-1) |
| 45 | |
| 46 | # assign initial work units to all workers |
| 47 | for n in range(1, mpi.size): |
| 48 | mpi.send(row, n) |
| 49 | row += 1 |
| 50 | jobs += 1 |
| 51 | |
| 52 | # master's main loop: |
| 53 | while jobs > 0: |
| 54 | # receive computed result from any worker |
| 55 | result, status = mpi.recv() |
| 56 | |
| 57 | workers[status.source] += 1 |
| 58 | |
| 59 | # incorporate newly computed row into image data |
| 60 | image[result[0]] = result[1] |
| 61 | |
| 62 | if row < ny: |
| 63 | # send new work unit to the (now) idle worker |
| 64 | mpi.send(row, status.source) |
| 65 | row += 1 |
| 66 | else: |
| 67 | # no jobs remain: dismiss the worker |
| 68 | mpi.send(-1, status.source) |
| 69 | jobs -= 1 |
| 70 | |
| 71 | # convert data to color image and save it in a file |
| 72 | bmp.write_image('image.bmp', nx, ny, image, MAX_ITER) |
| 73 | for w in range(1,mpi.size): |
| 74 | print workers[w],"tasks completed by worker",w |
| 75 | |
| 76 | else: |
| 77 | # "worker" node: |
| 78 | while 1: |
| 79 | # receive work unit info |
| 80 | row, status = mpi.recv() |
| 81 | # check if we're still needed |
| 82 | if row == -1: |
| 83 | break |
| 84 | # compute row of image |
| 85 | rdata = [] |
| 86 | |
| 87 | # Magic here... 4.0j is a complex number |
| 88 | c = 4.0j*row/ny-2.0j |
| 89 | for x in range(nx): |
| 90 | rdata += [pixel(c+4.0*x/nx-2.0)] |
| 91 | # send the result to master |
| 92 | mpi.send([row, rdata], 0) |
| |