1

I tested an array with 10 million elements and iterating is always faster than streams. Can someone explains why?

String[] words = new String[10000000];
Arrays.fill(words, "hello world!");

Instant start = Instant.now();
long count = 0;
for (String word : words) {
    if (word.length() > 8)
        count++;
}
Instant end = Instant.now();
System.out.println("Using iterations:" + count + " in  " + Duration.between(start, end).toMillis());

start = Instant.now();
count = Arrays.stream(words).parallel().filter(word -> word.length() > 8).count();
end = Instant.now();
System.out.println("Using Streams   :" + count + " in  " + Duration.between(start, end).toMillis());

Sample run

Using iterations:10000000 in  13
Using Streams   :10000000 in  53
ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
Selva
  • 567
  • 2
  • 5
  • 12
  • 4
    My guess is that since the operation you are doing is so simple (comparing two numbers once), the overhead of streams is very significant. Plus, the if statement always runs, so branch prediction could make the loop significantly faster. But before all that, please, use a proper benchmarking library like JMH. – Sweeper Mar 12 '21 at 04:42
  • 1
    It isn't **always** faster. It depends on the example. It may also depend on the version of Java you are using. (Expect streams to get faster in the future.) – Stephen C Mar 12 '21 at 04:43
  • 2
    You are not comparing apples and apples here. One possible problem is your use of `parallel` in the stream case. Try it without that. If the tasks being performed are too small, the overheads of submitting and running tasks is likely to swamp the potential speedup from using multiple cores. – Stephen C Mar 12 '21 at 04:51
  • @StephenC that was exactly what I was thinking too - if he reran removing the parallel() I'm guessing he might be in the sub-30s for the streams case. Of course the filter stage too is possibly creating an intermediate result that has to grow or shrink (and in parallel) – Mr R Mar 12 '21 at 05:02

1 Answers1

3

Cannot reproduce with JMH:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class Streams {
    String[] words;

    @Setup
    public void setup() {
        words = new String[10_000_000];
        Arrays.fill(words, "hello world!");
    }

    @Benchmark
    public long forLoop() {
        long count = 0;
        for (String word : words) {
            if (word.length() > 8)
                count++;
        }
        return count;
    }

    @Benchmark
    public long stream() {
        return Arrays.stream(words).filter(word -> word.length() > 8).count();
    }

    @Benchmark
    public long parallelStream() {
        return Arrays.stream(words).parallel().filter(word -> word.length() > 8).count();
    }
}

Results:

Benchmark               Mode  Cnt   Score   Error  Units
Streams.forLoop         avgt    5   8,408 ? 0,086  ms/op
Streams.parallelStream  avgt    5   3,943 ? 0,353  ms/op
Streams.stream          avgt    5  12,891 ? 0,064  ms/op

As expected, a parallel stream is faster than a for loop, and a for loop is faster than a non-parallel stream.

Use right tools to measure performance.

ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
  • Of course your 24 core CPU might be helping there too, compared with his X86 :-) – Mr R Mar 12 '21 at 05:05
  • Perhaps you JDK is not the same as one the author is using. – Foredoomed Mar 12 '21 at 08:02
  • And both of these issues point to flaws in the OP's methodology. A Java benchmark result should state the hardware, the operating system and the (precise) Java release used in the test. – Stephen C Mar 17 '21 at 01:10