Package ca.cgjennings.algo
Class SplitJoin
- java.lang.Object
-
- ca.cgjennings.algo.SplitJoin
-
public abstract class SplitJoin extends java.lang.Object
Simplifies running an algorithm in parallel on multiple CPUs (or CPU cores). Like the fork-join model, it is used to break a problem into independent subproblems, the results of which are combined to produce a final result. Unlike the fork-join model, the split-join model only splits the problem up one time, whereas fork-join repeats the split recursively. The split-join model thus avoids some of the overhead of subproblem creation, but is more sensitive to having its performance degraded by a rate-limiting subproblem. The split-join model is typically more efficient than fork-join when the following conditions hold:- All of the computational units have similar performance characteristics (multi-core CPUs, for example).
- The problem is CPU-bound so that performance is predictable.
- The problem can be broken down in roughly equal subproblems, each with the same computational complexity. (Or, more generally, each subproblem can be expected to complete in roughly the same amount of time.)
A typical pattern for using
SplitJoin
is as follows:- Obtain one of the available shared instances using
getInstance()
. - Call that instance's
getIdealSplitCount()
to determine the number of subproblems to create. - Create an array or collection or Runnables or Callables, each of which will complete one subproblem.
- Use one of the
run
orevaluate
methods to complete the subproblems (in parallel if possible). This will return when all of the subproblems have been completed. - If necessary, combine the results of the subproblems.
- Since:
- 3.0
- Author:
- Chris Jennings
-
-
Constructor Summary
Constructors Modifier Constructor Description protected
SplitJoin()
A constructor to be used by concrete subclasses.
-
Method Summary
All Methods Static Methods Instance Methods Abstract Methods Concrete Methods Modifier and Type Method Description static SplitJoin
createInstance()
Returns a newSplitJoin
instance suited to the platform.static SplitJoin
createInstance(int nThreads)
Returns a newSplitJoin
instance that will use the specified number of threads.void
dispose()
Provides a hint to this instance that it will no longer be used.abstract <V> java.util.List<V>
evaluate(java.util.Collection<? extends java.util.concurrent.Callable<V>> subproblems)
Submits subproblems for completion.<V> java.util.List<V>
evaluateUnchecked(java.util.Collection<? extends java.util.concurrent.Callable<V>> subproblems)
Submits subproblems as if byevaluate( subproblems )
.abstract void
execute(java.lang.Runnable task)
Runs atask
.static boolean
getHighPriorityThreadHint()
Returns the value of a hint that indicates whether SplitJoins are allowed to create higher than normal priority threads.int
getIdealSplitCount()
Returns the ideal number of evenly divided subproblems to break problems into.static SplitJoin
getInstance()
Returns aSplitJoin
instance suited to the platform.abstract void
run(java.lang.Runnable[] subproblems)
Submits an array of subproblems for completion.abstract void
run(java.util.Collection<? extends java.lang.Runnable> subproblems)
Submits a collection of subproblems for completion.void
runUnchecked(java.lang.Runnable[] subproblems)
Submits subproblems as if byrun( subproblems )
.void
runUnchecked(java.util.Collection<? extends java.lang.Runnable> subproblems)
Submits subproblems as if byrun( subproblems )
.static void
setHighPriorityThreadHint(boolean enable)
Sets a hint as to whether the creation of higher than normal priority threads is allowed.static void
setParallelExecutionEnabled(boolean enable)
Sets a hint as to whether to allow parallel execution of subproblems.
-
-
-
Constructor Detail
-
SplitJoin
protected SplitJoin()
A constructor to be used by concrete subclasses. To get an instance of a suitable concrete subclass, usegetInstance()
orcreateInstance()
.
-
-
Method Detail
-
getInstance
public static SplitJoin getInstance()
Returns aSplitJoin
instance suited to the platform.- Returns:
- a suitable instance; it may be new or shared
-
createInstance
public static SplitJoin createInstance()
Returns a newSplitJoin
instance suited to the platform. This instance is guaranteed not to be shared.- Returns:
- a new instance suitable for parallelizing CPU bound tasks
-
createInstance
public static SplitJoin createInstance(int nThreads)
Returns a newSplitJoin
instance that will use the specified number of threads. This instance is guaranteed not to be shared, and is guaranteed to employ up to the specified number of threads (depending on the number of subproblems).- Parameters:
nThreads
- the number of threads to be used- Returns:
- a new instance that uses the specified number of threads
- Throws:
java.lang.IllegalArgumentException
- if the number of threads is not positive
-
run
public abstract void run(java.lang.Runnable[] subproblems) throws java.util.concurrent.ExecutionException
Submits an array of subproblems for completion. The subproblems will not return a result; if a return value is required for the join stage, submit Callables instead.- Parameters:
subproblems
- the subproblems to complete- Throws:
java.util.concurrent.ExecutionException
- if at least one subproblem throws an exception, then this method will wrap and throw one of those exceptions (which one cannot be guaranteed)
-
run
public abstract void run(java.util.Collection<? extends java.lang.Runnable> subproblems) throws java.util.concurrent.ExecutionException
Submits a collection of subproblems for completion. The subproblems will not return a result; if a return value is required for the join stage, submit Callables instead.- Parameters:
subproblems
- the subproblems to complete- Throws:
java.util.concurrent.ExecutionException
- if at least one subproblem throws an exception, then this method will wrap and throw one of those exceptions (which one cannot be guaranteed)
-
evaluate
public abstract <V> java.util.List<V> evaluate(java.util.Collection<? extends java.util.concurrent.Callable<V>> subproblems) throws java.util.concurrent.ExecutionException
Submits subproblems for completion. Each subproblem is a callable that returns a value of type V. The return values of each subproblem will be returned in an array (in the same order as the subproblems).- Type Parameters:
V
- the type of the value returned by each subproblem- Parameters:
subproblems
- the problems to complete- Returns:
- the individual return values from each subproblem
- Throws:
java.util.concurrent.ExecutionException
- if at least one subproblem throws an exception, then this method will wrap and throw one of those exceptions (which one cannot be guaranteed)
-
runUnchecked
public void runUnchecked(java.lang.Runnable[] subproblems)
Submits subproblems as if byrun( subproblems )
. However, if one of the subproblems throws an exception, it will be thrown as an uncheckedRuntimeException
.- Parameters:
subproblems
- the subproblems to complete- Throws:
java.lang.RuntimeException
- if one of the subproblems throws an exception- See Also:
run(java.lang.Runnable[])
-
runUnchecked
public void runUnchecked(java.util.Collection<? extends java.lang.Runnable> subproblems)
Submits subproblems as if byrun( subproblems )
. However, if one of the subproblems throws an exception, it will be thrown as an uncheckedRuntimeException
.- Parameters:
subproblems
- the subproblems to complete- Throws:
java.lang.RuntimeException
- if one of the subproblems throws an exception- See Also:
run(java.util.Collection)
-
evaluateUnchecked
public <V> java.util.List<V> evaluateUnchecked(java.util.Collection<? extends java.util.concurrent.Callable<V>> subproblems)
Submits subproblems as if byevaluate( subproblems )
. However, if one of the subproblems throws an exception, it will be thrown as an uncheckedRuntimeException
.- Type Parameters:
V
- the type of the value returned by each subproblem- Parameters:
subproblems
- the subproblems to complete- Returns:
- the individual return values from each subproblem
- Throws:
java.lang.RuntimeException
- if one of the subproblems throws an exception- See Also:
evaluate(java.util.Collection)
-
execute
public abstract void execute(java.lang.Runnable task)
Runs atask
. The task may be run in another thread, in which case this method will not wait for the task to complete. The purpose of this method is to reuse a thread that would normally have been used for split-join tasks instead of creating a newThread
yourself. If there is an idle split-join thread available, then this method generally has lower overhead than creating a new thread.- Parameters:
task
- the task to run in another thread
-
dispose
public void dispose()
Provides a hint to this instance that it will no longer be used. Calling this is not required, but may free up resources sooner than not calling it would. The effect of continuing to use thisSplitJoin
after disposing of it is undefined.
-
getIdealSplitCount
public int getIdealSplitCount()
Returns the ideal number of evenly divided subproblems to break problems into. (Since theSplitJoin
is unaware of the nature of the problem being solved, it cannot guarantee the accuracy of this value. However, if each subproblem performs the same amount of work, the returned value should be close to the ideal value.)- Returns:
- the number of subproblems that a problem should be broken into to achieve optimal wall clock execution times
-
setHighPriorityThreadHint
public static void setHighPriorityThreadHint(boolean enable)
Sets a hint as to whether the creation of higher than normal priority threads is allowed. Iftrue
, thenSplitJoin
instances may try to creates threads with above average priority. Note that even if enabled, this is not guaranteed to have an effect.- Parameters:
enable
-true
to allow higher than normal priority threads
-
getHighPriorityThreadHint
public static boolean getHighPriorityThreadHint()
Returns the value of a hint that indicates whether SplitJoins are allowed to create higher than normal priority threads.- Returns:
- the current value of the hint (default is
true
)
-
setParallelExecutionEnabled
public static void setParallelExecutionEnabled(boolean enable)
Sets a hint as to whether to allow parallel execution of subproblems. This may be useful during debugging or to work around any hardware- or platform-specific issues that appear (such as CPU overheating). When set tofalse
, future calls togetInstance()
orcreateInstance()
that create a new instance rather than share an existing one will return aSplitJoin
implementation that executes subproblems serially in the calling thread. To guarantee that allSplitJoin
instances run serially, this must value must be set before the first call togetInstance()
.- Parameters:
enable
- allow parallel problem solving iftrue
, disable iffalse
(default istrue
)
-
-