• Tidak ada hasil yang ditemukan

The values of crMin, crMax, ciMin and ciMax therefore determine which portion of the Mandelbrot set is visible in our application window

N/A
N/A
Protected

Academic year: 2023

Membagikan "The values of crMin, crMax, ciMin and ciMax therefore determine which portion of the Mandelbrot set is visible in our application window"

Copied!
5
0
0

Teks penuh

(1)

COMPSCI 230 Tutorial 10 Concurrency 2014 Assignment

Background

This assignment concerns a program that displays a visualization of a beautiful mathematical object known as the Mandelbrot set. The wikipedia page at http://en.wikipedia.org/wiki/Mandelbrot_set contains a lot of interesting and useful information regarding this set and its place in mathematics.

I’d encourage you to read this—especially if you’ve never encountered the Mandelbrot set before.

However, you need to know very little about the set to successfully complete this assignment. What you do need to know is detailed here.

A visualization of the Mandelbrot set involves a rectangular array of pixels:

These pixels are mapped onto an equivalent array of points in a two-dimensional space known as the complex plane, which is where the Mandelbrot set lives. This mapping is controlled by the coordinates in this plane at the edge of our pixel grid. For instance, the top-left pixel (0,0) maps to the complex coordinate (crMin, ciMin), while the bottom-right pixel (width-1, height-1) maps to the complex coordinate (crMax, ciMax). The values of crMin, crMax, ciMin and ciMax therefore

determine which portion of the Mandelbrot set is visible in our application window.

In order to display an image, a visualizer needs to perform the following for every point (x,y) in the pixel array:

1. Determine the corresponding complex coordinate (cr, ci).

2. Evaluate a simple but computationally intensive algorithm that returns an integer as its result.

3. Assign a colour to the pixel depending on the value of the integer returned.

The computationally intensive algorithm is parameterized by another integer: the maximum number of “iterations”. This can be regarded as the accuracy with which the computation is done, but also directly affects the worst-case time complexity of the algorithm; higher maximum iteration values will give better-looking results (particularly at higher ”magnification” levels) but will take longer to compute.

(2)

Instructions:

Download the zip file from https://github.com/tgvaughan/Mandelscape/archive/master.zip and extract it into a new folder.

Once you have the source, create a new Eclipse project and build the application. The main class is MandelscapeApp. Feel free to ask your tutors or classmates for help with this step.

Theory:

Question 1

1. [2 marks] Run Mandelscape and experiment with it. (Selecting “Controls” from the Help menu will give you some instructions on how to interact with the program.) Keeping in mind that the program must perform computationally intensive calculations in order to display the image, describe the main problem with the performance of the program.

2. [3 marks] The computationally intensive portion of the code can be found in the update() method of the MandelModel class. Looking at the code, try to explain (broadly) why the problem you identified exists.

Hint: Keep in mind that Java Swing programs execute event handlers from a single thread - the Event Dispatch Thread or EDT. When this thread executes an event handler, it cannot process additional events until the event handler returns.

Coding:

Question 2

Java Swing provides high level concurrency classes to help resolve exactly these kinds of issues.

We will use one of these, SwingWorker, to dramatically improve the responsiveness of the Mandelscape program.

To learn about SwingWorker, read through the tutorial at http://docs.oracle.

com/javase/tutorial/uiswing/concurrency/worker.html. If you don’t understand something related to this tutorial, feel free to ask your tutors or classmates for help.

1. [5 marks] Use SwingWorker to prevent the interruption of event handling by the

computation in MandelModel.update(). To accomplish this, you will need to do something similar to the following:

Replace the existing update() method body with the definition of a new anonymous class which extends SwingWorker<Void,Void> and overrides the doInBackground() and done() methods. (Note that the type Void specifies that done() must return null - our calculation has no result.)

Place the majority of the original code in doInBackground(), but put the fireModelChangedEvent() call in done().

(3)

Assign the instance of the anonymous class to a new private field of MandelModel named worker.

Add a call to worker.execute() as the last statement in MandelModel.update().

2. [5 marks] Allow SwingWorker to be cancelled by premature calls to update(). Since update() no longer holds up the event dispatch thread, it is now possible for update() to be called before the SwingWorker created in a previous call is complete. This may lead to thread interference between the older and newer workers. It is also wasteful, as the older workers are spending time on calculations that will be overwritten immediately by the newer workers. To cope with this, you will need to do something similar to the following:

At the start of update(), check that worker is non-null and, if so, call the method worker.cancel(true).

In the body of the loop in MandelModel.getEscapeIters(), check for a true value of Thread.interrupted() and throw an InterruptedException if this succeeds. (Cancelling the SwingWorker using worker.cancel(true) will result in the thread being interrupted.)

You should find that the program is now much more responsive.

3. [5 marks] Use publish() to display interim results. SwingWorker allows the workers to intermittently prompt the EDT to process partially completed results. In our case, we can use this to prompt MandelPanel to redraw the partially-complete image at regular intervals. To do this, simply have doInBackground() periodically (after each column of pixels is processed, for instance) call the SwingWorker method publish(). Then, override process() and place within its definition a call to fireModelChangedEvent().

(4)

Bonus Parts (Optional):

Question 3 [12 marks]

Following the modifications you made as part of Q2, you should find that the program is a much more usable (and fun!) Mandelbrot set explorer. However, there are still some fairly simple things we can do to improve its performance.

1. [5 marks] Modify drawing code to update the iters array in increasingly small blocks. This sounds complicated, but can in fact be done quite simply:

Wrap the outer for loop (over x) in doInBackground() in a new loop over a variable called bsize. This loop should initialize bsize to some power of 2 (64 is a good choice) and successive iterations should divide the value of bsize by 2 until bsize is 1.

Modify the loops over x and y so that they step in increments of bsize rather than 1.

Instead of directly setting elements of the iters array to the result of getEscapeIters(), record the value of this call in a local variable named escapeIters. (Try to avoid making the call to getEscapeIters() for a grid point for which this call has already been evaluated using a larger block size!)

Instead of setting a single pixel in iters to this value, use a pair of nested loops to set all of the pixels in the block (x,y) to (x+bsize, y+bsize) to the value of escapeIters. Make sure these loops respect the boundaries of the pixel grid!

Move the call to publish() to the end of the body of the bsize loop.

This change should make a huge difference to the responsiveness of the program!

2. [5 marks] Use a multiple threads in parallel to efficiency on multicore machines. Until now, we have been using a single worker thread to perform the computationally intensive task in the visualizer. However, this task is embarrassingly parallel which means that it is composed of many independent tasks that can be easily run concurrently. Make use of this fact by doing the following:

Replace the single worker field of MandelModel with a workers field of type ArrayList<SwingWorker>. Initialise this field at the point where it is defined in the class.

Replace the worker.cancel(true) call at the start of MandelModel.update() with a loop that cancels every worker in the workers array and then clears the ArrayList.

Modify the worker instantiation code so that the new instance is added to workers before it is executed.

Wrap the entire worker instantiation code fragment (including the code to execute the worker and add it to workers) inside of a new loop over a variable widx. This variable should run from 0 to WMAX where WMAX is the number of concurrent threads you wish to use. (You might wish to add a component to bottomPanel in MandelscapeApp to allow users to control this parameter, but this isn’t necessary.)

Within this loop, define new variables xmin=widx*width/WMAX and xmax=(widx+1)*width/WMAX.

Use these in place of 0 and width as the bounds for x in its loop. (You’ll also need to adjust the bound on the xp loop.)

Note that here we are making use of threads belonging to the default SwingWorker thread pool to execute our tasks. The maximum size of this pool is fixed by Swing to 10. This shouldn’t affect us as

(5)

we don’t need any more threads than there are cores to achieve maximum use of our hardware.

However, be aware that we could easily maintain many more threads in parallel if we needed to by submitting our workers to an

ExecutorService such as those created using Executors.newFixedThreadPool().

3. [2 marks] Increase the number of iterations to the maximum possible value (10000) and describe how different numbers of concurrent threads affect the rendering speed of the starting image.

(Pressing the “Reset Zoom” button will cause the picture to be regenerated, making it easy to get a feel for this speed.) How does this performance relate to the number of cores available on your computer?

Question 4 [8 marks]

1. [5 marks]Use Timer to animate colours. The class javax.swing.Timer (see here for a tutorial) provides a simple way to execute simple tasks periodically. Generally, these tasks must be quick to run. Here we use Timer to animate the colour scheme of our Mandelbrot visualization.

Add a private field to MandelscapeApp named colourTimer of type javax.swing.Timer.

Add “implements ActionListener” to the MandelscapeApp class definition.

Provide an implementation of the method actionPerformed. This method should simply retrieve the current colour model from mandelPanel.getColourModel() and increment the its offset value by a fixed amount, say 10, modulo the value of mandelPanel.getColourModel().getPeriod().

Add a JCheckBox to the bottom panel of the application (see the tutorial to refresh your memory on how to do use these components). Add a ChangeListener (similar to the one for iterSpinner) which fires when the checkbox value is changed and calls colourTimer.start() when the checkbox is selected and colourTimer.stop() when the checkbox is deselected.

2. [3 marks] Possible sources of residual thread interference. Whenever multiple threads of execution work on a single shared variable, there is the possibility for interference. We have been relatively lucky in this assignment in that our concurrent SwingWorkers, while they have worked together on the array MandelModel.iters, they have worked on distinct elements of this array. This can be done safely without locking.

Despite this, there do remain some possibilities for interference. Try to think of some possible problems and describe how they might come about. Hint: remember that SwingWorkers - and threads in general - must handle their own cancellation/interruption. They don’t necessarily terminate immediately!

Referensi

Dokumen terkait

We do not have a variable which will be used to push the time derivative of descent function (25) less than zero.. The similar method using artificial input can be seen in [8]..

The first intervention lesson focuses on introducing the new strategy to the pupils, and the objective of the lessons is that pupils will be able to accomplish the