@ -0,0 +1,2 @@ | |||
# Project exclude paths | |||
/target/ |
@ -0,0 +1,92 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>net.jrtechs</groupId> | |||
<artifactId>parallel-java-performance-overview</artifactId> | |||
<version>1.0-SNAPSHOT</version> | |||
<name>parallel-java-performance-overview</name> | |||
<url>https://jrtechs.net/java/parallel-java-performance-overview</url> | |||
<properties> | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
<maven.compiler.source>1.8</maven.compiler.source> | |||
<maven.compiler.target>1.8</maven.compiler.target> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<version>4.11</version> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> | |||
<plugins> | |||
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> | |||
<plugin> | |||
<artifactId>maven-clean-plugin</artifactId> | |||
<version>3.1.0</version> | |||
</plugin> | |||
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> | |||
<plugin> | |||
<artifactId>maven-resources-plugin</artifactId> | |||
<version>3.0.2</version> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<version>3.8.0</version> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-surefire-plugin</artifactId> | |||
<version>2.22.1</version> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<version>3.0.2</version> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-install-plugin</artifactId> | |||
<version>2.5.2</version> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<version>2.8.2</version> | |||
</plugin> | |||
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> | |||
<plugin> | |||
<artifactId>maven-site-plugin</artifactId> | |||
<version>3.7.1</version> | |||
</plugin> | |||
<plugin> | |||
<artifactId>maven-project-info-reports-plugin</artifactId> | |||
<version>3.0.0</version> | |||
</plugin> | |||
</plugins> | |||
</pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<configuration> | |||
<source>7</source> | |||
<target>7</target> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<configuration> | |||
<source>8</source> | |||
<target>8</target> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@ -0,0 +1,22 @@ | |||
package net.jrtechs; | |||
import java.util.concurrent.ThreadLocalRandom; | |||
import java.util.stream.IntStream; | |||
public class DoMaths<E> extends WorkGenerator<Double> | |||
{ | |||
@Override | |||
Work<Double> generateWork(Double param) { | |||
return new Work<Double>() { | |||
@Override | |||
Double runTask() | |||
{ | |||
return IntStream.range(0, (int)Math.round(param)) | |||
.boxed() | |||
.map(i -> Math.sin(i * ThreadLocalRandom.current().nextDouble())) | |||
.mapToDouble(java.lang.Double::doubleValue) | |||
.sum(); | |||
} | |||
}; | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
package net.jrtechs; | |||
public class DoNothing<E> extends WorkGenerator<E> | |||
{ | |||
@Override | |||
Work<E> generateWork(E param) { | |||
return new Work<E>() { | |||
@Override | |||
E runTask() { | |||
return param; | |||
} | |||
}; | |||
} | |||
} |
@ -0,0 +1,30 @@ | |||
package net.jrtechs; | |||
import java.util.Vector; | |||
public class GenericTester<E> | |||
{ | |||
public long timeTrialMS(ParallelExecutor<E> executor, Vector<Work<E>> tasks) | |||
{ | |||
long start = System.nanoTime(); | |||
executor.runTasks(tasks); | |||
long finish = System.nanoTime(); | |||
return (finish-start)/1000000; | |||
} | |||
public Result testAll(Vector<Work<E>> tasks) | |||
{ | |||
ParallelExecutor<E> streams = new ParallelStreamsExecutor<>(); | |||
ParallelExecutor<E> threads = new RunThreads<>(); | |||
ParallelExecutor<E> manager = new Manager<>(8); | |||
ParallelExecutor<E> single = new SingleThread<>(); | |||
ParallelExecutor<E> pool = new ThreadPoolExecutor<>(); | |||
Result res = new Result(); | |||
res.streams = timeTrialMS(streams, tasks); | |||
res.manager = timeTrialMS(manager, tasks); | |||
res.threads = timeTrialMS(threads, tasks); | |||
res.pool = timeTrialMS(pool, tasks); | |||
res.singleThread = timeTrialMS(single, tasks); | |||
return res; | |||
} | |||
} |
@ -0,0 +1,66 @@ | |||
package net.jrtechs; | |||
import java.util.*; | |||
import java.util.concurrent.ConcurrentSkipListSet; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
public class Manager<E> extends ParallelExecutor<E> | |||
{ | |||
/** Number of threads to use at once */ | |||
private final int threadCount; | |||
public Manager(int threadCount) | |||
{ | |||
this.threadCount = threadCount; | |||
} | |||
/** | |||
* This is the fun method. | |||
* | |||
* This will run all of the tasks in parallel using the | |||
* desired amount of threads until all of the jobs are | |||
* complete. | |||
* @return | |||
* @param tasks | |||
*/ | |||
public List<E> runTasks(Vector<Work<E>> tasks) | |||
{ | |||
List<E> results = new Vector<>(); | |||
Queue<Integer> taskQueue = new LinkedList<>(); | |||
taskQueue.addAll(IntStream.range(0, tasks.size()) | |||
.boxed().collect(Collectors.toList())); | |||
int desiredThreads = Math.min(threadCount, tasks.size()); | |||
Thread[] runners = new Thread[desiredThreads]; | |||
for(int i = 0; i < desiredThreads; i++) | |||
{ | |||
runners[i] = new Thread(()-> | |||
{ | |||
Work<E> t; | |||
while(true) | |||
{ | |||
Integer nextTask; | |||
synchronized (taskQueue) | |||
{ | |||
nextTask = taskQueue.poll(); | |||
} | |||
if(nextTask == null) | |||
return; | |||
results.add(tasks.get(nextTask).runTask()); | |||
} | |||
}); | |||
runners[i].start(); | |||
} | |||
for(Thread t: runners) | |||
{ | |||
try | |||
{ | |||
t.join(); | |||
} catch (InterruptedException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
return results; | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
package net.jrtechs; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Vector; | |||
public abstract class ParallelExecutor<E> | |||
{ | |||
public abstract List<E> runTasks(Vector<Work<E>> tasks); | |||
} |
@ -0,0 +1,16 @@ | |||
package net.jrtechs; | |||
import java.util.List; | |||
import java.util.Vector; | |||
import java.util.stream.Collectors; | |||
public class ParallelStreamsExecutor<E> extends ParallelExecutor<E> | |||
{ | |||
@Override | |||
public List<E> runTasks(Vector<Work<E>> tasks) | |||
{ | |||
return tasks.parallelStream() | |||
.map(Work::runTask) | |||
.collect(Collectors.toList()); | |||
} | |||
} |
@ -0,0 +1,42 @@ | |||
package net.jrtechs; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
public class PythonGraphingConversion | |||
{ | |||
List<Long> single; | |||
List<Long> threaded; | |||
List<Long> manager; | |||
List<Long> streams; | |||
List<Integer> size; | |||
public PythonGraphingConversion() | |||
{ | |||
single = new ArrayList<>(); | |||
threaded = new ArrayList<>(); | |||
manager = new ArrayList<>(); | |||
streams = new ArrayList<>(); | |||
size = new ArrayList<>(); | |||
} | |||
public void addPoint(Result res, int i) | |||
{ | |||
size.add(i); | |||
single.add(res.singleThread); | |||
threaded.add(res.threads); | |||
manager.add(res.manager); | |||
streams.add(res.streams); | |||
} | |||
public void printPythonCode(String title) | |||
{ | |||
System.out.println(String.format("single = %s", this.single.toString())); | |||
System.out.println(String.format("threads = %s", this.threaded.toString())); | |||
System.out.println(String.format("manager = %s", this.manager.toString())); | |||
System.out.println(String.format("streams = %s", this.streams.toString())); | |||
System.out.println(String.format("sizes = %s", this.size.toString())); | |||
System.out.println(String.format("plot_result(single, threads, manager, streams, sizes, title='%s')", title)); | |||
} | |||
} |
@ -0,0 +1,21 @@ | |||
package net.jrtechs; | |||
public class Result | |||
{ | |||
long streams; | |||
long threads; | |||
long manager; | |||
long singleThread; | |||
long pool; | |||
@Override | |||
public String toString() { | |||
return "Result{" + | |||
"streams=" + streams + | |||
", threads=" + threads + | |||
", manager=" + manager + | |||
", singleThread=" + singleThread + | |||
", pool=" + pool + | |||
'}'; | |||
} | |||
} |
@ -0,0 +1,27 @@ | |||
package net.jrtechs; | |||
import java.util.List; | |||
import java.util.Vector; | |||
import java.util.stream.Collectors; | |||
public class RunThreads<E> extends ParallelExecutor<E> | |||
{ | |||
@Override | |||
public List<E> runTasks(Vector<Work<E>> tasks) | |||
{ | |||
List<E> results = new Vector<>(); | |||
List<Thread> threads = tasks.stream() | |||
.map(task -> | |||
new Thread(() -> results.add(task.runTask()))) | |||
.collect(Collectors.toList()); | |||
threads.forEach(Thread::start); | |||
threads.forEach(t-> { | |||
try { | |||
t.join(); | |||
} catch (InterruptedException e) { | |||
e.printStackTrace(); | |||
} | |||
}); | |||
return results; | |||
} | |||
} |
@ -0,0 +1,17 @@ | |||
package net.jrtechs; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Vector; | |||
import java.util.stream.Collectors; | |||
public class SingleThread<E> extends ParallelExecutor<E> | |||
{ | |||
@Override | |||
public List<E> runTasks(Vector<Work<E>> tasks) | |||
{ | |||
return tasks.stream() | |||
.map(Work::runTask) | |||
.collect(Collectors.toList()); | |||
} | |||
} |
@ -0,0 +1,19 @@ | |||
package net.jrtechs; | |||
public class SleepWork<E> extends WorkGenerator<E> | |||
{ | |||
@Override | |||
Work<E> generateWork(E param) { | |||
return new Work<E>() { | |||
@Override | |||
E runTask() { | |||
try { | |||
Thread.sleep(500); | |||
} catch (InterruptedException e) { | |||
e.printStackTrace(); | |||
} | |||
return param; | |||
} | |||
}; | |||
} | |||
} |
@ -0,0 +1,140 @@ | |||
package net.jrtechs; | |||
import java.sql.SQLOutput; | |||
import java.util.List; | |||
import java.util.Vector; | |||
import java.util.concurrent.ThreadLocalRandom; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
public class Test | |||
{ | |||
public static void runDoNothingTest(int max, int incrementer) | |||
{ | |||
WorkGenerator<Object> generator = new DoNothing<>(); | |||
PythonGraphingConversion result = new PythonGraphingConversion(); | |||
Vector<Work<Object>> workAll = new Vector<>(); | |||
workAll.addAll( | |||
IntStream.range(0, max) | |||
.boxed() | |||
.map(i -> generator.generateWork(i)) | |||
.collect(Collectors.toList()) | |||
); | |||
GenericTester<Object> genObjectTester = new GenericTester<>(); | |||
for(int i = 1; i <= max; i+= incrementer) | |||
{ | |||
Vector<Work<Object>> work = new Vector<>(workAll.subList(0, i)); | |||
Result res = genObjectTester.testAll(work); | |||
result.addPoint(res, i); | |||
} | |||
result.printPythonCode("Operational Overhead"); | |||
} | |||
public static void sleepTest(int max, int incrementer) | |||
{ | |||
WorkGenerator<Object> generator = new SleepWork<>(); | |||
PythonGraphingConversion result = new PythonGraphingConversion(); | |||
Vector<Work<Object>> workAll = new Vector<>(); | |||
workAll.addAll( | |||
IntStream.range(0, max) | |||
.boxed() | |||
.map(i -> generator.generateWork(i)) | |||
.collect(Collectors.toList()) | |||
); | |||
GenericTester<Object> genObjectTester = new GenericTester<>(); | |||
for(int i = 1; i <= max; i+= incrementer) | |||
{ | |||
Vector<Work<Object>> work = new Vector<>(workAll.subList(0, i)); | |||
Result res = genObjectTester.testAll(work); | |||
result.addPoint(res, i); | |||
} | |||
result.printPythonCode("Sleeping Tasks"); | |||
} | |||
public static void arithmeticWork(int max, int incrementer) | |||
{ | |||
WorkGenerator<Double> generator = new DoMaths<>(); | |||
PythonGraphingConversion result = new PythonGraphingConversion(); | |||
Vector<Work<Double>> workAll = new Vector<>(); | |||
workAll.addAll( | |||
IntStream.range(0, max) | |||
.boxed() | |||
.map(i -> generator.generateWork(i*1.0)) | |||
.collect(Collectors.toList()) | |||
); | |||
GenericTester<Double> doubleGenericTester = new GenericTester<>(); | |||
for(int i = 1; i <= max; i+= incrementer) | |||
{ | |||
Vector<Work<Double>> work = new Vector<>(workAll.subList(0, i)); | |||
Result res = doubleGenericTester.testAll(work); | |||
result.addPoint(res, i); | |||
} | |||
result.printPythonCode("Complex Maths"); | |||
} | |||
public static void main(String[] arguments) | |||
{ | |||
//sleepTest(50, 5); | |||
arithmeticWork(10000, 20); | |||
// runDoNothingTest(10000, 100); | |||
// Vector<Work<Object>> work = new Vector<>(); | |||
// work.addAll( | |||
// IntStream.range(0, 100000).boxed() | |||
// .map(i -> new Work<Object>() { | |||
// @Override | |||
// Object runTask() { | |||
// //System.out.println("running task"); | |||
// return i; | |||
// } | |||
// } | |||
// ).collect(Collectors.toList()) | |||
// ); | |||
// System.out.println(testAll(work)); | |||
// | |||
// Vector<Work<Object>> work = new Vector<>(); | |||
// work.addAll( | |||
// IntStream.range(0, 10000).boxed() | |||
// .map(i -> new Work<Object>() { | |||
// @Override | |||
// Object runTask() { | |||
// for(int z = 0; z < 10000; z++) | |||
// { | |||
// Object o = Math.sin(z * ThreadLocalRandom.current().nextDouble()); | |||
// } | |||
// return ThreadLocalRandom.current().nextDouble() * Math.PI; | |||
// //return i; | |||
// } | |||
// } | |||
// ).collect(Collectors.toList()) | |||
// ); | |||
// GenericTester<Object> genericTester = new GenericTester<>(); | |||
// System.out.println(genericTester.testAll(work)); | |||
// Vector<Work<Object>> work = new Vector<>(); | |||
// work.addAll( | |||
// IntStream.range(0, 10).boxed() | |||
// .map(i -> new Work<Object>() { | |||
// @Override | |||
// Object runTask() { | |||
// try { | |||
// Thread.sleep(500); | |||
// } catch (InterruptedException e) { | |||
// e.printStackTrace(); | |||
// } | |||
// return ThreadLocalRandom.current().nextDouble() * Math.PI; | |||
// //return i; | |||
// } | |||
// } | |||
// ).collect(Collectors.toList()) | |||
// ); | |||
// System.out.println(testAllThree(work)); | |||
} | |||
} |
@ -0,0 +1,45 @@ | |||
package net.jrtechs; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Vector; | |||
import java.util.concurrent.*; | |||
import java.util.stream.Collectors; | |||
public class ThreadPoolExecutor<E> extends ParallelExecutor<E> | |||
{ | |||
@Override | |||
public List<E> runTasks(Vector<Work<E>> tasks) | |||
{ | |||
ExecutorService executor = Executors.newCachedThreadPool(); | |||
List<Callable<E>> callables = new ArrayList<Callable<E>>(); | |||
for(Work<E> work: tasks) | |||
{ | |||
Callable<E> c = new Callable<E>() { | |||
@Override | |||
public E call() throws Exception { | |||
return work.runTask(); | |||
} | |||
}; | |||
callables.add(c); | |||
} | |||
List<E> results = new ArrayList<>(); | |||
try | |||
{ | |||
List<Future<E>> futures = executor.invokeAll(callables); | |||
for(Future<E> future: futures) | |||
{ | |||
try { | |||
results.add(future.get()); | |||
} catch (ExecutionException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
} | |||
catch (InterruptedException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
return results; | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
package net.jrtechs; | |||
public abstract class Work<E> | |||
{ | |||
abstract E runTask(); | |||
} |
@ -0,0 +1,11 @@ | |||
package net.jrtechs; | |||
import java.util.concurrent.ThreadLocalRandom; | |||
import java.util.stream.IntStream; | |||
public abstract class WorkGenerator<E> | |||
{ | |||
abstract Work<E> generateWork(E param); | |||
} |