Threads and Executors
Contents
- 1 Motivation
- 2 Background
- 3 Where To Start
- 4 Join All Warm Up
- 5 Threads
- 6 Executors
- 6.1 Javadocs
- 6.2 Videos
- 6.3 Mistake To Avoid: Do NOT call shutdown
- 6.4 XNucleobaseCount
- 6.4.1 note countSequential
- 6.4.2 implement int countRangeSequential(byte[] chromosome, Nucleobase nucleobase, int min, int max)
- 6.4.3 implement int count2WaySplit(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase)
- 6.4.4 implement int countNWaySplit(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase, int numTasks)
- 6.4.5 implement int countDivideAndConquer(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase, int threshold)
- 6.4.6 implement int countDivideAndConquerKernel(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase, int min, int max, int threshold)
- 6.5 XQuicksort
- 6.5.1 Videos
- 6.5.2 note sequentialQuicksort
- 6.5.3 implement void sequentialQuicksortKernel(int[] array, int min, int maxExclusive)
- 6.5.4 implement void parallelQuicksort(ExecutorService executor, int[] array, int threshold)
- 6.5.5 implement void parallelQuicksortKernel(ExecutorService executor, int[] array, int min, int maxExclusive, Queue<Future<?>> futures, int threshold)
- 6.5.6 Beyond 231
- 7 Rubric
- 8 Pledge, Acknowledgments, Citations
Motivation
Background
Where To Start
Habanero Java Library (HJlib) is the product of the Habanero Extreme Scale Software Research Laboratory. It builds on the X10 programming language.
HJlib provides some parallel features via static methods, most notably async and finish which handle a lot of the details of starting and joining tasks. We in CSE231 have thinly wrapped these methods for stylistic reasons as well as to afford more easily testing student code.
In this assignment we will remove the training wheels for a moment to get some experience with some core Java parallel features Threads and Executors.
Tutorial
Java 8 Concurrency Tutorial: Threads and Executors
JUnit ThreadsAndExcutorsTestSuite
Mac: Command+Shift+T ThreadsAndExcutorsTestSuite
Windows: Ctrl+Shift+T ThreadsAndExcutorsTestSuite
Join All Warm Up
OPTIONAL
source: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html
"Iterators are weakly consistent, returning elements reflecting the state of the queue at some point at or since the creation of the iterator."
JoinAllTestSuite
ThreadsEventually joinAllInQueueViaPoll
Completing this optional warm up will help you when you implement #XQuicksort.
Threads
Javadocs
class SimpleThreadFactory
Video
implement Thread newThread(Runnable target)
Create and return a new thread with the target Runnable parameter you are passed.
Do *NOT* start this thread.
Certainly, do *NOT* run this thread.
Do not pass Go. Do not collect $200.
To repeat: just create a new Thread with the target Runnable and return it.
TAgeSum
implement int sumUpperLowerSplit(int[] ages, ThreadFactory threadFactory)
You will need use the passed in ThreadFactory to create a new thread or two (at your preference), start any threads you create, and join them.
Think about where you need to start and join any Threads to ensure both correctness and an appropriate amount of parallelism.
Executors
Javadocs
Videos
Executor submit and Future get
Mistake To Avoid: Do NOT call shutdown
Each method you write will be passed an executor. You need not (and should NOT) invoke shutdown(). That is the responsibility of whoever created the executor. For example, a JUnit test would do this if it was appropriate (i.e. it created an executor and was not using the common pool).
XNucleobaseCount
This part of the assignment should be very familiar as it is to a large degree implementing the Nucleobase Counting assignment with Executors instead of Habanero. It also adds a divide and conquer implementation of nucleobase counting.
note countSequential
public static int countSequential(byte[] chromosome, Nucleobase nucleobase) { return countRangeSequential(chromosome, nucleobase, 0, chromosome.length); }
implement int countRangeSequential(byte[] chromosome, Nucleobase nucleobase, int min, int max)
implement int count2WaySplit(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase)
implement int countNWaySplit(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase, int numTasks)
Please feel free to use the provided SliceUtils createNSlices(byte[] data, int numSlices). Then use the Java for each loop to iterate over the slices to create your tasks.
implement int countDivideAndConquer(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase, int threshold)
This method should just get things going by invoking the countDivideAndConquerKernel on the entire chromosome array.
implement int countDivideAndConquerKernel(ExecutorService executor, byte[] chromosome, Nucleobase nucleobase, int min, int max, int threshold)
Note: when you get down below the threshold and convert from parallel to sequential execution, do NOT feel compelled to build a sequential divide and conquer. Just invoke countRangeSequential on the remaining range. It is not like divide and conquer gives you a performance benefit in counting like it would for sorting.
XQuicksort
Quicksort is an oldie but a goodie. First developed in 1959 and published in 1961 it is still the go to sorting algorithm today. The JDK8 implementation of Arrays.sort(array) is a DualPivotQuicksort.
Quicksort is also amenable to parallelism. Once the partitioning is done, both sides of the pivot can be sorted completely independently in parallel. This lends itself very nicely to Habanero as you can freely async as you divide and conquer, then join all of the tasks by wrapping it all in a single call to finish.
In this assignment you will mimic this behavior by submitting tasks to an executor, tracking the returned futures in in a ConcurrentLinkedQueue, then invoking get on all of those futures to mimic the single finish.
NOTE: ConcurrentLinkQueue's iterators are weakly consistent. Do the #Join All Warm Up to gain experience with handling this issue.
Videos
note sequentialQuicksort
Unlike in Prof. Sarkar's videos which use inclusive maximums, in CSE 231 we use exclusive maximums to avoid having to subtract 1 all of the time. The provided sequentialQuicksort method hopes to get you on the right track.
public static void sequentialQuicksort(int[] array) { sequentialQuicksortKernel(array, 0, array.length); }
implement void sequentialQuicksortKernel(int[] array, int min, int maxExclusive)
implement void parallelQuicksort(ExecutorService executor, int[] array, int threshold)
Make sure to use a thread safe data structure like ConcurrentLinkedQueue and NOT an unsafe data structure like LinkedList.
implement void parallelQuicksortKernel(ExecutorService executor, int[] array, int min, int maxExclusive, Queue<Future<?>> futures, int threshold)
Beyond 231
While perhaps a bit more complicated, and beyond the scope of this class, the partitioning step can also be done in parallel with scan.
Rubric
As always, please make sure to cite your work appropriately.
Total points: 100
SimpleThreadFactory subtotal: 5
- Correct newThread (5)
TAgeSum subtotal: 10
- Correct sumUpperLowerSplit (10)
XNucleobaseCount subtotal: 45
- Correct countRangeSequential (5)
- Correct count2WaySplit (10)
- Correct countNWaySplit (15)
- Correct countDivideAndConquer and countDivideAndConquerKernel (15)
XQuicksort subtotal: 30
- Correct sequentialQuicksortKernel (5)
- Correct parallelQuicksort and parallelQuicksortKernel (25)
Whole project:
- Clarity and efficiency (10)
Pledge, Acknowledgments, Citations
As always, fill out the Pledge, Acknowledgments, and Citations file: hw3-pledge-acknowledgments-citations.txt