-1

Sample Program provided to count number of elements from an array that are less than the specified value. Processing time taken by the program varies, using Java 8 forEach and stream takes more time for execution. Please explain if I should go with Java 8 features and if not which areas should it be avoided, additionally will parallelStream or multithreading with multi core processor help?

Code:

public static void main(String[] args) {
    int lessThan = 4;

    // Using for loop iteration
    List<Integer> integerForSort1 = Arrays.asList(4, 1, 1, 2, 3);
    long startTime1 = System.nanoTime();
    long count1 = countNumbers(integerForSort1, lessThan);
    long stopTime1 = System.nanoTime();
    System.out.println(stopTime1 - startTime1);
    System.out.println(count1);
    integerForSort1 = null;
    System.gc();

    // Using binary search
    List<Integer> integerForSort2 = Arrays.asList(4, 1, 1, 2, 3);
    long startTime2 = System.nanoTime();
    long count2 = countByBinarySearch(integerForSort2, lessThan);
    long stopTime2 = System.nanoTime();
    System.out.println(stopTime2 - startTime2);
    System.out.println(count2);
    integerForSort2 = null;
    System.gc();

    // Using Java 8
    List<Integer> integerForSort3 = Arrays.asList(4, 1, 1, 2, 3);
    long startTime3 = System.nanoTime();
    long count3 = integerForSort3.stream()
            .filter(p -> p < lessThan)
            .count();
    long stopTime3 = System.nanoTime();
    System.out.println(stopTime3 - startTime3);
    System.out.println(count3);
    integerForSort3 = null;
    System.gc();

    //Using Java 8 for each loop
    List<Integer> integerForSort4 = Arrays.asList(4, 1, 1, 2, 3);
    long startTime4 = System.nanoTime();
    long count4 = process(integerForSort4, p -> p < lessThan);
    long stopTime4 = System.nanoTime();
    System.out.println(stopTime4 - startTime4);
    System.out.println(count4);
    integerForSort4 = null; 
}

public static long countNumbers(List<Integer> integerForSort, int lessThan) {
    long count = 0;
    Collections.sort(integerForSort);
    for (Integer anIntegerForSort : integerForSort) {
        if (anIntegerForSort < lessThan)
            count++;
    }
    return count;
}

public static long countByBinarySearch(List<Integer> integerForSort, int lessThan){
    if(integerForSort==null||integerForSort.isEmpty())
        return 0;
    int low = 0, mid = 0, high = integerForSort.size();
    Collections.sort(integerForSort);
    while(low != high){
        mid = (low + high) / 2;

        if (integerForSort.get(mid) < lessThan) {
            low = mid + 1;
        }
        else {
            high = mid;
        }
    }
    return low;
}    

public static long process(List<Integer> integerForSort, Predicate<Integer> predicate) {
    final AtomicInteger i = new AtomicInteger(0);
    integerForSort.forEach((Integer p) -> {
        if (predicate.test(p)) {
            i.getAndAdd(1);
        }
    });

    return i.intValue();
}

Output:

345918
4
21509
4
29651234
4
2242999
4

Questions:

Is it possible to reduce the process time using Java 8 features?
Why does Java 8 stream takes more time?
How can I use lambda expression with binary Search, will it process faster?

Even using multithreading with concurrent.ExecutorService gave consistent results:

Result 4 : Thread 'pool-1-thread-1' ran process - Using Java 8 stream in 6 millisecond, from 11:16:05:361 to 11:16:05:367
Result 4 : Thread 'pool-1-thread-2' ran process - Using Java 8 forEach in 3 millisecond, from 11:16:05:361 to 11:16:05:364
Result 4 : Thread 'pool-1-thread-4' ran process - Using Java 7 binary Search in 0 millisecond, from 11:16:05:379 to 11:16:05:379
Result 4 : Thread 'pool-1-thread-3' ran process - Using Java 7 for loop in 1 millisecond, from 11:16:05:362 to 11:16:05:363
gshibug
  • 1
  • 4
  • 2
    these tests prove nothing - this is cold start measurement. These numbers create a false impression of reality (it's worse than measuring at all). JMH is probably what you need to look at – Eugene Feb 06 '18 at 19:29
  • Do yourself a favor and follow Eugene's advice: learn how to use JMH. Also see https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Stefan Zobel Feb 06 '18 at 20:05
  • 1
    MicroBenchmarking using openjdk JMH, average time calculated was - Using Java 7 for loop : 4775847.330 ± 12047.627 ns/op, Using Java 7 binary Search : 2948050.460 ± 8049.363 ns/op, Using Java 8 forEach : 2055778.740 ± 10232.815 ns/op, Using Java 8 stream : 1615554.750 ± 12721.254 ns/op. Thanks, this makes sense... – gshibug Feb 07 '18 at 03:02
  • The ordinary loop would be much faster if it didn’t contain the entirely unnecessary `sort` operation. Further, it’s inconsistent to use dedicated methods for three cases, but inlined code for the forth. – Holger Feb 08 '18 at 16:44

1 Answers1

1

I do not know the answer, since I didn't perform any tests, but I think, the performance tests with 5 elements have no diagnostic value and are senseless. You should generate arrays of 10s,100s of thousands, or hundreds of milions, to see the performance difference.

Java 8 creates several objects, which causes some overhead in opposite of simple for loop. So having only 5 test elements, your results depend on how much work is needed for initialization of you execution.

You know, multiplying of 5 numbers on CPU is faster then even copying it to GPU memory, so you have your result on CPU even before GPU starts to compute. But if your data grows up and your GPU multiplies parallely hundreds of numbers, you will see the speed difference.

fairtrax
  • 416
  • 2
  • 8
  • 2
    Only IBM's JDK and OpenJ9 have (very limited) support (only) for Nvidia GPUs. And that's *only* for `forEach` under very very strict conditions. Not to mention, that is has to be enabled explicitly. So, in general, stream computations don't run on the GPU. Nevertheless, +1. – Stefan Zobel Feb 06 '18 at 20:01
  • @StefanZobel indeed. Sorry, If I am confusing, the GPU matter was an example, where too few samples also show unexpected or inversed results, that had nothing to do with java. I didn't intend to mix java streams with gpu. – fairtrax Feb 06 '18 at 20:16