import java.net.URL;
import java.util.concurrent.Phaser;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class FJChains {
    static final int NCPU = Runtime.getRuntime().availableProcessors();
    static final int LENGTH = 20_000_000;
    static final int PAD = 128; //64;
    static final long[] array = new long[NCPU * PAD];
    static final Phaser phaser = new Phaser(1);
    
    public static void main(String[] args) throws Exception {
        System.out.println("ForkJoinPool: " + whereIs(ForkJoinPool.class));
        for (int chains = 2; chains <= NCPU; chains <<= 1) {
            System.out.println("CHAINS: " + chains);
            runs(chains);
            Thread.sleep(500);
        }
    }

    static void runs(int chains) throws Exception {
        ForkJoinPool fjp = 
            new ForkJoinPool(chains, 
                             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                             null, true);
        for (int k = 6; k >= 0; --k) {
            int n = LENGTH >>> k;
            System.out.printf("N: %9d ms: ", n);
            for (int i = 0; i < 5; i++)
                onerun(fjp, chains, n);
            System.out.println();
        }
        fjp.shutdown();
    }

    static void onerun(ForkJoinPool fjp, int chains, int n) throws Exception {
        long start = System.nanoTime();
        Action[] as = new Action[chains];
        for (int i = 0; i < chains; i++) {
            array[i * PAD] = n;
            as[i] = new Action(i);
            phaser.register();
        }

        for (int i = 0; i < chains; i++) {
            fjp.submit(as[i]);
        }

        phaser.arriveAndAwaitAdvance();
        System.out.printf("%9d ", (System.nanoTime() - start) / 1_000_000);
    }
    
    static final class Action extends RecursiveAction {
        final int chain;

        public Action(int chain) {
            this.chain = chain;
        }

        public final void compute() {
            if (array[chain * PAD]-- <= 0)
                phaser.arriveAndDeregister();
            else
                new Action(chain).fork();
        }
    }

    public static String whereIs(Class<?> clazz) {
        final String resource = clazz.getName().replace('.', '/') + ".class";
        URL url = clazz.getResource(resource);
        if (url == null)
            url = (clazz.getClassLoader() != null ? clazz.getClassLoader() : ClassLoader.getSystemClassLoader()).getResource(resource);
        return url != null ? url.toString() : null;
    }
}
