Item 48. Parallel Stream

์ŠคํŠธ๋ฆผ ๋ณ‘๋ ฌํ™”๋Š” ์ฃผ์˜ํ•ด์„œ ์ ์šฉํ•˜๋ผ

์ž๋ฐ”๋Š” ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ์žˆ์–ด ๋งŽ์ด ์ง€์›๋˜๋Š” ํŽธ์ธ๋ฐ, ์ŠคํŠธ๋ฆผ ์—ญ์‹œ parallel() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ณ‘๋ ฌํ™”๋ฅผ ์ง€์›ํ•œ๋‹ค. ํ•ด๋‹น ๋ฉ”์„œ๋“œ ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•˜๋ฉด ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์—ฐ์‚ฐ์ด ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰๋˜์ง€๋งŒ, ์˜ฌ๋ฐ”๋ฅด๊ณ  ๋น ๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋„๋ก ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด์„  ๋ช‡ ๊ฐ€์ง€ ์ฃผ์˜์‚ฌํ•ญ์ด ์žˆ๋‹ค.

class Example {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE))
                .filter(mersenne -> mersenne.isProbablePrime(50))
                .limit(20)
                //  .parallel()
                .forEach(System.out::println);
        System.out.println(System.currentTimeMillis() - start);
    }

    static Stream<BigInteger> primes() {
        return Stream.iterate(BigInteger.TWO, BigInteger::nextProbablePrime);
    }
}

์œ„ ์ฝ”๋“œ์—์„œ parallel() ๋ฉ”์„œ๋“œ๋ฅผ ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹คํ–‰ํ•˜๋ฉด 10์ดˆ ๋‚ด์™ธ๋กœ ์™„๋ฃŒ๋˜์ง€๋งŒ, ์ฃผ์„์„ ํ•ด์ œํ•˜๊ณ  ์‹คํ–‰ํ•˜๋ฉด ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š๊ณ  ์—ฐ์‚ฐ๋งŒ ๊ณ„์† ์ง„ํ–‰๋œ๋‹ค. ํ•ด๋‹น ์›์ธ์€ ์ด ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณ‘๋ ฌํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋‚ด์ง€ ๋ชป ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ธ๋ฐ, ๊ทธ ์›์ธ์€ limit() ๋ฉ”์„œ๋“œ์— ์žˆ๋‹ค.

  • ๋ณ‘๋ ฌํ™”๋Š” limit์„ ๋‹ค๋ฃฐ ๋•Œ CPU ์ฝ”์–ด๊ฐ€ ๋‚จ์œผ๋ฉด ์›์†Œ๋ฅผ ๋” ์ฒ˜๋ฆฌํ•œ ๋’ค ๊ฒฐ๊ณผ๋ฅผ ๋ฒ„๋ฆฌ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

  • ํ•˜์ง€๋งŒ ์œ„ ์ฝ”๋“œ๋Š” ์ƒˆ๋กญ๊ฒŒ ๋ฉ”๋ฅด์„ผ ์†Œ์ˆ˜๋ฅผ ์ฐพ์„ ๋•Œ๋งˆ๋‹ค ๊ทธ ์ „ ์†Œ์ˆ˜๋ฅผ ์ฐพ์„ ๋•Œ๋ณด๋‹ค ํ›จ์”ฌ ์˜ค๋žœ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ๋‹ค.(์ƒˆ๋กœ์šด ์›์†Œ ํ•˜๋‚˜๊ฐ€ ๊ทธ ์ด์ „๊นŒ์ง€ ์ฒ˜๋ฆฌํ•œ ์‹œ๊ฐ„๋ณด๋‹ค ์˜ค๋ž˜ ๊ฑธ๋ฆผ)

  • ๋”ฐ๋ผ์„œ ๋ณ‘๋ ฌํ™”๋ฅผ ์ ์šฉํ•˜๋ฉด ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์ด ๋˜์–ด๋ฒ„๋ฆฌ๊ณ , ๊ฒฐ๊ณผ๋ฅผ ๋‚ด์ง€ ๋ชปํ•˜๊ณ  ๋๋‚˜๋ฒ„๋ฆฐ๋‹ค.

    • 20๋ฒˆ์งธ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์‹œ์ ์— ๋‚จ๋Š” ์ฝ”์–ด๊ฐ€ ์ƒ๊ฒจ 21,22,23 .. ์—ฐ์‚ฐ์„ ์‹œ์ž‘ํ•ด๋ฒ„๋ฆฌ์ง€๋งŒ ๊ทธ ์‹œ๊ฐ„์€ ํ›จ์”ฌ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ฒŒ ๋œ๋‹ค.

๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์˜ํ–ฅ ์š”์†Œ

๋Œ€์ฒด๋กœ ์ŠคํŠธ๋ฆผ ์†Œ์Šค๊ฐ€ ArrayList, HashMap, HashSet, ConcurrentHashMap์˜ ์ธ์Šคํ„ด์Šค๊ฑฐ๋‚˜ ๋ฐฐ์—ด, int ๋ฒ”์œ„, long ๋ฒ”์œ„์ผ ๋•Œ ๋ณ‘๋ ฌํ™”์˜ ํšจ๊ณผ๊ฐ€ ๊ฐ€์žฅ ์ข‹๋‹ค. ์ด๋Ÿฌํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋“ค์€ ๋ฐ์ดํ„ฐ๋ฅผ ์›ํ•˜๋Š” ํฌ๊ธฐ๋กœ ์ •ํ™•ํ•˜๊ณ  ์†์‰ฝ๊ฒŒ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์–ด ๋‹ค์ˆ˜์˜ ์Šค๋ ˆ๋“œ ๋ถ„๋ฐฐ๊ฐ€ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ฐธ์กฐ ์ง€์—ญ์„ฑ(Locality of Reference)

๋น ๋ฅธ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ๋Š” ์ฐธ์กฐ ์ง€์—ญ์„ฑ์ด ๋›ฐ์–ด๋‚˜์•ผ ํ•œ๋‹ค.(์ด์›ƒํ•œ ์›์†Œ์˜ ์ฐธ์กฐ๋“ค์ด ๋ฉ”๋ชจ๋ฆฌ์— ์—ฐ์†ํ•ด์„œ ์ €์žฅ๋˜์–ด ์žˆ์–ด์•ผ ํ•จ) ๋งŒ์•ฝ ์ฐธ์กฐ๋“ค์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ์ฒด๋“ค์ด ์„œ๋กœ ๋–จ์–ด์ ธ ์žˆ๋‹ค๋ฉด(์ฐธ์กฐ ์ง€์—ญ์„ฑ์ด ๋‚ฎ์œผ๋ฉด) ๋ฐ์ดํ„ฐ๊ฐ€ ์ฃผ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๋กœ ์ „์†ก๋˜๋Š” ์‹œ๊ฐ„์„ ๋Œ€๊ธฐํ•˜๊ฒŒ ๋˜๊ณ , ์ด๋Š” ๋ณ‘๋ ฌํ™”์˜ ํšจ๊ณผ๋ฅผ ๋–จ์–ด๋œจ๋ฆฐ๋‹ค.

์ŠคํŠธ๋ฆผ ์ข…๋‹จ ์—ฐ์‚ฐ

์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๋ณ‘๋ ฌํ™”์˜ ํšจ๊ณผ๋Š” ์ข…๋‹จ ์—ฐ์‚ฐ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค.

  • ๋ชจ๋“  ์›์†Œ๋ฅผ ํ•˜๋‚˜๋กœ ํ•ฉ์น˜๋Š”(count, max, min, sum) ์ถ•์†Œ(reduction) ์—ฐ์‚ฐ์€ ๋ณ‘๋ ฌํ™”์˜ ํšจ๊ณผ๊ฐ€ ๊ฐ€์žฅ ์ข‹๋‹ค.

  • anyMatch, allMatch, noneMatch ๊ฐ™์€ ์กฐ๊ฑด์— ๋งž์œผ๋ฉด ๋ฐ”๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ์—ฐ์‚ฐ๋„ ํšจ๊ณผ์ ์ด๋‹ค.

  • ํ•˜์ง€๋งŒ ๊ฐ€๋ณ€ ์ถ•์†Œ(mutable reduction)๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” collect ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋Š” ๋ณ‘๋ ฌํ™”์— ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค.

Spliterator

๋ณ‘๋ ฌํ™”๋ฅผ ๋‚˜๋ˆ„๋Š” ์ž‘์—…์€ Spliterator๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, Stream์ด๋‚˜ Iterable์˜ spliterator() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. ์ด spliterator() ๋ฉ”์„œ๋“œ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์žฌ์ •์˜ํ•˜๋ฉด ๋†’์€ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‚œ์ด๋„ ์žˆ๋Š” ์ž‘์—…์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฑ…์—์„œ๋Š” ๋‹ค๋ฃจ์ง€ ์•Š์•˜๋‹ค.

์ŠคํŠธ๋ฆผ ๋ณ‘๋ ฌํ™”์˜ ํšจ์œจ์„ฑ

์ŠคํŠธ๋ฆผ์„ ์ž˜๋ชป ๋ณ‘๋ ฌํ™”ํ•˜๋ฉด ์„ฑ๋Šฅ์ด ๋‚˜๋น ์ง€๊ฑฐ๋‚˜ ์ฒซ ์˜ˆ์‹œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์•„์˜ˆ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ์ ์€ ์–‘์˜ ๋ฐ์ดํ„ฐ๋Š” ์˜คํžˆ๋ ค ๋ณ‘๋ ฌํ™”์— ๋“œ๋Š” ์ถ”๊ฐ€ ๋น„์šฉ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ํ–ฅ์ƒ์€ ๋ฏธ๋ฏธํ•˜๊ฑฐ๋‚˜ ์˜คํžˆ๋ ค ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ๋‹ค.(์ฑ…์— ์˜ํ•˜๋ฉด ์›์†Œ ์ˆ˜ * ์ˆ˜ํ–‰ ์ฝ”๋“œ ์ˆ˜ > ์ˆ˜์‹ญ๋งŒ ์ •๋„ ๋˜์–ด์•ผ ๋ณ‘๋ ฌํ™”์˜ ํšจ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค๊ณ  ํ•œ๋‹ค.) ๋•Œ๋ฌธ์— ๋ณ‘๋ ฌํ™”๋ฅผ ๊ณ ๋ คํ•  ๋•Œ๋Š” ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์‹ค์ œ๋กœ ํšจ๊ณผ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. ์‹ค์ œ๋กœ ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ ๋ณ‘๋ ฌํ™”๋Š” ์‚ฌ์šฉํ•  ์ผ์ด ๋งŽ์ง€ ์•Š์ง€๋งŒ, ์•„๋ž˜์™€ ๊ฐ™์ด ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.(์•ฝ 5๋ฐฐ ์„ฑ๋Šฅ ํ–ฅ์ƒ, M2 Pro 10์ฝ”์–ด CPU ๊ธฐ์ค€)

class Example {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        System.out.println(piSerial(10_000_000));
        System.out.println(System.currentTimeMillis() - start);

        start = System.currentTimeMillis();
        System.out.println(piParallel(10_000_000));
        System.out.println(System.currentTimeMillis() - start);
    }

    static Long piSerial(long n) {
        return LongStream.rangeClosed(2, n)
                .mapToObj(BigInteger::valueOf)
                .filter(i -> i.isProbablePrime(50))
                .count();
    }

    static Long piParallel(long n) {
        return LongStream.rangeClosed(2, n)
                .parallel()
                .mapToObj(BigInteger::valueOf)
                .filter(i -> i.isProbablePrime(50))
                .count();
    }
}

/**** result ****
 664579
 14567
 664579
 2725
 ****************/

Last updated