Skip to content

BigJob Tutorial Part 4: Mandelbrot Example

melrom edited this page Dec 6, 2012 · 7 revisions

This page is part of the BigJob Tutorial.


Overview / Background

In this example, we split up the calculation of a Mandelbrot set into several tiles.

Rather than submit a single job for each tile, we want to submit one BigJob and then execute each individual tile job in a distributed fashion. This is why BigJob is useful. We reserve the resources we need for all of the jobs, but submit just one job that requests all of these resources. Once the job becomes active, the compute units are executed in a distributed fashion. The tiles are then retrieved using features included in BigJob such as the SAGA File API, and the final image is stitched together from the individual tiles.

Mandelbrot Tiles

Hands-On: Distributed Mandelbrot Fractals

In order for this example to work, we need to install an additional Python module, the Python Image Library (PIL). This is done via pip:

pip install PIL

Next, we need to download the Mandelbrot fractal generator itself. It is really just a very simple python script that, if invoked on the command line, outputs a full or part of a Mandelbrot fractal as a PNG image. Download the script into your $HOME directory:

curl --insecure -Os https://raw.github.com/saga-project/bliss/master/examples/advanced/mandelbrot/mandelbrot.py

You can give mandelbrot.py a test-drive locally by calculating a single-tiled 1024x1024 Mandelbrot fractal:

python mandelbrot.py 1024 1024 0 1024 0 1024 frac.gif

Cut and paste code below into bj_mandelbrot.py.

import os, time, sys
from PIL import Image
import bliss.saga as saga 
from pilot import PilotComputeService, ComputeDataService, State

# the dimension (in pixel) of the whole fractal
imgx = 8192 
imgy = 8192

# the number of tiles in X and Y direction
tilesx = 2
tilesy = 2

	
### This is the number of jobs you want to run
NUMBER_JOBS=4
COORDINATION_URL = "redis://localhost"

if __name__ == "__main__":

    pilot_compute_service = PilotComputeService(COORDINATION_URL)
    
    # copy image tiles back to our 'local' directory
    dirname = 'sftp://localhost/%s/PJ-mbrot/' % '/tmp'
    workdir = saga.filesystem.Directory(dirname, saga.filesystem.Create)

    pilot_compute_description={ "service_url": "fork://localhost",
                                "number_of_processes": 12,
                                #"allocation": "XSEDE12-SAGA",
                                #"queue": "development",
                                "working_directory": workdir.get_url().path,
                                "walltime":10
                              }

    pilot_compute_service.create_pilot(pilot_compute_description=pilot_compute_description)

    compute_data_service = ComputeDataService()
    compute_data_service.add_pilot_compute_service(pilot_compute_service)

    print ("Finished Pilot-Job setup. Submitting compute units")

    # submit compute units
    for x in range(0, tilesx):
        for y in range(0, tilesy):                
            # describe a single Mandelbrot job. we're using the 
            # directory created above as the job's working directory
            outputfile = 'tile_x%s_y%s.gif' % (x,y)
                
            compute_unit_description = {
                        "executable": "python",
                        "arguments": [os.getenv("HOME")+'/mandelbrot.py', str(imgx), str(imgy), 
                                        str(imgx/tilesx*x), str(imgx/tilesx*(x+1)),
                                        str(imgy/tilesy*y), str(imgy/tilesy*(y+1)),
                                        outputfile],
                        "number_of_processes": 1,    
                        "working_directory":workdir.get_url().path,        
                        "output": "stdout_x%s_y%s.txt" % (x,y),
                        "error": "stderr_x%s_y%s.txt" % (x,y),
                        }    
            compute_data_service.submit_compute_unit(compute_unit_description)

    print ("Waiting for compute units to complete")
    compute_data_service.wait()
                
    # Preparing the final image
    for image in workdir.list('*.gif'):
        print ' * Copying %s/%s back to %s' % (workdir.get_url(), image, os.getcwd())
        workdir.copy(image, 'sftp://localhost/%s/' % os.getcwd()) 

    # stitch together the final image
    fullimage = Image.new('RGB',(imgx, imgy),(255,255,255))
    print ' * Stitching together the whole fractal: mandelbrot_full.png'
    for x in range(0, tilesx):
        for y in range(0, tilesy):
            partimage = Image.open('tile_x%s_y%s.gif' % (x, y))
            fullimage.paste(partimage, (imgx/tilesx*x, imgy/tilesy*y, imgx/tilesx*(x+1), imgy/tilesy*(y+1)) )
    fullimage.save("mandelbrot_full.gif", "GIF")

    print ("Terminate Pilot Jobs")
    compute_data_service.cancel()    
    pilot_compute_service.cancel()

Execute the bj_mandelbrot.py script using command

python bj_mandelbrot.py

Compare the execution of bj_mandelbrot.py to its SAGA counterpart saga_mandelbrot.py. Notice that a pilot-job is used in the BigJob case to submit many jobs at one time, instead of submitting them serially as in the SAGA example.


Back: [Tutorial Home](BigJob Tutorial)    Next: BigJob Tutorial Part 5: Chained Ensemble Example