Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Stream-API performance with infinite series

I am observing some peculiar behavior with Java8 and the new Stream-API.

I would expect the performance of the following two statements to be identical, but it's not.

LongStream.iterate(1, n -> n + 1).limit(5000)
          .anyMatch(n -> isPerfectCube((n*n*n)+((n*n)*p)));

versus

LongStream.iterate(1, n -> n + 1)
          .anyMatch(n -> isPerfectCube((n*n*n)+((n*n)*p)));

Both statements should return true, and I wouldn't expect any performance difference given that they can both short circuit on the first match found. The only difference in the statements is that one is bounded in an upper limit on the range of numbers to iterate, while the other is not.

Can someone explain to me why one would run faster and use less memory than the other?

like image 459
Amir Afghani Avatar asked Jun 02 '14 21:06

Amir Afghani


1 Answers

There are some values of p where the condition is true for large values of n. For example with p = 3, the condition becomes true for n = 50_331_648. In that case, the limit of 5000 will of course win in terms of performance but the two calculations won't return the same result.

I have randomly picked a p (3002) that returns true for n less than 5000 and the results are very close (although the version with limit is slightly slower, probably because of the extra condition n < 5000).

Benchmark results (in micro-seconds per call to anyMatch):

Benchmark                    Mode   Samples         Mean   Mean error    Units
c.a.p.SO24003674.limit       avgt         5      130.165        2.663    us/op
c.a.p.SO24003674.noLimit     avgt         5      126.876        2.440    us/op

Benchmark code (using jmh):

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Fork(1)
public class SO24003674 {

  private int p = 3002;

  @GenerateMicroBenchmark
  public boolean limit() {
    return LongStream.iterate(1, n -> n + 1).limit(5000)
              .anyMatch(n -> isPerfectCube((n * n * n) + ((n * n) * p)));
  }

  @GenerateMicroBenchmark
  public boolean noLimit() {
    return LongStream.iterate(1, n -> n + 1)
              .anyMatch(n -> isPerfectCube((n * n * n) + ((n * n) * p)));
  }

  private static boolean isPerfectCube(long n) {
    long tst = (long) (Math.cbrt(n) + 0.5);
    return tst * tst * tst == n;
  }
}
like image 109
assylias Avatar answered Oct 17 '22 14:10

assylias