Mandelbrots as a Web service

I have been mostly silent over the last two weeks. The reason being my wife and I have just had a kid. In between periods of changing, comforting, <insert task here>, and attempting to construct a users guide for a newly introduced mind i have been playing around with something that would be fun, visually appealing and help me learn about Scala and Scala actors.

The result is a simple Web service, utilizing Jersey and written in Scala, that calculates the Mandelbrot set over a specified area (in the complex plane) and renders that area as an image. (See the latest build, which contains this example). I am sure the code shows that i am a Scala novice! (i know for sure the Mandelbrot calculation is not very efficient).

The Mandelbrot Web services is implemented using Jersey as follows:

1 @UriTemplate("({lx},{ly}),({ux},{uy})")
2 class MandelService(
3         @UriParam("lx") lx: double,
4         @UriParam("ly") ly: double,
5         @UriParam("ux") ux: double,
6         @UriParam("uy") uy: double,
7         @DefaultValue("512") @QueryParam("imageSize") imageSize: int,
8         @DefaultValue("512") @QueryParam("limit") limit: int,
9         @DefaultValue("8") @QueryParam("workers") workers: int) {
10
11     val lower = new Complex(lx, ly);
12     val upper = new Complex(ux, uy);
13     val dx = upper.re - lower.re;
14     val dy = upper.im - lower.im;
15
16     val width : int = if (dx >= dy) imageSize else (dx/dy \* imageSize).toInt
17
18     val height : int = if (dy >= dx) imageSize else (dy/dx \* imageSize).toInt
19   
20     @ProduceMime(Array("image/png"))
21     @HttpMethod
22     def getMandelbrot() = {
23         val image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
24         new ParallelMandelRenderer(workers, lower, upper, limit,

25 image.getRaster()).render();
26         image
27     }
28 }

As you can see the actual Web service is very simple. Read here for general details.

This class will get instantiated every time the request path of the URI matches the URI template declared at line 1.

Lines 2 to 9 utilize Scala's compact syntax for class constructors. The URI parameters, in lines 3 to 6, represent the bottom left and upper right coordinates, as double types, of the area (of the complex plane) to perform calculations over. The query parameters, in lines 7 to 9, have default values declared such that if a query parameter is absent from the URI the default value will be used instead.

Lines 16 and 18 calculate the width and height of the image and ensure that the aspect ratio is the same as the area to perform calculations over.

Lines 20 and 22 declare an HTTP GET method that produces an image in the PNG format. Line 23 creates a new BufferedImage according to the calculated width and height. Lines 23 and 24 calculate the Mandelbrot set, in parallel (or more specifically concurrently), over the specified area and renders the results into the raster of the buffered image. Line 26 returns the image. The example contains an entity provider that serializes types of java.awt.image.RenderedImage, using the javax.imageio.ImageIO class according to media type declared in line 20.

The ParallelMandelRender utilizes event-based Scala actors (see here for a paper on Scala actors, the preliminary results in section 7 are very interesting). The render method is as follows:

1 override def render() : unit = {
2     val c = new JobCoordinator(n)
3     yRanges foreach ( x => c ! (new c.Job { def execute = render(x._1, x._2) }) )
4     c.waitForCompletion
5 }

Line 2 creates a new JobCoordinator where 'n' is the number of workers (actors) to instantiate (more on this later). In line 3 yRanges represents the image height split into ranges according to the number of workers. For each y-range a new job is created to render that y-range and that new job is sent as a message to the coordinating actor encapsulated by the JobCoordinator instance. The coordinating actor then sends that job to a worker that is available to receive work. Line 3 waits until all jobs have completed.

Strictly speaking it should not be necessary to specify the number of workers. Scala's scalability of event-based actors is very good and the underlying runtime should (in theory) utilize as many threads as the system configuration allows. So i could create as many actors as there are horizontal lines in the image and simplify the code (or for every pixel, who wants to port this to a DAP!, but that is a bit extreme). However, the current approach allows me to verify scalability and easily compare thread-based to event-based actors (changing 'react' to 'receive' in the actor code).

Now, all i need is to get my hands one of those T5120 servers and give it a serious workout doing some fractal calculations!

As the Mandelbrot wikipedia entry says the algorithm is embarrassingly parallel. Not only should it be easy to scale up but once the service is exposed as a network-based service it should be easy to scale out. So another way to scale the service is to have a Web client split the area into tiles and send out multiple requests. A client that was capable of browsing the Mandelbrot set a bit like browsing a map using say google maps would be really cool :-) something i would definitely like to include the example.

Comments:

Its been a few years since you posted this but its never to late to revisit an old idea.

I did a distributed mandelbrot with a raster being feed to as many workers as you have running on your network.

http://www.dbzoo.com/blog/scala_distributed_mandelbrot

Posted by Brett England on December 01, 2009 at 07:09 AM CET #

Very nice! it is never too late to play around with Mandelbrot sets :-)

Posted by Paul Sandoz on December 01, 2009 at 07:29 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

sandoz

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today