
import java.lang.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;

public class ForkJoinTest {
    // compare classical ThreadPool vs ForkJoinPool
    // in this scenario, which does not involve task stealing,
    // both perform almost equally well
    public static void main(String[] args) {
        int procs = Runtime.getRuntime().availableProcessors();
        int threads = 2 * procs; // 1 * procs

        int[][] numbers = new int[10][];
        
        for (int i=0; i<numbers.length; i++) {
            numbers[i] = new int[1000000];
            for (int j=0; j<numbers[i].length; j++) {
                numbers[i][j] = 1000;
            }
        }
            
        test_invokeAll(threads, numbers);
        test_fjInvokeAll(threads, numbers);
    }
    
    static void test_invokeAll(int threads, int[][] numbers) {
        System.out.format("%nTest Executor.invokeAll%n");
        ExecutorService executor = Executors.newFixedThreadPool(threads);
        _test(executor, numbers);
    }

    static void test_fjInvokeAll(int threads, int[][] numbers) {
        System.out.format("%nTest ForkJoinPool.invokeAll%n");
        ExecutorService fjexecutor = new ForkJoinPool(threads);
        _test(fjexecutor, numbers);
    }
    
    static void _test(ExecutorService executor, int[][] numbers) {
         try { 
            long time = System.currentTimeMillis();
            
            List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();

            for (int i=0; i<numbers.length; i++) {
                final int[] nums = numbers[i];
                
                Callable<Integer> c = new Callable<Integer>() {
                    // artificial long computation 
                    // time proportional to the length of nums=numbers[i]
                    public Integer call() {
                        return _crunching(nums);
                    }
                };

                tasks.add(c);
            };
                
            List<Future<Integer>> futures = executor.invokeAll(tasks);
            time = System.currentTimeMillis() - time;
            System.out.format("... secs=%.3f %n", time/1000D);

            System.out.format("... no further waiting to get results%n");
            for (Future<Integer> fs: futures) {
                int res = fs.get();
                System.out.format("got res=%s%n", res);
            }
                
            executor.shutdown();
            // awaitTermination is not really needed here (coz we have used invokeAll() and get())
            // try { executor.awaitTermination(10, TimeUnit.SECONDS); } catch(Exception ex) { System.out.format("*** %s%n", ex.getMessage()); };

        } catch(Exception ex) { 
            System.out.format("*** %s%n", ex.getMessage());
        }
    }

    // artificial long computation 
    // time proportional to the length of nums
    static int _crunching(final int[] nums) { 
        int k = 0;
        for (int j=0; j<nums.length; j++) {
            int m = 0;
            for (int r=0; r<100000; r++) {
                m += nums[j];
            }
            k += m/100000;
        }
        return k/1000;
    }

}
            