Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What am i doing wrong with java 8 lambda Predicate<Integer>? [duplicate]

This is not a duplicate of my question. I checked it and mine is how to use proper Predicate, and THAT is about differences between removeIf & remove.

I'm beginner Java programmer.
Yesterday, I tried to follow this tutorial https://dzone.com/articles/why-we-need-lambda-expressions
After I leared how to use Lambda expressions and Predicate, I made my own code to practice.
like, sum all numbers if(n % 3 == 0 || n % 5 == 0). here is my code.

public class Euler1Lambda {
    long max;
    public Euler1Lambda(long max) {
        this.max = max;
    }
public static boolean div3remainder0(int number) {
    return number % 3 == 0;
}

public static boolean div5remainder0(int number) {
    return number % 5 == 0;
}

public long sumAll() {
    long sum = 0;
    for(int i=1; i<max; i++) {
        if (div3remainder0(i) ||div5remainder0(i)) {
            sum += i;
        }
    }
    return sum;
}

public long sumAllLambda(Predicate<Integer> p) {
    long total = 0;
    for (int i = 1; i< max; i++){
        if (p.test(i)) {
            total += i;
        }
    }
return total;
}

public static void main(String[] args) {
    //conv
    long startTime = System.currentTimeMillis();
    for(int i = 0; i < 10; i++){
        new Euler1Lambda(100000000).sumAll();
    }
    long endTime = System.currentTimeMillis();
    long conv = (endTime - startTime);
    System.out.println("Total execution time: " + conv);
    //lambda
    startTime = System.currentTimeMillis();
    for(int i = 0; i < 10; i++){
        new Euler1Lambda(100000000).sumAllLambda(n -> div3remainder0(n) || div5remainder0(n));
    }
    endTime = System.currentTimeMillis();
    long lambda = (endTime - startTime);
    System.out.println("Total execution time: " + lambda);
    System.out.println("lambda / conv : " + (float)lambda/conv);
}
}

In this code, did timing tests. and result is like this.

Total execution time conv: 1761
Total execution time lambda: 3266

lambda / conv : 1.8546281

As you can see, lambda expression with predicate is slower than simple for-loop.
I have no idea why does the result like this.
What am I doing wrong? or just predicate is too slow way to use?

like image 803
Dinwy Avatar asked Jan 07 '23 00:01

Dinwy


2 Answers

First of all, let's look at the scale of things. You're talking about a difference of about 1505 ms for 100000000 items, or about 15 nanoseconds per item. That overhead is not very substantial.

That said, the overhead is from autoboxing all those ints into Integers for the sake of the Predicate<Integer>. Predicate::test takes an Integer, so p.test(i) is really getting compiled down to p.test(Integer.valueOf(i)). That method isn't super duper expensive, but it's not free. Apparently it takes about 15 nanoseconds on your computer.

If you use an IntPredicate instead — which uses an int primitive as its input, and thus avoids the boxing — you'll find that the difference between the direct and lambda-based approach is virtually gone.

Aside from that, there are the usual warnings about microbenchmarking in Java (warmup loops, using a framework like JMH, etc). There is a wealth of knowledge out there about that subject, and I strongly encourage you to read up on it if you want to continue benchmarking quick actions like this.

like image 184
yshavit Avatar answered Jan 23 '23 23:01

yshavit


Performance and lambda can be tricky. In your case the use of wrapper type Integer and auto boxing slow down stuff.

Switch

public long sumAllLambda(Predicate<Integer> p) 

to

public long sumAllLambda(IntPredicate p) 

the results are almost the same

Total execution time conv: 3190
Total execution time lambda: 3037
lambda / conv : 0.95203763 
like image 29
Haim Raman Avatar answered Jan 23 '23 23:01

Haim Raman