CHAP07 - Modern-Java-in-Action/Online-Study GitHub Wiki

๋ณ‘๋ ฌ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ ์„ฑ๋Šฅ

  • ์ด ์žฅ์—์„œ๋Š”
    • ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•˜๊ธฐ
    • ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์˜ ์„ฑ๋Šฅ ๋ถ„์„
    • ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ
    • Spliterator๋กœ ์ŠคํŠธ๋ฆผ ๋ฐ์ดํ„ฐ ์ชผ๊ฐœ๊ธฐ

7.1 ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ

  • parallelStream(): ์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ์ฒญํฌ๋กœ ๋ถ„ํ• ๋œ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ๋œ๋‹ค.
  • ์‚ฌ๋ก€: n๊นŒ์ง€์˜ ์ˆซ์ž ํ•ฉ๊ณ„
    • ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
    • n์œผ๋กœ ์ŠคํŠธ๋ฆผ ํฌ๊ธฐ ์ œํ•œ
    • ๋”ํ•˜๋Š” ๋ฆฌ๋“€์‹ฑ ์ž‘์—… ์ˆ˜
public long sequentialSum(long n) {
    return Stream.iterate(1L, i -> i + 1)
        .limit(n)
        .reduce(0L, Long::sum);
}
  • ์ „ํ†ต์ ์ธ ๋ฐฉ์‹
public long iterativeSum(long n) {
    long result = 0;
    for (long i = 1L; i <= n; i++) {
        result += i;
    }
    return result;
}

7.1.1 ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

  • ์ด๋Ÿฐ ๊ฑฑ์ • NO
    • ๊ฒฐ๊ณผ ๋ณ€์ˆ˜๋ฅผ ์–ด๋–ป๊ฒŒ ๋™๊ธฐํ™”ํ•ด์•ผ?
    • ๋ช‡๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ?
    • ์ˆซ์ž๋Š” ์–ด๋–ป๊ฒŒ ์ƒ์„ฑ?
    • ์ƒ์„ฑ๋œ ์ˆซ์ž๋Š” ๋ˆ„๊ฐ€ ๋”ํ• ๊นŒ?
  • parallel() ๋ฉ”์†Œ๋“œ ์ถ”๊ฐ€
    • ์—ฌ๋Ÿฌ ์ฒญํฌ๋กœ ๋ถ„ํ• 
public long parallelSum(long n) {
    return Stream.iterate(1L, i -> i + 1)
        .limit(n)
        .parallel()
        .reduce(0L, Long::sum);
}
  • sequential() ๋„ ์žˆ๋‹ค.
  • ๋ฒˆ๊ฐˆ์•„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ๋งˆ์ง€๋ง‰ ํ˜ธ์ถœ์ด ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์— ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค.
    • pararellel์ด ๋งˆ์ง€๋ง‰ ํ˜ธ์ถœ๋ฌ์œผ๋‹ˆ.
stream.parallel()
    .filter(...)
    .sequential()
    .map(...)
    .parallel()
    .reduce();

7.1.2 ์ŠคํŠธ๋ฆผ ์„ฑ๋Šฅ ์ธก์ •

  • 3๊ฐ€์ง€ ๊ธฐ๋ฒ• ์„ฑ๋Šฅ ๋น„๊ต
    • ๋ฐ˜๋ณตํ˜•, ์ˆœ์ฐจ๋ฆฌ๋“€์Šคํ˜•, ๋ณ‘๋ ฌ ๋ฆฌ๋“€์‹ฑ
  • ๋„๊ตฌ: JMH (Java Microbenchmark Harness)
    • ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐฉ์‹์œผ๋กœ ์ธก์ • ์‹ฌํ”Œ
  • ์„ค์ •
    • XML ์„ค์ •
    • ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.17.4</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.17.4</version>
</dependency>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals><goal>shade</goal></goals>
                    <configuration>
                        <finalName>benchmarks</finalName>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.
                                                         resource.ManifestResourceTransformer">
                                <mainClass>org.openjdk.jmh.Main</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
@BenchmarkMode(Mode.AverageTime) // ํ‰๊ท  ์‹œ๊ฐ„ ์ธก์ •
@OutputTimeUnit(TimeUnit.MILLISECONDS) // ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„ ์ถœ๋ ฅ
@Fork(2, jvmArgs={"-Xms4G", "-Xmx4G"}) // 2๋ฒˆ ์‹คํ–‰ (์‹ ๋ขฐ์„ฑ UP)
public class ParallelStreamBenchmark {
    private static final long N= 10_000_000L;
    @Benchmark // ๋ฒค์น˜๋งˆํฌ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ 
    public long sequentialSum() {
        return Stream.iterate(1L, i -> i + 1).limit(N)
            .reduce( 0L, Long::sum);
    }
    @TearDown(Level.Invocation) // ๊ฐ ๋ฒค์น˜๋งˆํฌ ๋ณ„ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ ๋™์ž‘ ์‹œํ‚ค๊ธฐ 
    public void tearDown() {
        System.gc();
    }
}
  • ์‹คํ–‰ ํ™˜๊ฒฝ
    • i7-4600u 2.1GHz ์ฟผ๋“œ ์ฝ”์–ด

๊ธฐ์กด ๋ฐ˜๋ณตํ˜• (iterativeSum)

  • ์‹œ๊ฐ„: 3.278 ms
for (long i = 1L; i <= n; i++) {
	result += i;
}

์ˆœ์ฐจ๋ฆฌ๋“€์Šคํ˜• (sequentialSum)

  • ์‹œ๊ฐ„: 121.843 ms
Stream.iterate(1L, i -> i + 1)
	.limit(n)
	.reduce(0L, Long::sum);

๋ณ‘๋ ฌ ๋ฆฌ๋“€์‹ฑ (parallelSum)

  • ์‹œ๊ฐ„: 604.059 ms
Stream.iterate(1L, i -> i + 1)
        .limit(n)
        .parallel()
        .reduce(0L, Long::sum);

๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด,

  • for๋ฌธ์ด ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋‚˜์™”๋‹ค.
  • ์˜์™ธ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ ์ด์œ 
    • ๋ฐ•์‹ฑ๋œ ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋ฏ€๋กœ, ๋”ํ•˜๋ ค๋ฉด ์–ธ๋ฐ•์‹ฑ์„ ํ•ด์•ผํ•œ๋‹ค.
    • ๋ฐ˜๋ณต ์ž‘์—…์€ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋…๋ฆฝ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค.
  • ์ด์ฒ˜๋Ÿผ ๋ณ‘๋ ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ํ•จ์ •์ด ์ˆจ์–ด์žˆ๋‹ค.

๋” ํŠนํ™”๋œ ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ

  • LongStream.rangeClosed() ์žฅ์ 
    • ๊ธฐ๋ณธํ˜• long์„ ์ง์—… ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ๋ฐ•์‹ฑ๊ณผ ์–ธ๋ฐ•์‹ฑ ์˜ค๋ฒ„ํ—ค๋“œ x
    • ์‰ฝ๊ฒŒ ์ฒญํฌ๋กœ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ๋Š” ์ˆซ์ž ๋ฒ”์œ„๋ฅผ ์ƒ์„ฑ ํ•ด์ค€๋‹ค.
      • ์˜ˆ, 1~20์„ 1-5, 6-10, 11-15, 16-20 ๋กœ ๋ถ„ํ• 
  • ์•„๋ž˜ ์ฝ”๋“œ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„: 5.315 ms
    • ๊ธฐ์กด iterate ๋ฉ”์„œ๋“œ ๋ณด๋‹ค ์†๋„๊ฐ€ ๋น ๋ฅด๋‹ค.
    • ์–ด๋–ค ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ณ‘๋ ฌํ™”๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ ์ ˆํ•œ ์ž๋ฃŒ๊ตฌ์กฐ ์„ ํƒ์ด ์ค‘์š”ํ•˜๋‹ค.
@Benchmark
public long rangedSum() {
    return LongStream.rangeClosed(1, N)
        .reduce(0L, Long::sum);
}
  • ํ•˜์ง€๋งŒ, ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ์ ์šฉํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?
  • ์•„๋ž˜ ์ฝ”๋“œ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„: 2.677 ms
    • ๋“œ๋””์–ด ๋น ๋ฅธ ๋ณ‘๋ ฌ ๋ฆฌ๋“€์‹ฑ์„ ๋งŒ๋“ค์—ˆ๋‹ค.
@Benchmark
public long parallelRangedSum() {
    return LongStream.rangeClosed(1, N)
        .parallel()
        .reduce(0L, Long::sum);
}

์ฃผ์˜ํ•  ์ 

  • ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์„ ์„œ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ํ• ๋‹นํ•˜๊ณ  ๋‹ค์‹œ ํ•ฉ์น˜๋Š”๋ฐ, ์ฆ‰, ๋ฉ€ํ‹ฐ์ฝ”์–ด๊ฐ„ ๋ฐ์ดํ„ฐ ์ด๋™์€ ๋น„์‹ธ๋‹ค. ์ด๋ถ€๋ถ„์„ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

7.1.3 ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์˜ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ๋ฒ•

  • ํ€ด์ฆˆ: ๋‹ค์Œ ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋Š” ๋ฌด์—‡์ผ๊นŒ?
    • ๋‹ต: total์ด ๊ณต์œ ๋œ ์ƒํƒœ์ด๊ธฐ์— ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค๊ฐ€ ๋ฐœ์ƒ ํ•  ์ˆ˜ ์žˆ์–ด, ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ฝ”๋“œ์ด๋‹ค.
public long sideEffectSum(long n) {
    Accumulator accumulator = new Accumulator();
    LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
    return accumulator.total;
}

public class Accumulator {
    public long total = 0;
    public void add(long value) { total += value; }
}
System.out.println(sideEffectSum(10_000_000L)); // ์ฝ”๋“œ ์‹คํ–‰์‹œ ๊ฒฐ๊ณผ 500000500000์ด ๋‚˜์˜ค์ง€ ์•Š๋Š”๋‹ค...

7.1.4 ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

  • 1000๊ฐœ ์ด์ƒ ์š”์†Œ์ผ ๋•Œ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์‚ฌ์šฉ -> ์ด๋Ÿฐ ๊ธฐ์ค€์€ ์—†๋‹ค
  • ๊ทธ๋Ÿฌ๋‚˜, ์ˆ˜๋Ÿ‰์  ํžŒํŠธ ์‚ฌ์šฉ์€ ๋„์›€
    • ๋ฒค์น˜๋งˆํฌ๋กœ ์ง์ ‘ ์ธก์ •
    • ๋ฐ•์‹ฑ ์ฃผ์˜
      • IntStream, LongStream, DoubleStream ์‚ฌ์šฉ ์ถ”์ฒœ
    • ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ๋ณด๋‹ค ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์—์„œ ์„ฑ๋Šฅ์ด ๋–จ์–ด์ง€๋Š” ์—ฐ์‚ฐ
      • ์˜ˆ: limit(), findFirst()
    • ํŒŒ์ดํ”„๋ผ์ธ ์—ฐ์‚ฐ ๋น„์šฉ ๊ณ ๋ ค
      • N*Q
        • N: ์ฒ˜๋ฆฌํ•  ์š”์†Œ์ˆ˜
        • Q: ํ•˜๋‚˜ ์š”์†Œ ์ฒ˜๋ฆฌ์— ๋“œ๋Š” ๋น„์šฉ
      • Q๊ฐ€ ๋†’์„ ๊ฒฝ์šฐ, ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์‚ฌ์šฉ์‹œ ๊ฐœ์„ ๋  ํ™•๋ฅ ์ด ๋†’๋‹ค.
    • ์†Œ๋Ÿ‰ ๋ฐ์ดํ„ฐ์—์„œ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์€ ๋„์›€ x
    • ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ์ ์ ˆํ•œ์ง€ ํ™•์ธ
      • LinkedList ๋ณด๋‹จ ArrayList๊ฐ€ ํšจ์œจ์ ์œผ๋กœ ๋ถ„ํ•  ๊ฐ€๋Šฅ. ์™œ? LL์€ ๋ชจ๋“  ์š”์†Œ ํƒ์ƒ‰ํ•ด์•ผ ํ•˜๋‹ˆ.
    • ์ŠคํŠธ๋ฆผ์˜ ํŠน์„ฑ๊ณผ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ค‘๊ฐ„์—ฐ์‚ฐ์ด ์ŠคํŠธ๋ฆผ์˜ ํŠน์„ฑ์„ ์–ด๋–ป๊ฒŒ ๋ฐ”๊พธ๋Š”์ง€์— ๋”ฐ๋ผ ๋ถ„ํ•ด ๊ณผ์ • ์„ฑ๋Šฅ์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.
      • ์‰ฝ๊ฒŒ ๋งํ•ด, SIZED ์ŠคํŠธ๋ฆผ์€ ๊ฐ™์€ ํฌ๊ธฐ๋กœ ๋ถ„ํ•  ๊ฐ€๋Šฅํ•œ๋ฐ, ํ•„ํ„ฐ์—ฐ์‚ฐ์ด ์žˆ์œผ๋ฉด ์ŠคํŠธ๋ฆผ ๊ธธ์ด๋ฅผ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ํ• ์ˆ˜์žˆ์„์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.
    • ์ตœ์ข… ์—ฐ์‚ฐ์˜ ๋ณ‘ํ•ฉ ๊ณผ์ • ๋น„์šฉ์„ ์‚ดํŽด๋ณด๋ผ.

7.2 FORK/JOIN ํ”„๋ ˆ์ž„ ์›Œํฌ

  • ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์ด ์ˆ˜ํ–‰๋‚ด๋˜๋Š” ๋‚ด๋ถ€ ์ธํ”„๋ผ ๊ตฌ์กฐ๋ฅผ ์•Œ๊ธฐ์œ„ํ•ด ์ด ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค.
  • ๊ณผ์ •: ์žฌ๊ท€์ ์œผ๋กœ ์ž‘์€ ์ž‘์—…์œผ๋กœ ๋ถ„ํ•  -> ์„œ๋ธŒ ํƒœ์Šคํฌ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์นœ๋‹ค.
  • ํฌํฌ ์˜๋ฏธ: ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“œ๋Š”๊ฒƒ.

7.2.1 RecursiveTask ํ™œ์šฉ

image

  • ํ•„์š”ํ•œ ๊ณผ์ •์€ ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™๋‹ค
if (์ž‘์—…์ด ์ถฉ๋ถ„ํžˆ ์ž‘๊ฑฐ๋‚˜ ๋”์ด์ƒ ๋ถ„ํ•  ํ•  ์ˆ˜ ์—†์œผ๋ฉด) {
    // ์ˆœ์ฐจ์ ์œผ๋กœ ์ž‘์—… ๊ณ„์‚ฐ 
} else {
    // ๋‘ ์„œ๋ธŒ์ž‘์—…์œผ๋กœ ๋ถ„ํ•  
    // ํƒœ์Šคํฌ๊ฐ€ ๋‹ค์‹œ ์„œ๋ธŒ์ž‘์—…์œผ๋กœ ๋ถ„ํ• ๋˜๋„๋ก ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœ 
    // ๋ชจ๋“  ์„œ๋ธŒ์ž‘์—… ์™„๋ฃŒ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆผ
    // ์„œ๋ธŒ์ž‘์—…๋“ค ๊ฒฐ๊ณผ ํ•ฉ์นจ 
}
  • ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ด์šฉํ•œ ๋ณ‘๋ ฌ ํ•ฉ๊ณ„ ์ˆ˜ํ–‰
public class ForkJoinSumCalculator
    extends java.util.concurrent.RecursiveTask<Long> {
    private final long[] numbers; // ๋”ํ•  ์ˆซ์ž
    private final int start; // ๊ฐ ์„œ๋ธŒ์ž‘์—…๋ณ„ ์‹œ์ž‘์ˆซ์ž 
    private final int end;
    public static final long THRESHOLD = 10_000; // ์ด ๊ฐ’ ์ดํ•˜๋กœ๋Š” ๋ถ„ํ•  ๋ชปํ•˜๊ฒŒ

    public ForkJoinSumCalculator(long[] numbers) { 
        this(numbers, 0, numbers.length);
    }
    
	// ์„œ๋ธŒ์žก ์žฌ๊ท€์ ์œผ๋กœ ๋งŒ๋“ค ๋น„๊ณต๊ฐœ ์ƒ์„ฑ์ž
    private ForkJoinSumCalculator(long[] numbers, int start, int end) { 
        this.numbers = numbers; 
        this.start = start; 
        this.end = end;
    } 

    @Override 
    protected Long compute() { // RecursiveTask์˜ ์ถ”์ƒ ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋ผ์ด๋“œ
        int length = end - start; 
        if (length <= THRESHOLD) { 
            return computeSequentially();
        } 
        ForkJoinSumCalculator leftTask = 
            new ForkJoinSumCalculator(numbers, start, start + length/2); 
        leftTask.fork();
        ForkJoinSumCalculator rightTask =
            new ForkJoinSumCalculator(numbers, start + length/2, end); 
        Long rightResult = rightTask.compute(); 
        Long leftResult = leftTask.join(); 
        return leftResult + rightResult;
    }

    private long computeSequentially() { 
        long sum = 0;
        for (int i = start; i < end; i++) { 
            sum += numbers[i];
        } 
        return sum; 
    }
}

ForkJoinSumCalculator ์‹คํ–‰

  • ForkJoinPool์€ ์Šค๋ ˆ๋“œ ํ’€๋กœ ์ž‘์—…์ž ์Šค๋ ˆ๋“œ์— ๋ถ„์‚ฐ ํ• ๋‹นํ•˜๋Š” ExecutorService๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค.
  • N: 100,000 ๊ฒฝ์šฐ
    • 10๊ฐœ๋กœ ์ชผ๊ฐ  ํ›„
    • ์‹œ์ž‘์ ์€ 1~10,000 ...
  • ์ด๋Ÿฐ์‹์œผ๋กœ ์ž‘๋™๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.
    • ForkJoinSumCalculator 100,000
      • Left: ForkJoinSumCalculator 1~50,000
        • Left: ...
        • Right: ...
      • Right: ForkJoinSumCalculator 50,001~100,000
        • Left: ...
        • Right: ...
public static long forkJoinSum(long n) {
    long[] numbers = LongStream.rangeClosed(1, n).toArray(); 
    ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers); 
    return new ForkJoinPool().invoke(task);
}

TIP: ์ผ๋ฐ˜์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ForkJoinPool์„ 1๊ฐœ์˜ ์‹ฑ๊ธ€ํ„ด์œผ๋กœ ๋งŒ๋“ค๊ณ  ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

7.2.2 ํฌํฌ/์กฐ์ธ ํ”„๋ ˆ์ž„์›Œํฌ ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

  • ํ€ด์ฆˆ: ์™œ ํ•œ์ชฝ์—์„œ๋งŒ fork()๋ฅผ ์“ฐ๊ณ , ๋‹ค๋ฅธ ํ•œ์ชฝ์€ compute()๋ฅผ ์‚ฌ์šฉํ–ˆ์„๊นŒ?

    • ๊ฐ๊ฐ ์‚ฌ์šฉ์ด ํšจ์œจ์ ์ด๋‹ค. -> ๋‘ ์„œ๋ธŒํƒœ์Šคํฌ์˜ ํ•œ ํƒœ์Šคํฌ์— ๊ฐ™์€ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ์œผ๋กœ ๋ถˆํ•„์š”ํ•œ ํƒœ์Šคํฌ ํ• ๋‹น์˜ ์˜ค๋ฒ„ํ—ค๋“œ ๋ฐฉ์ง€ํ•œ๋‹ค.
  • ํฌํฌ/์กฐ์ธ์—์„œ ๋ณ‘๋ ฌ ๊ณ„์‚ฐ์‹œ ๋””๋ฒ„๊น…์ด ์–ด๋ ค์šด ์ด์œ ๋Š”?

    • fork ๋ผ ๋ถˆ๋ฆฌ๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ, compute๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๊ฐ€ ๋„์›€๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ํŒ, ํšจ์œจ์  ์„ฑ๋Šฅ์„ ์œ„ํ•ด, ์„œ๋ธŒํƒœ์Šคํฌ์˜ ์‹คํ–‰์‹œ๊ฐ„ > forking ์‹œ๊ฐ„

// ์œ„๋ž‘ ๋™์ผ ForkJoinSumCalculator ์ฝ”๋“œ 
public class ForkJoinSumCalculator
    extends java.util.concurrent.RecursiveTask<Long> {
    private final long[] numbers;
    private final int start; 
    private final int end;
    public static final long THRESHOLD = 10_000; 

    public ForkJoinSumCalculator(long[] numbers) { 
        this(numbers, 0, numbers.length);
    }

    private ForkJoinSumCalculator(long[] numbers, int start, int end) { 
        this.numbers = numbers; 
        this.start = start; 
        this.end = end;
    } 

    @Override 
    protected Long compute() { 
        int length = end - start; 
        if (length <= THRESHOLD) { 
            return computeSequentially();
        } 
        ForkJoinSumCalculator leftTask = 
            new ForkJoinSumCalculator(numbers, start, start + length/2); 
        leftTask.fork();
        ForkJoinSumCalculator rightTask =
            new ForkJoinSumCalculator(numbers, start + length/2, end); 
        Long rightResult = rightTask.compute(); 
        // ์•„๋ž˜ join()์€...
        // ๊ฒฐ๊ณผ๊ฐ€ ์ค€๋น„๋ ๋•Œ๊นŒ์ง€, ํ˜ธ์ถœ์ž๋ฅผ ๋ธ”๋ก์‹œํ‚จ๋‹ค .
        // ๊ฐ๊ฐ ์„œ๋ธŒ ์ž‘์—…์ด ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๊ธธ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ผ ๋ฐœ์ƒ์„ ๋ง‰์•„์ค€๋‹ค. 
        Long leftResult = leftTask.join(); 
        return leftResult + rightResult;
    }

    private long computeSequentially() { 
        long sum = 0;
        for (int i = start; i < end; i++) { 
            sum += numbers[i];
        } 
        return sum; 
    }
}
public static long forkJoinSum(long n) {
    long[] numbers = LongStream.rangeClosed(1, n).toArray();
    ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
    // RecursiveTask์˜ invoke()๋Š”
    // ๋ณ‘๋ ฌ ๊ณ„์‚ฐ์„ ์‹œ์ž‘ํ• ๋•Œ๋งŒ ์‚ฌ์šฉ 
    return FORK_JOIN_POOL.invoke(task); 
}

public static void main(String[] args) {
    forkJoinSum(100_000);
}

7.2.3 ์ž‘์—… ํ›”์น˜๊ธฐ (work stealing)

  • N = 1000๋งŒ ๊ฒฝ์šฐ -> 1000๊ฐœ ์ด์ƒ ์„œ๋ธŒ ํƒœ์Šคํฌ๋กœ ์ชผ๊ฐœ์งˆ๊ฒƒ
  • ํ€ด์ฆˆ: ์ฝ”์–ด๊ฐ€ ์ด๋ ‡๊ฒŒ ๋งŽ์ง€๋„ ์„œ๋ธŒํƒœ์Šคํฌ๋ฅผ ๋งŽ์ด ์ชผ๊ฐœ๋Š”๊ฒŒ ์ข‹์€๊ฑธ๊นŒ?
    • ๋‹ต: ์ฝ”์–ด์ˆ˜์™€ ๊ด€๊ณ„์—†์ด ์ ์ ˆํ•œ ํฌ๊ธฐ๋กœ ๋ถ„ํ• ๋œ ๋งŽ์€ ํƒœ์Šคํฌ๋ฅผ ํฌํ‚นํ•˜๋Š” ๊ฒƒ์€ ๋ฐ”๋žŒ์งํ•˜๋‹ค.
  • ์ž‘์—… ํ›”์น˜๊ธฐ๋Š” ๋ถ„ํ•  ๊ธฐ๋ฒ•์ด ํšจ์œจ์ ์ด์ง€ ์•Š์„๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ค€๋‹ค.
    • ์—ญํ• : ForkJoinPool์˜ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๋ฅผ ๊ณต์ •ํ•˜๊ฒŒ ๋ถ„ํ• 
    • ์ž‘๋™๊ณผ์ •
      • Doubly LinkedList ์‚ฌ์šฉ
      • ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ ์ž‘์—…์ด ๋๋‚˜๋ฉด, ๋งํฌ๋“œ๋ฆฌ์ŠคํŠธ ํ์— ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋‹ค๋ฅธ ์ž‘์—…์—๊ฒŒ ๋„˜๊ฒจ์ค€๋‹ค.
  • ํ•œ ์ž‘์—…์ž๊ฐ€ ํƒœ์Šคํฌ๋ฅผ ๋ถ„ํ• ํ–ˆ์„๋•Œ, ๋‹ค๋ฅธ ์ž‘์—…์ž๊ฐ€ ํ›”์น˜๊ธฐ ๊ฐ€๋Šฅ image

7.3 Spliterator ์ธํ„ฐํŽ˜์ด์Šค

  • Spliterator (spliter + iterator): ์ž๋™์œผ๋กœ ์ŠคํŠธ๋ฆผ์„ ๋ถ„ํ• ํ•ด์ค€๋‹ค. (์ž๋ฐ” 8์—์„œ ๋“ฑ์žฅ)
  • ๋งํฌ
    • ๊ฒฐ๋ก : ~ "๊ธฐ์กด Stream์œผ๋กœ๋Š” ๋ฒˆ๊ฑฐ๋กœ์šด ์ž‘์—…๋“ค์„ ์ข€ ๋” ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ์–ด, ์ž˜ ์“ฐ๊ธฐ๋งŒ ํ•œ๋‹ค๋ฉด ๋งŽ์ด ํ™œ์šฉ ํ•  ์ˆ˜ ์žˆ์„๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค."
public interface Spliterator<T> {
    // ์š”์†Œ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์†Œ๋น„ํ•˜๋ฉฐ ํƒ์ƒ‰ํ•  ์š”์†Œ๊ฐ€ ์žˆ์œผ๋ฉด ์ฐธ ๋ฐ˜ํ™˜. (iterator๋ž‘ ๋น„์Šท)
    boolean tryAdvance(Consumer<? super T> action);
    // ์ผ๋ถ€์š”์†Œ๋ฅผ ๋ถ„ํ• ํ•ด์„œ ๋‘๋ฒˆ์งธ Spliterator๋ฅผ ์ƒ์„ฑ ๋ฆฌํ„ด
    Spliterator<T> trySplit();
    // ํƒ์ƒ‰ํ•ด์•ผ๋  ์š”์†Œ์ˆ˜ ์ •๋ณด๋ฅผ ์ œ๊ณต 
    long estimateSize();
    // Spliterator์˜ ํŠน์„ฑ ์ง‘ํ•ฉ์„ ํฌํ•จํ•˜๋Š” int ๋ฐ˜ํ™˜ 
    int characteristics(); // ORDERED, DISTINCT, SORTED, SIZED, IMMUTABLE, CONCURRENT, SUBSIZED
}

7.3.1 ๋ถ„ํ•  ๊ณผ์ •

  • null์€ ๋”์ด์ƒ ๋ถ„ํ•  ๋ถˆ๊ฐ€ ์˜๋ฏธ

image

7.3.2 ์ปค์Šคํ…€ Spliterator ๊ตฌํ˜„ํ•˜๊ธฐ

  • ๊ธฐ์กด ๋ฐ˜๋ณตํ˜•์„ ์ด์šฉํ•ด ๋‹จ์–ด์ˆ˜๋ฅผ ๊ตฌํ•  ๊ฒฝ์šฐ
final String SENTENCE =
    "Nel mezzo del cammin di nostra vita " +
    "mi ritrovai in una selva oscura" +
    " chรฉ la dritta via era smarrita ";

System.out.println("Found " + countWordsIteratively(SENTENCE) + " words");
public int countWordsIteratively(String s) {
    int counter = 0;
    boolean lastSpace = true;
    for (char c : s.toCharArray()) {
        if (Character.isWhitespace(c)) {
            lastSpace = true;
        } else {
            if (lastSpace) counter++;
            lastSpace = false;
        }
    }
    return counter;
}
๊ฒฐ๊ณผ: Found 19 words

WordCounter ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰

  • ์•„๋ž˜ ์ฝ”๋“œ ๋ณ‘๋ ฌ X, ๊ทธ๋ƒฅ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ์‹œ
  • 266~277์ชฝ์ด ์—†๋„ค์š”...ใ…œใ…œ
// main ์ฝ”๋“œ 
Stream<Character> stream = IntStream.range(0, SENTENCE.length())
    							  .mapToObj(SENTENCE::charAt);

// ๋งŒ๋“ค์–ด์ง„ ์ŠคํŠธ๋ฆผ: [N, e, l,  , m, e, z, z, o,  , d, e, l,  , c, a, m, m, i, n,  , d, i,  , n, o, s, t, r, a,  , v, i, t, a,  , m, i,  , r, i, t, r, o, v, a, i,  , i, n,  , u, n, a,  , s, e, l, v, a,  , o, s, c, u, r, a,  , c, h, รฉ,  , l, a,  , d, r, i, t, t, a,  , v, i, a,  , e, r, a,  , s, m, a, r, r, i, t, a,  ]

System.out.println("Found " + countWords(stream) + " words");
private int countWords(Stream<Character> stream) {
    WordCounter wordCounter = stream.reduce(new WordCounter(0, true),
                                            WordCounter::accumulate,
                                            WordCounter::combine);
    return wordCounter.getCounter();
}
class WordCounter {
    private final int counter;
    private final boolean lastSpace;
    public WordCounter(int counter, boolean lastSpace) {
        this.counter = counter;
        this.lastSpace = lastSpace;
    }
    // 
    public WordCounter accumulate(Character c) {
        if (Character.isWhitespace(c)) {
            return lastSpace ?
                this : new WordCounter(counter, true);
        } else {
            return lastSpace ?
                new WordCounter(counter + 1, false) : this;
        }
    }
    // ์„œ๋ธŒ์ž‘์—…์—์„œ ๊ณ„์‚ฐ๋œ counter๋“ค์„ ํ•ฉ์น˜๋Š” ์—ญํ• . 
    public WordCounter combine(WordCounter wordCounter) {
        return new WordCounter(counter + wordCounter.counter,
                               wordCounter.lastSpace);
    }
    public int getCounter() {
        return counter;
    }
}
๊ฒฐ๊ณผ: Found 19 words
  • ์•„๋ž˜ ์ฝ”๋“œ ๋ณ‘๋ ฌ O
// main ์ฝ”๋“œ
// stream์— parallel() ์ถ”๊ฐ€
System.out.println("Found " + countWords(stream.parallel()) + " words");
  • ๊ธฐ๋Œ€ํ–ˆ๋˜ 19 words ๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๊ฐ’...
    • ์ด์œ : ์›๋ž˜์˜ ๋ฌธ์ž์—ด์„ ์›๋ž˜์˜ ์œ„์น˜์—์„œ ๋‘˜๋กœ ๋‚˜๋ˆ„๋‹ค๋ณด๋‹ˆ ์˜ˆ์ƒ์น˜ ๋ชปํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ๋‹จ์–ด๋ฅผ ๋‘˜๋กœ ๊ณ„์‚ฐํ•˜๋Š” ์ƒํ™ฉ์ด ์™”๋‹ค. ๋ฌธ์Šจ๋ง?????? ์ฆ‰, ์ˆœ์ฐจ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ”๊ฟ€๋•Œ ๋ถ„ํ•  ์œ„์น˜์— ๋”ฐ๋ผ ์ž˜๋ชป๋œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ํ•ด๊ฒฐ๋ฒ•: ๋‹จ์–ด๋ฅผ ์ค‘๊ฐ„์ด ์•„๋‹Œ ๋๋‚˜๋Š” ์œ„์น˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ถ„ํ• ํ•˜์ž.
๊ฒฐ๊ณผ: Found 25 words

ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, Spliterator ๋ฅผ ์‚ฌ์šฉํ• ๋•Œ๊ฐ€ ์™”๋‹ค!

public static void main(String[] args) {
    Spliterator<Character> spliterator = new WordCounterSpliterator(SENTENCE);
    Stream<Character> stream = StreamSupport.stream(spliterator, true);
    System.out.println("Found " + countWords(stream) + " words");
}

private static int countWords(Stream<Character> stream) {
    WordCounter wordCounter = stream.reduce(new WordCounter(0, true),
                                            WordCounter::accumulate,
                                            WordCounter::combine);
    return wordCounter.getCounter();
}
class WordCounterSpliterator implements Spliterator<Character> {
    private final String string;
    private int currentChar = 0;
    public WordCounterSpliterator(String string) {
        this.string = string;
    }
    @Override
    public boolean tryAdvance(Consumer<? super Character> action) {
        action.accept(string.charAt(currentChar++));
        return currentChar < string.length();
    }
    @Override
    public Spliterator<Character> trySplit() {
        int currentSize = string.length() - currentChar;
        if (currentSize < 10) { 
            return null;
        }
        for (int splitPos = currentSize / 2 + currentChar; splitPos < string.length(); splitPos++) {
            if (Character.isWhitespace(string.charAt(splitPos))) {
                Spliterator<Character> spliterator =
                    new WordCounterSpliterator(string.substring(currentChar,
                                                                splitPos));
                currentChar = splitPos;
                return spliterator;
            }
        }
        return null;
    }
    @Override
    public long estimateSize() {
        return string.length() - currentChar;
    }
    @Override
    public int characteristics() {
        return ORDERED + SIZED + SUBSIZED + NON-NULL + IMMUTABLE;
    }
}
๊ฒฐ๊ณผ: Found 19 words

7.4 ๋งˆ์น˜๋ฉฐ

  • ๋‚ด๋ถ€ ๋ฐ˜๋ณต์„ ์ด์šฉํ•˜๋ฉด, ๋ช…์‹œ์ ์œผ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • ํ•ญ์ƒ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ๋น ๋ฅธ๊ฒƒ์€ ์•„๋‹˜. (์ง์ ‘ ์„ฑ๋Šฅ ์ฒดํฌ ํ•„์ˆ˜)
  • ์ฒ˜๋ฆฌํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ๊ฑฐ๋‚˜ ๊ฐ ์š”์†Œ ์ฒ˜๋ฆฌ ์˜ค๋žœ์‹œ๊ฐ„ ๊ฑธ๋ฆด๋•Œ ๋ณ‘๋ ฌ์ŠคํŠธ๋ฆผ ์ถ”์ฒœ
  • ์„ฑ๋Šฅ์„ ์œ„ํ•ด, ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ ์‚ฌ์šฉ ๊ถŒ์žฅ
  • ํฌํฌ/์กฐ์ธ์€ ์„œ๋ธŒํƒœ์Šคํฌ๋กœ ๋ถ„ํ•  ํ›„ ๊ฐ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ›„ ๋จธ์ง€
  • Spliterator๋Š” ํƒ์ƒ‰ํ•˜๋ ค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ์–ด๋–ป๊ฒŒ ๋ณ‘๋ ฌํ™”ํ•  ๊ฒƒ์ธ์ง€ ์ •์˜
โš ๏ธ **GitHub.com Fallback** โš ๏ธ