# Lecture 15: More Mandelbrot and Thread Pools

## Outline

Draw this picture as quickly as possible!

## Defining the Mandelbrot Set

To determine if $c$ is in the Mandelbrot set $M$:

• compute $z_1 = c$
• define $z_n = z_{n-1}^2 + c$ for $n > 1$

If $z_n$ remains bounded, $c$ is in $M$; otherwise $c$ is not in $M$.

## Depicting the Mandelbrot Set

Make a grid of pixels!

## Computing the Mandelbrot Set

Choose parameters:

• $N$ number of iterations
• $M$ maximum modulus ($M > 2$)

Given a complex number $c$:

• compute $z_1 = c, z_2 = z_1^2 + c,\ldots$ until
1. $|z_n| \geq M$
• stop because sequence appears unbounded
2. $N$th iteration
• stop because sequence appears bounded
• if $N$th iteration reached $c$ is likely in Mandelbrot set

## Illustration

https://complex-analysis.com/content/mandelbrot_set.html

## Drawing the Mandelbrot Set

• Choose a region consisting of $a + b i$ with
• $x_{min} \leq a \leq x_{max}$
• $y_{min} \leq b \leq y_{max}$
• Make a grid in the region
• For each point in grid, determine if in Mandelbrot set
• Color accordingly

## Counting Iterations

Given a complex number $c$:

• compute $z_1 = c, z_2 = z_1^2 + c,\ldots$ until
1. $|z_n| \geq M$
• stop because sequence appears unbounded
2. $N$th iteration
• stop because sequence appears bounded
• if $N$th iteration reached $c$ is likely in Mandelbrot set

## Color by Escape Time

1. Color black in case 2 (point is in Mandelbrot set)
2. Change color based on $n$ in case 1:
• smaller $n$ are “farther” from Mandelbrot set
• larger $n$ are “closer”

## Lab 03

Input:

• A square region of complex plane

Output:

• Escape times for a grid of points in the region
• A picture of corresponding region

Goal:

• Compute escape times as quickly as possible

## Mandelbrot Viewer Demo

• mandelbrot.zip

## Getting a Single Escape Time

    public static float getValue (ComplexNumber c) {
ComplexNumber z = new ComplexNumber(0, 0);
int iter = 0;
while (iter < MAX_ITER && z.modulus() <= MAX_MODULUS) {
z = z.times(z).plus(c);
iter++;
}
return (float) (MAX_ITER - iter) / MAX_ITER;
}


## Getting Many Values

    private void updateBitmap () {
for (int i = 0; i < BOX_WIDTH; i++) {
for (int j = 0; j < BOX_HEIGHT; j++) {
ComplexNumber c = getValueFromIndices(i, j);
float val = Mandelbrot.getValue(c);
bitmap[i][j] = colorMap(val);
}
}
}


## So Far

• Created Threads and ran them in parallel

• implmenet Runnable interface
• create and start instances
• join to wait until threads finish

## Drawbacks

• Creating new Threads has significant overhead
• best performance by balancing number of threads/processors available
• Need to explicitly partition into relatively few pieces
• partitioning may be unnatural
• partition may be unbalanced:
• don’t know in advance how long computations will take

When tasks are fairly homogenous (e.g., computing $\pi$, shortcuts) previous approach is good

## A (Sometimes) Better Way

A nice Java feature: thread pools

• Create a (relatively small) pool of threads
• Assign tasks to the pool

## When are Thread Pools Better?

• Fixed partition of problem may be unbalanced
• e.g., processing requests for web server

• Implement Executor interface
• void execute(Runnable command) method
• More control of task handling: ExecutorService interface:
• wait for tasks to complete
• shut down pool (don’t accept new tasks)

## Built-in ExecutorService Implementations

From java.util.concurrent.Executors:

• newFixedThreadPool(int nThreads)
• make a pool with a fixed number of threads
• newSingleThreadExecutor()
• make a pool with a single thread
• newCachedThreadPool()
• make pool that creates new threads as needed (reuses old if available)

public class MyTask implements Runnable {
...
public void run () {
...
}
}


Create a pool, e.g., fixed thread pool

int nThreads = ...;



MyTask task = new MyTask(...);



Shutting down the pool

pool.shutdown();


Wait for all pending processes to complete (like join() method)

try {

pool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);

} catch (InterruptedException e) {

// do nothing

}


## Example

Shortcuts from Lab 02:

for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
float min = Float.MAX_VALUE;
for (int k = 0; k < size; ++k) {
float x = matrix[i][k]; float y = matrix[k][j];
float z = x + y;
if (z < min)
min = z;
}
shortcuts[i][j] = min;
}
}


For fixed row i, col j:

        float min = Float.MAX_VALUE;
for (int k = 0; k < size; ++k) {
float x = matrix[i][k]; float y = matrix[k][j];
float z = x + y;
if (z < min)
min = z;
}
shortcuts[i][j] = min;


## Two Approaches

Approach 1:

• need size * size threads
• choose pool size from availableProcessors()
• executer-shortcuts.zip