1

I am trying to transform each element of an array of length 10,00,00,000. My first approach is using a single thread in a simple main method. My next approach is using fork-join framework of java by dividing the array into chunks of 10,00,000. But the total time taken to transform the array is almost same in both the approaches.

public class SerialComputation {

    public static void main(String[] args) {
        Integer[] array = new Integer[100000000];

        for (int i = 0; i < array.length; i++) {
            array[i] = new Random().nextInt(100);
        }
        System.out.println("First 10 elements before transformation:");
        Arrays.asList(array).stream().limit(10).forEach(d -> System.out.print(d + " "));
        System.out.println();

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < array.length; i++) {
            array[i] *= 2;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("First 10 elements after transformation:");
        Arrays.asList(array).stream().limit(10).forEach(d -> System.out.print(d + " "));
        System.out.println();
        System.out.println("Total time taken: " + (endTime - startTime));
    }   
}


class ParallelComputation {
    public static void main(String[] args) {
        Integer[] array = new Integer[100000000];

        for (int i = 0; i < array.length; i++) {
            array[i] = new Random().nextInt(100);
        }
        System.out.println("First 10 elements before transformation:");
        Arrays.asList(array).stream().limit(10).forEach(d -> System.out.print(d + " "));
        System.out.println();

        ForkJoinTask<?> forkJoinTask = new TransformTask(0, array.length, array);
        ForkJoinPool pool = new ForkJoinPool();
        long startTime = System.currentTimeMillis();
        pool.invoke(forkJoinTask);
        long endTime = System.currentTimeMillis();

        System.out.println("First 10 elements after transformation:");
        Arrays.asList(array).stream().limit(10).forEach(d -> System.out.print(d + " "));
        System.out.println("Total time taken: " + (endTime - startTime));
    }
}

class TransformTask extends RecursiveAction {

    private static final long serialVersionUID = 1L;
    private int start;
    private int end;
    private Integer[] array;

    public TransformTask(int start, int end, Integer[] array) {
        this.start = start;
        this.end = end;
        this.array = array;
    }

    @Override
    protected void compute() {

        if (end - start <= 1000000) {
            for (int i = start; i < end; i++) {
                array[i] *= 2;
            }
        } else {
            int middle = start + ((end - start) / 2);
            System.out.println("start:" + start + "middle:" + middle + "end:" + end);
            invokeAll(new TransformTask(start, middle, array), new TransformTask(middle, end, array));
        }  
    }  
}

I am expecting the ParallelComputation to calculate the result much quicker than the SerialComputation. But both are doing the job in almost same time. I am using a machine with Intel core i7 processor with windows 10.

c0der
  • 18,467
  • 6
  • 33
  • 65
  • While it might not show anything different, you should look at [How do I write a correct micro-benchmark in Java?](https://stackoverflow.com/questions/504103/). Also, how many processors do you have? – Slaw Dec 25 '18 at 16:22
  • It has 4 cores. It's a 7th Gen Core i7 processor. In both the scenarios the CPU utilization is reaching 100 %. – Bhawani shankar Panda Dec 25 '18 at 18:54
  • 1
    Most of the work here is creating new objects. I suggest using an `int[]` if you want it to be faster. – Peter Lawrey Dec 25 '18 at 21:06
  • 1
    On my computer, using JMH, I got an average execution time of `265.343 ± 114.029 ms` for parallel and `847.664 ± 12.183 ms` for serial (with 10 warm-up iterations). If I use `int[]` instead of `Integer[]`, as Peter suggested, the average time drops to about `27 ms` for both parallel and serial. – Slaw Dec 29 '18 at 03:54

1 Answers1

0

I can't comment on TransformTask implementation, but this :

static long parallelStreamComputation() {

    Integer[] array = new Integer[100000000];

    for (int i = 0; i < array.length; i++) {
        array[i] = new Random().nextInt(100);
    }

    long startTime = System.currentTimeMillis();
    Arrays.stream(array).parallel().mapToInt( i -> i*2).toArray();
    long endTime = System.currentTimeMillis();
    return endTime-startTime;
}

Was measured to be about 10 times faster.

c0der
  • 18,467
  • 6
  • 33
  • 65
  • I don't think so. The above code is running faster if you remove .parallel(). Serial stream is faster than parallel stream in short running tasks because parallel streams will have an overhead of coordinating between different running threads. In my laptop, the above code is completing in 523 ms but after removing .parallel() it's completing in 323 ms. – Bhawani shankar Panda Dec 25 '18 at 19:01
  • On my machine parallel runs about faster (450 vs 400). Anyway it runs much faster than the option you posted and faster than 4 threads processing the array. – c0der Dec 26 '18 at 05:03