/** * The MIT License * * Copyright (c) 2013 Pablo R. Mier <pablo.rodriguez.mier@usc.es>. * Centro de Investigación en Tecnoloxías da Información (CITIUS) http://citius.usc.es * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* * Original source: https://github.com/pablormier/parallel-loops */ package net.ftb.util; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import net.ftb.data.Settings; import net.ftb.log.Logger; /** * <p> * This class contains some useful classes and methods to parallelize code in an * easy and fluent way. Parallel class is self-contained to enable an easier * integration and reuse in different java projects. Simple example: * </p> * * <pre> * {@code Parallel.ForEach(elements, new Parallel.Action<String>() { * public void doAction(String s) { * System.out.println("Processing element " + s + " on thread " + Thread.currentThread().getName()); * } * }); * } * </pre> * * <p> * The ForEach function can be used as a parallel map function to transform a collection of elements * in parallel. For example, suppose we want to convert a list of words to upper case: * </p> * * <pre> * { * @code * Collection<String> upperCaseWords = new ForEach<Integer, String>(elements).apply(new Function<String>() * { * public Integer apply (String element) * { * return element.toUpperCase(); * } * }).values(); * } * </pre> * * @author Pablo Rodríguez Mier <pablo.rodriguez.mier@usc.es> */ public final class Parallel { private Parallel () { throw new RuntimeException("Use Parallel static methods"); } /** * First-order function interface * * @param <E> Element to transform * @param <V> Result of the transformation * @author Pablo Rodríguez Mier */ public static interface F<E, V> { /** * Apply a function over the element e. * * @param e Input element * @return transformation result */ V apply (E e); } /** * Action class can be used to define a concurrent task that does not return * any value after processing the element. * * @param <E> Element processed within the action */ public static abstract class Action<E> implements F<E, Void> { /** * This method is final and cannot be overridden. It applies the action * implemented by {@link Action#doAction(Object)}. */ public final Void apply (E element) { doAction(element); return null; } /** * Defines the action that will be applied over the element. Every * action must implement this method. * * @param element element to process */ public abstract void doAction (E element); } /** * This class provides some useful methods to handle the execution of a * collection of tasks. * * @param <V> value of the task */ public static class TaskHandler<V> { private Collection<Future<V>> runningTasks = new LinkedList<Future<V>>(); private ExecutorService executorService; public TaskHandler (ExecutorService executor, Iterable<Callable<V>> tasks, boolean poolSizeCheck, int maxPool, int sleep) { this.executorService = executor; for(Callable<V> task : tasks) { while (poolSizeCheck && (((ThreadPoolExecutor)executor).getTaskCount() - ((ThreadPoolExecutor)executor).getCompletedTaskCount()) > maxPool) { if (Settings.getSettings().getDebugLauncher()) { Logger.logDebug("system time: " + System.currentTimeMillis()); Logger.logDebug("task count: " + (((ThreadPoolExecutor)executor).getTaskCount() - ((ThreadPoolExecutor)executor).getCompletedTaskCount())); } try { Thread.sleep(sleep); } catch (Exception e) {} } runningTasks.add(executor.submit(task)); } } public TaskHandler (ExecutorService executor, Iterable<Callable<V>> tasks) { this.executorService = executor; for(Callable<V> task : tasks) { runningTasks.add(executor.submit(task)); } } /** * Get the current tasks (futures) that are being executed. * * @return Collection of futures * @see Future */ public Collection<Future<V>> tasks () { return this.runningTasks; } /** * This function is equivalent to * {@link ExecutorService#awaitTermination(long, TimeUnit)} * * @see ExecutorService#awaitTermination(long, TimeUnit) */ public boolean wait (long timeout, TimeUnit unit) throws InterruptedException { return this.executorService.awaitTermination(timeout, unit); } /** * Retrieves the result of the transformation of each element (the value * of each Future). This function blocks until all tasks are terminated. * * @return a collection with the results of the elements transformation * @throws InterruptedException * @throws ExecutionException */ public Collection<V> values () throws InterruptedException, ExecutionException { Collection<V> results = new LinkedList<V>(); for(Future<V> future : this.runningTasks) { V result = future.get(); if (result != null) { results.add(future.get()); } } return results; } public void shutdown () { executorService.shutdown(); } } /** * Class to generate a parallelized version of the for each loop. * @param <E> elements to iterate over. * @param <V> processed element type result. */ public static class ForEach<E, V> implements F<F<E, V>, TaskHandler<V>> { // Source elements private Iterable<E> elements; private boolean poolSizeCheck = false; private int sleep = 0; private int maxTasksInPool = 0; // // Default executor will run tasks in four threads private ExecutorService executor = Executors.newFixedThreadPool(4); public ForEach (Iterable<E> elements) { this.elements = elements; } /** * Configure Pool Size. * Will limit speed of the apply(). * * @param tasks Size of the tasks in pool * @param sleepTime Time to sleep(in ms) before trying add more tasks in pool * @return a ForEach instance */ public ForEach<E, V> configurePoolSize (int tasks, int sleepTime) { poolSizeCheck = true; sleep = sleepTime; maxTasksInPool = tasks; return this; } /** * Configure the number of available threads that will be used. Note * that this configuration has no effect if a custom executor * {@link ForEach#customExecutor(ExecutorService)} is provided. * * @param threads number of threads to use * @return a ForEach instance */ public ForEach<E, V> withFixedThreads (int threads) { this.executor = Executors.newFixedThreadPool(threads); return this; } /** * Set a custom executor service * * @param executor ExecutorService to use * @return the instance of ForEach configured with the new executor * service. */ public ForEach<E, V> customExecutor (ExecutorService executor) { this.executor = executor; return this; } /** * * Encapsulates the ForEach instance into a Callable that retrieves a * TaskHandler with the invoked tasks. Example: * * <pre> * {@code Collection<Double> numbers = new Collection<Double>(...); * Callable<TaskHandler<V>> forEach = new ForEach<Double, String>(numbers) * .prepare(new F<Double, String>() { * String apply(Double e) { * return e.toString(); * } * }); * forEach.call().values(); * } * </pre> * * @param f * @return */ public Callable<TaskHandler<V>> prepare (final F<E, V> f) { return new Callable<Parallel.TaskHandler<V>>() { public TaskHandler<V> call () throws Exception { return new ForEach<E, V>(elements).apply(f); } }; } public TaskHandler<V> apply (F<E, V> f) { return new TaskHandler<V>(executor, map(elements, f), poolSizeCheck, maxTasksInPool, sleep); } private Iterable<Callable<V>> map (final Iterable<E> elements, final F<E, V> f) { return new Iterable<Callable<V>>() { @Override public Iterator<Callable<V>> iterator () { return new Iterator<Callable<V>>() { Iterator<E> it = elements.iterator(); @Override public boolean hasNext () { return it.hasNext(); } @Override public Callable<V> next () { final E e = it.next(); return new Callable<V>() { public V call () throws Exception { return f.apply(e); } }; } @Override public void remove () { throw new UnsupportedOperationException(); } }; } }; } } /** * InterruptedExceptions occurred during the execution will be thrown as * RuntimeExceptions. To handle these interruptions, use new For.Each * instead of this static method. * * @param elements * @param task */ public static <A, V> Collection<V> ForEach (Iterable<A> elements, F<A, V> task) { try { TaskHandler<V> loop = new ForEach<A, V>(elements).apply(task); Collection<V> values = loop.values(); loop.executorService.shutdown(); return values; } catch (Exception e) { throw new RuntimeException("ForEach method exception. " + e.getMessage()); } } /** * Perform a parallel iteration, similar to {@code for(i=from;i<to;i++)} * but launching one thread per iteration. * * @param from starting index * @param to upper bound * @param action the action to perform in each iteration */ public static void For (final long from, final long to, final Action<Long> action) { ForEach(new Iterable<Long>() { public Iterator<Long> iterator () { return new Iterator<Long>() { private long current = from; public boolean hasNext () { return current < to; } public Long next () { return current++; } public void remove () { throw new UnsupportedOperationException(); } }; } }, action); } }