| @ -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); | |||
| } | |||