ITEM 48: USE CAUTION WHEN MAKING STREAMS PARALLEL
225
As a simple example of a stream pipeline where parallelism is effective, con-
sider this function for computing
π
(
n
), the number of primes less
than or equal to
n
:
// Prime-counting stream pipeline - benefits from parallelization
static long pi(long n) {
return LongStream.rangeClosed(2, n)
.mapToObj(BigInteger::valueOf)
.filter(i -> i.isProbablePrime(50))
.count();
}
On my machine, it takes 31 seconds to compute
π
(10
8
) using this function. Simply
adding a
parallel()
call reduces the time to 9.2 seconds:
// Prime-counting stream pipeline - parallel version
static long pi(long n) {
return LongStream.rangeClosed(2, n)
.parallel()
.mapToObj(BigInteger::valueOf)
.filter(i -> i.isProbablePrime(50))
.count();
}
In other words, parallelizing the computation speeds it up by a factor of 3.7 on my
quad-core machine. It’s worth noting that this is
not
how you’d compute
π
(
n
) for
large
values of
n
in practice. There are far more efficient algorithms, notably
Lehmer’s formula.
If you are going to parallelize a stream of random numbers, start with a
SplittableRandom
instance rather than a
ThreadLocalRandom
(or the essentially
obsolete
Random
).
SplittableRandom
is designed for precisely this use, and has
the potential for linear speedup. ThreadLocalRandom
is designed for use by a
single thread, and will adapt itself to function as a parallel stream source, but
won’t be as fast as
SplittableRandom
.
Random
synchronizes on every operation,
so it will result in excessive, parallelism-killing contention.
In summary, do not even attempt to parallelize a stream pipeline unless you
have good reason to believe that it will preserve the correctness of the computation
and increase its speed. The cost of inappropriately parallelizing
a stream can be a
program failure or performance disaster. If you believe that parallelism may be
justified, ensure that your code remains correct when run in parallel, and do careful
performance measurements under realistic conditions. If your code remains correct
and these experiments bear out your suspicion of increased performance, then and
only then parallelize the stream in production code.
227
C H A P T E R
8
Methods
T
HIS
chapter discusses several aspects of method design: how to treat
parameters and return values, how to design method signatures, and how to
document methods. Much of the material in this chapter applies to constructors as
well as to methods. Like Chapter 4, this
chapter focuses on usability, robustness,
and flexibility.
Do'stlaringiz bilan baham: