๐น chap7. ๋ณ๋ ฌ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ์ฑ๋ฅ
7.1 ๋ณ๋ ฌ ์คํธ๋ฆผ
: ๊ฐ๊ฐ์ ์ค๋ ๋์์ ์ฒ๋ฆฌํ ์ ์๋๋ก ์คํธ๋ฆผ ์์๋ฅผ ์ฌ๋ฌ ์ฒญํฌ๋ก ๋ถํ ํ ์คํธ๋ฆผ์ด๋ค. ๋ชจ๋ ๋ฉํฐ์ฝ์ด ํ๋ก์ธ์๊ฐ ๊ฐ๊ฐ์ ์ฒญํฌ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ํ ๋นํ ์ ์๋ค.
์ปฌ๋ ์ ์ parallelStream์ ํธ์ถํ๋ฉด ๋ณ๋ ฌ ์คํธ๋ฆผ์ด ์์ฑ๋๋ค.
ex) ์ซ์ n์ ์ธ์๋ก ๋ฐ์ 1๋ถํฐ n๊น์ง์ ๋ชจ๋ ์ซ์์ ํฉ๊ณ๋ฅผ ๋ฐํํ๋ ๋ฉ์๋ ๊ตฌํ
1
2
3
4
5
public long sequentialSum(long n) {
return Stream.iterate(1L, i -> i + 1) // ๋ฌดํ ์์ฐ์ ์คํธ๋ฆผ ์์ฑ
.limit(n) // n๊ฐ ์ดํ๋ก ์ ํ
.reduce(0L, Long::sum); // ๋ชจ๋ ์ซ์๋ฅผ ๋ํ๋ ์คํธ๋ฆผ ๋ฆฌ๋์ฑ ์ฐ์ฐ
}
์ ํต์ ์ธ ์๋ฐ์์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๋ณต๋ฌธ์ผ๋ก ์ด๋ฅผ ๊ตฌํํ ์ ์๋ค.
1
2
3
4
5
6
7
public long iterativeSum(long n) {
long result = 0;
for (long i = 1L; i <= n; i++) {
result += i;
}
return result;
}
ํนํ n์ด ์ปค์ง๋ค๋ฉด ์ด ์ฐ์ฐ์ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ข์ ๊ฒ์ด๋ค.
- ๋ฌด์๋ถํฐ ๊ฑด๋๋ ค์ผํ๋?
- ๊ฒฐ๊ณผ ๋ณ์๋ ์ด๋ป๊ฒ ๋๊ธฐํํด์ผ ํ๋?
- ๋ช ๊ฐ์ ์ค๋ ๋๋ฅผ ์ฌ์ฉํด์ผ ํ๋?
- ์ซ์๋ ์ด๋ป๊ฒ ์์ฑํ ๊ฒ์ธ๊ฐ?
- ์์ฑ๋ ์ซ์๋ ๋๊ฐ ๋ํ๋?
๋ณ๋ ฌ ์คํธ๋ฆผ์ ์ฌ์ฉํ๋ฉด ์ ์ง๋ฌธ์ ๋ํ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ์ฝ๊ฒ ํด๊ฒฐํ ์ ์๋ค.
7.1.1 ์์ฐจ ์คํธ๋ฆผ์ ๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ๋ณํํ๊ธฐ
์์ฐจ ์คํธ๋ฆผ์ parallel ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ๊ธฐ์กด์ ํจ์ํ ๋ฆฌ๋์ฑ ์ฐ์ฐ(์ซ์ ํฉ๊ณ ๊ณ์ฐ)์ด ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌ๋๋ค.
1
2
3
4
5
public long parallelSum(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
**.parallel()** // ์คํธ๋ฆผ์ ๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
.reduce(0L, Long::sum);
์ ์ฝ๋์์๋ ์คํธ๋ฆผ์ด ์ฌ๋ฌ ์ฒญํฌ๋ก ๋ถํ ๋๋ค. ๋ฐ๋ผ์ ์๋ ๊ทธ๋ฆผ์ฒ๋ผ ๋ฆฌ๋์ฑ ์ฐ์ฐ์ ์ฌ๋ฌ ์ฒญํฌ์ ๋ณ๋ ฌ๋ก ์ํํ ์ ์๋ค. ๋ง์ง๋ง์ผ๋ก ๋ฆฌ๋์ฑ ์ฐ์ฐ์ผ๋ก ์์ฑ๋ ๋ถ๋ถ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ๋ฆฌ๋์ฑ ์ฐ์ฐ์ผ๋ก ํฉ์ณ์ ์ ์ฒด ์คํธ๋ฆผ์ ๋ฆฌ๋์ฑ ๊ฒฐ๊ณผ๋ฅผ ๋์ถํ๋ค.
์ฌ์ค ์์ฐจ ์คํธ๋ฆผ์ parallel์ ํธ์ถํด๋ ์คํธ๋ฆผ ์์ฒด์๋ ์๋ฌด ๋ณํ๋ ์ผ์ด๋์ง ์๋๋ค. ๋ด๋ถ์ ์ผ๋ก๋ parallel์ ํธ์ถํ๋ฉด ์ดํ ์ฐ์ฐ์ด ๋ณ๋ ฌ๋ก ์ํํด์ผ ํจ์ ์๋ฏธํ๋ ๋ถ๋ฆฌ์ธ ํ๋๊ทธ๊ฐ ์ค์ ๋๋ค.
๋ฐ๋๋ก sequential๋ก ๋ณ๋ ฌ ์คํธ๋ฆผ์ ์์ฐจ ์คํธ๋ฆผ์ผ๋ก ๋ฐ๊ฟ ์ ์๋ค. ์ด ๋ ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ด๋ค ์ฐ์ฐ์ ๋ณ๋ ฌ๋ก ์คํํ๊ณ ์ด๋ค ์ฐ์ฐ์ ์์ฐจ๋ก ์คํํ ์ง ์ ์ดํ ์ ์๋ค.
1
2
3
4
5
6
stream.parallel()
.filter(...)
.sequential()
.map(...)
.parallel()
.reduce();
parallel๊ณผ sequential ๋ ๋ฉ์๋ ์ค ์ต์ข ์ ์ผ๋ก ํธ์ถ๋ ๋ฉ์๋๊ฐ ์ ์ฒด ํ์ดํ๋ผ์ธ์ ์ํฅ์ ๋ฏธ์น๋ค.
์ ์ฝ๋์์ ํ์ดํ๋ผ์ธ์ ๋ง์ง๋ง ํธ์ถ์ parallel์ด๋ฏ๋ก ํ์ดํ๋ผ์ธ์ ์ ์ฒด์ ์ผ๋ก ๋ณ๋ ฌ๋ก ์คํ๋๋ค.
7.1.2 ์คํธ๋ฆผ ์ฑ๋ฅ ์ธก์
๋ณ๋ ฌํ๋ฅผ ์ด์ฉํ๋ฉด ์์ฐจ๋ ๋ฐ๋ณต ํ์์ ๋นํด ์ฑ๋ฅ์ด ์ข์์ง ๊ฒ์ด๋ผ ์ถ์ธกํ์ง๋ง, ์ค์ ์ธก์ ์ ํด๋ด์ผํ๋ค.
๋ฐ๋ผ์ ์๋ฐ ๋ง์ดํฌ๋ก๋ฒค์น๋งํฌ ํ๋์ค(JMH)๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด ์์ ๋ฒค์น๋งํฌ๋ฅผ ๊ตฌํํ๋ค. JMH๋ฅผ ์ด์ฉํ๋ฉด ๊ฐ๋จํ๊ณ , ์ด๋ ธํ ์ด์ ๊ธฐ๋ฐ ๋ฐฉ์์ ์ง์ํ๋ฉฐ, ์์ ์ ์ผ๋ก ์๋ฐ ํ๋ก๊ทธ๋จ์ด๋ ์๋ฐ ๊ฐ์ ๋จธ์ (JVM)์ ๋์์ผ๋ก ํ๋ ๋ค๋ฅธ ์ธ์ด์ฉ ๋ฒค์น๋งํฌ๋ฅผ ๊ตฌํํ ์ ์๋ค.
๋ฉ์ด๋ธ(Maven) ๋น๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๋น๋ ๊ณผ์ ์ ์ ์ํ๋ pom.xml ํ์ผ์ ๋ช ๊ฐ์ง ์์กด์ฑ์ ์ถ๊ฐํด ํ๋ก์ ํธ์์ JMH๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jml-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>
์ฒซ ๋ฒ์งธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํต์ฌ JMH ๊ตฌํ์ ํฌํจํ๊ณ , ๋ ๋ฒ์งธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์๋ฐ ์์นด์ด๋ธ(Java Archive, JAR) ํ์ผ์ ๋ง๋๋ ๋ฐ ๋์์ ์ฃผ๋ ์ด๋ ธํ ์ด์ ํ๋ก์ธ์๋ฅผ ํฌํจํ๋ค.
๋ฉ์ด๋ธ ์ค์ ์ ๋ค์ ํ๋ฌ๊ทธ์ธ๋ ์ถ๊ฐํ ๋ค์ ์๋ฐ ์์นด์ด๋ธ ํ์ผ์ ์ด์ฉํด์ ๋ฒค์น๋งํฌ๋ฅผ ํธ๋ฆฌํ๊ฒ ์คํํ ์ ์๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>
์๋ ์ฝ๋์ฒ๋ผ ์ด์ ์ sequentialSum ๋ฉ์๋๋ฅผ ๊ฐ๋จํ๊ฒ ๋ฒค์น๋งํฌํ ์ ์๋ค.
- n๊ฐ์ ์ซ์๋ฅผ ๋ํ๋ ํจ์์ ์ฑ๋ฅ ์ธก์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@BenchmarkMode(Mode.AverageTime) // ๋ฒค์น๋งํฌ ๋์ ๋ฉ์๋๋ฅผ ์คํํ๋ ๋ฐ ๊ฑธ๋ฆฐ ํ๊ท ์๊ฐ ์ธก์
@OutputTimeUnit(TimeUnit.MILLISECONS) // ๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ฆฌ์ด ๋จ์๋ก ์ถ๋ ฅ
@Fork(2, jvmArgs={"-Xms4G", "-Xmx4G"}) // 4Gb์ ํ ๊ณต๊ฐ์ ์ ๊ณตํ ํ๊ฒฝ์์ ๋ ๋ฒ ๋ฒค์น๋งํฌ๋ฅผ ์ํํด ๊ฒฐ๊ณผ์ ์ ๋ขฐ์ฑ ํ๋ณด
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();
}
}
ํด๋์ค๋ฅผ ์ปดํ์ผํ๋ฉด ์ด์ ์ ์ค์ ํ ๋ฉ์ด๋ธ ํ๋ฌ๊ทธ์ธ์ด benchmarks.jar๋ผ๋ ๋ ๋ฒ์งธ ํ์ผ์ ๋ง๋ ๋ค. ๋ค์์ฒ๋ผ ์คํํ ์ ์๋ค.
1
java -jar ./target/benchmarks.jar ParallelStreamBenchmark
โ ๋ฒค์น๋งํฌ๊ฐ ๊ฐ๋ฅํ ๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์ํฅ์ ๋ฐ์ง ์๋๋ก ํ์ ํฌ๊ธฐ๋ฅผ ์ถฉ๋ถํ๊ฒ ์ค์
โ ๋ฒค์น๋งํฌ๊ฐ ๋๋ ๋๋ง๋ค ๊ฐ๋น์ง ์ปฌ๋ ํฐ๊ฐ ์คํ๋๋๋ก ๊ฐ์
์ด๋ ๊ฒ ํด๋ ๊ฒฐ๊ณผ๋ ์ ํํ์ง ์์ ์ ์๋ค. (๊ธฐ๊ณ๊ฐ ์ง์ํ๋ ์ฝ์ด์ ๊ฐ์ ๋ฑ์ด ์คํ ์๊ฐ์ ์ํฅ์ ๋ฏธ์น ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
- ์ ์ฝ๋ ํ๋ก๊ทธ๋จ ์คํ ํ์
- ๋ฒค์น๋งํฌ ์ค๋น: JMH ๋ช ๋ น์ ํซ์คํ์ด ์ฝ๋๋ฅผ ์ต์ ํ ํ ์ ์๋๋ก 20๋ฒ ์คํ
- ์ต์ข ๊ฒฐ๊ณผ ๊ณ์ฐ: 20๋ฒ ๋ ์คํ
JMH๋ ๊ธฐ๋ณธ์ ์ผ๋ก 20+20ํ ํ๋ก๊ทธ๋จ์ ๋ฐ๋ณต ์คํ
(ํน์ ์ด๋ ธํ ์ด์ ์ด๋ -w, -i ํ๋๊ทธ๋ฅผ ๋ช ๋ นํ์ ์ถ๊ฐํด์ ์ด ๊ธฐ๋ณธ ๋์ ํ์๋ฅผ ์กฐ์ ํ ์ ์๋ค.)
๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ
๋ค์์ ์ธํ i7-4600U 2.1GHz ์ฟผ๋ ์ฝ์ด์์ ์คํํ ๊ฒฐ๊ณผ๋ค.
์๋ ์ฝ๋๋ ์ ํต์ ์ธ for ๋ฃจํ๋ฅผ ์ฌ์ฉํด ๋ฐ๋ณตํ๋ ๋ฐฉ๋ฒ์ด ๋ ์ ์์ค์ผ๋ก ๋์ํ ๋ฟ ์๋๋ผ ํนํ ๊ธฐ๋ณธ๊ฐ์ ๋ฐ์ฑํ๊ฑฐ๋ ์ธ๋ฐ์ฑํ ํ์๊ฐ ์์ผ๋ฏ๋ก ๋ ๋น ๋ฅผ ๊ฒ์ด๋ผ ์์ํ ์ ์๋ค.
1
2
3
4
5
6
7
8
@Benchmark
public long iterativeSum() {
long result = 0;
for (long i = 1L; i <= N; i++) {
result += i;
}
return result;
}
๊ฐ์ ์ปดํจํฐ๋ก ๋ ๋ฒ์งธ ๋ฒค์น๋งํฌ ์ฝ๋๋ฅผ ๋๋ฆฐ ๊ฒฐ๊ณผ์ด๋ค.
์์๋๋ก ์์ฐจ์ ์ผ๋ก ์คํธ๋ฆผ์ ์ฌ์ฉํ๋ ๋ฒ์ ์ ๋นํด ๊ฑฐ์ 4๋ฐฐ๊ฐ ๋น ๋ฅด๋ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๋ฒ์๋ ๋ฒค์น๋งํฌ ํด๋์ค์ ๋ณ๋ ฌ ์คํธ๋ฆผ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋ฒ์ ์ ๊ฒฐ๊ณผ๋ค.
๋ณ๋ ฌ ๋ฒ์ ์ด ์ฟผ๋ ์ฝ์ด CPU๋ฅผ ์ฌ์ฉํ์ง ๋ชปํ๊ณ ์์ฐจ ๋ฒ์ ์ ๋นํด ๋ค์ฏ ๋ฐฐ๋ ๋๋ฆฐ ๊ฒฐ๊ณผ๊ฐ ๋์๋ค. ์ด ๊ฒฐ๊ณผ์ ๋ํด ๋ ๊ฐ์ง ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ ์ ์๋ค.
- ๋ฐ๋ณต ๊ฒฐ๊ณผ๋ก ๋ฐ์ฑ๋ ๊ฐ์ฒด๊ฐ ๋ง๋ค์ด์ง๋ฏ๋ก ์ซ์๋ฅผ ๋ํ๋ ค๋ฉด ์ธ๋ฐ์ฑ์ ํด์ผ ํ๋ค.
- ๋ฐ๋ณต ์์ ์ ๋ณ๋ ฌ๋ก ์ํํ ์ ์๋ ๋ ๋ฆฝ ๋จ์๋ก ๋๋๊ธฐ๊ฐ ์ด๋ ต๋ค.
์๋ ๊ทธ๋ฆผ์ฒ๋ผ ์ด์ ์ฐ์ฐ์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ๋ค์ ํจ์์ ์ ๋ ฅ์ด ๋ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ iterate ์ฐ์ฐ์ ์ฒญํฌ๋ก ๋ถํ ํ๊ธฐ๊ฐ ์ด๋ ต๋ค.
๋ฆฌ๋์ฑ ๊ณผ์ ์ ์์ํ๋ ์์ ์ ์ ์ฒด ์ซ์ ๋ฆฌ์คํธ๊ฐ ์ค๋น๋์ง ์์์ผ๋ฏ๋ก ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ ์ ์๋๋ก ์ฒญํฌ๋ก ๋ถํ ํ ์ ์๋ค. ์คํธ๋ฆผ์ด ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌ๋๋๋ก ์ง์ํ๊ณ ๊ฐ๊ฐ์ ํฉ๊ณ๊ฐ ๋ค๋ฅธ ์ค๋ ๋์์ ์ํ๋์์ง๋ง ๊ฒฐ๊ตญ ์์ฐจ์ฒ๋ฆฌ ๋ฐฉ์๊ณผ ํฌ๊ฒ ๋ค๋ฅธ ์ ์ด ์์ผ๋ฏ๋ก ์ค๋ ๋๋ฅผ ํ ๋นํ๋ ์ค๋ฒํค๋๋ง ์ฆ๊ฐํ๊ฒ ๋๋ค.
๋ณ๋ ฌ ํ๋ก๊ทธ๋๋ฐ์ ์ค์ฉํ๋ฉด ์คํ๋ ค ์ ์ฒด ํ๋ก๊ทธ๋จ์ ์ฑ๋ฅ์ด ๋ ๋๋น ์ง ์๋ ์๋ค. ๋ฐ๋ผ์ parallel ๋ฉ์๋๋ฅผ ํธ์ถํ์ ๋ ๋ด๋ถ์ ์ผ๋ก ์ด๋ค ์ผ์ด ์ผ์ด๋๋์ง ๊ผญ ์ดํดํด์ผ ํ๋ค.
๋ ํนํ๋ ๋ฉ์๋ ์ฌ์ฉ
LongStream.rangeClosed๋ผ๋ ๋ฉ์๋๋ iterate์ ๋นํด ๋ค์๊ณผ ๊ฐ์ ์ฅ์ ์ ์ ๊ณตํ๋ค.
- LongStream.rangeClosed๋ ๊ธฐ๋ณธํ long์ ์ง์ ์ฌ์ฉํ๋ฏ๋ก ๋ฐ์ฑ๊ณผ ์ธ๋ฐ์ฑ ์ค๋ฒํค๋๊ฐ ์ฌ๋ผ์ง๋ค.
- LongStream.rangeClosed๋ ์ฝ๊ฒ ์ฒญํฌ๋ก ๋ถํ ํ ์ ์๋ ์ซ์ ๋ฒ์๋ฅผ ์์ฐํ๋ค. ์๋ฅผ ๋ค์ด 1-20 ๋ฒ์์ ์ซ์๋ฅผ ๊ฐ๊ฐ 1-5, 6-10, 11-15, 16-20 ๋ฒ์์ ์ซ์๋ก ๋ถํ ํ ์ ์๋ค.
์ธ๋ฐ์ฑ๊ณผ ๊ด๋ จํ ์ค๋ฒํค๋๋ ์ผ๋ง๋ ๋ ๊น? ์ฐ์ ์์ฐจ ์คํธ๋ฆผ์ ์ฒ๋ฆฌํ๋ ์๊ฐ์ ์ธก์ ํ๋ค.
1
2
3
4
5
@Benchmark
public long rangedSum() {
return LongStream.rangeClosed(1, N)
.reduce(0L, Long::sum);
}
๊ธฐ์กด์ iterate ํฉํ ๋ฆฌ ๋ฉ์๋๋ก ์์ฑํ ์์ฐจ ๋ฒ์ ์ ๋นํด ์ด ์์ ์ ์ซ์ ์คํธ๋ฆผ ์ฒ๋ฆฌ ์๋๊ฐ ๋ ๋น ๋ฅด๋ค. ํนํ๋์ง ์์ ์คํธ๋ฆผ์ ์ฒ๋ฆฌํ ๋๋ ์คํ ๋ฐ์ฑ, ์ธ๋ฐ์ฑ ๋ฑ์ ์ค๋ฒํค๋๋ฅผ ์๋ฐํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ํฉ์ ๋ฐ๋ผ์๋ ์ด๋ค ์๊ณ ๋ฆฌ์ฆ์ ๋ณ๋ ฌํํ๋ ๊ฒ๋ณด๋ค ์ ์ ํ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ ํํ๋ ๊ฒ์ด ๋ ์ค์ํ๋ค๋ ์ฌ์ค์ ๋จ์ ์ผ๋ก ๋ณด์ฌ์ค๋ค. ์ด๋ฒ์ ๋ณ๋ ฌ ์คํธ๋ฆผ์ ์ด๋จ๊น?
1
2
3
4
5
6
@Benchmark
public long parallelRangedSum() {
return LongStream.rangedClosed(1, N)
.parallel()
.reduce(0L, Long::sum);
}
์ด๋ฒ์ ์ค์ง์ ์ผ๋ก ๋ฆฌ๋์ฑ ์ฐ์ฐ์ด ๋ณ๋ ฌ๋ก ์ํ๋๋ค. ์ฌ๋ฐ๋ฅธ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ ํํด์ผ ๋ณ๋ ฌ ์คํ๋ ์ต์ ์ ๊ธฐ๋ฅ์ ๋ฐํํ ์ ์๋ค๋ ๊ฒ์ ํ์ธํ๋ค.
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ฌ๋ฐ๋ก ์ฌ์ฉํ๋ฉด ๋ฐ๋ณต์ ์ผ๋ก ์ฝ๋๋ฅผ ์คํํ๋ ๋ฐฉ๋ฒ์ ๋นํด ์ต์ ๋ฉํฐ์ฝ์ด CPU๊ฐ ์ ๊ณตํ๋ ๋ณ๋ ฌ ์คํ์ ํ์ ๋จ์ํ๊ฒ ์ง์ ์ ์ผ๋ก ์ป์ ์ ์๋ค.
But ๋ณ๋ ฌํ๊ฐ ์์ ๊ณต์ง๋ ์๋๋ค. ๋ณ๋ ฌํ๋ฅผ ์ด์ฉํ๋ ค๋ฉด
- ์คํธ๋ฆผ์ ์ฌ๊ท์ ์ผ๋ก ๋ถํ ํด์ผ ํ๊ณ
- ๊ฐ ์๋ธ์คํธ๋ฆผ์ ์๋ก ๋ค๋ฅธ ์ค๋ ๋์ ๋ฆฌ๋์ฑ ์ฐ์ฐ์ผ๋ก ํ ๋นํ๊ณ
- ์ด๋ค ๊ฒฐ๊ณผ๋ฅผ ํ๋์ ๊ฐ์ผ๋ก ํฉ์ณ์ผ ํ๋ค.
๋ฉํฐ์ฝ์ด ๊ฐ์ ๋ฐ์ดํฐ ์ด๋์ ์๊ฐ๋ณด๋ค ๋น์ธ๋ค. ๋ฐ๋ผ์ ์ฝ์ด ๊ฐ์ ๋ฐ์ดํฐ ์ ์ก ์๊ฐ๋ณด๋ค ํจ์ฌ ์ค๋ ๊ฑธ๋ฆฌ๋ ์์ ๋ง ๋ณ๋ ฌ๋ก ๋ค๋ฅธ ์ฝ์ด์์ ์ํํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค.
7.1.3 ๋ณ๋ ฌ ์คํธ๋ฆผ์ ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ๋ฒ
๋ณ๋ ฌ ์คํธ๋ฆผ์ ์๋ชป ์ฌ์ฉํ๋ฉด์ ๋ฐ์ํ๋ ๋ง์ ๋ฌธ์ ๋ ๊ณต์ ๋ ์ํ๋ฅผ ๋ฐ๊พธ๋ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ผ์ด๋๋ค. ๋ค์์ n๊น์ง์ ์์ฐ์๋ฅผ ๋ํ๋ฉด์ ๊ณต์ ๋ ๋์ ์๋ฅผ ๋ฐ๊พธ๋ ํ๋ก๊ทธ๋จ์ ๊ตฌํํ ์ฝ๋๋ค.
1
2
3
4
5
6
7
8
9
10
public long sideEffectSum(long n) {
Accumulator accumulator = new Accumulator();
LongStream.rangeClosed(1, n).forEach(accumulator::add);
return accumulator.total;
}
public class Accumulator {
public long total = 0;
public void add(long value) { total += value; }
}
๋์ ์๋ฅผ ์ด๊ธฐํํ๊ณ , ๋ฆฌ์คํธ์ ๊ฐ ์์๋ฅผ ํ๋์ฉ ํ์ํ๋ฉด์ ๋์ ์์ ์ซ์๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
์ ์ฝ๋๋ ๋ณธ์ง์ ์ผ๋ก ์์ฐจ ์คํํ ์ ์๋๋ก ๊ตฌํ๋์ด ์์ผ๋ฏ๋ก ๋ณ๋ ฌ๋ก ์คํํ๋ฉด ์ฐธ์ฌ๊ฐ ์ผ์ด๋๋ค. ํนํ total์ ์ ๊ทผํ ๋๋ง๋ค ๋ฐ์ดํฐ ๋ ์ด์ค ๋ฌธ์ (๋ค์์ ์ค๋ ๋์์ ๋์์ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋) ๋ฌธ์ ๊ฐ ์ผ์ด๋๋ค. ๋๊ธฐํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค๋ณด๋ฉด ๊ฒฐ๊ตญ ๋ณ๋ ฌํ๋ผ๋ ํน์ฑ์ด ์์ด์ ธ ๋ฒ๋ฆด ๊ฒ์ด๋ค.
1
2
3
4
5
public long sideEffectParallelSum(long n) {
Accumulator accumulator = new Accumulator();
LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
return accumulator.total;
}
1
2
System.out.println("SideEffect parallel sum done in: " +
measurePerf(ParallelStreams::sideEffectParallelSum, 10_000_000L) + " msecs" );
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
๋ฉ์๋์ ์ฑ๋ฅ์ ๋์งธ ์น๊ณ , ์ฌ๋ฐ๋ฅธ ๊ฒฐ๊ณผ๊ฐ(50000005000000)์ด ๋์ค์ง ์๋๋ค. ์ฌ๋ฌ ์ค๋ ๋์์ ๋์์ ๋์ ์, ์ฆ total += value๋ฅผ ์คํํ๋ฉด์ ์ด๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ฌ๋ฌ ์ค๋ ๋์์ ๊ณต์ ํ๋ ๊ฐ์ฒด์ ์ํ๋ฅผ ๋ฐ๊พธ๋ forEach ๋ธ๋ก ๋ด๋ถ์์ add ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด์ ์ด ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ์ํ ๊ณต์ ์ ๋ฐ๋ฅธ ๋ถ์์ฉ์ ํผํด์ผ ํ๋ค.
7.1.4 ๋ณ๋ ฌ ์คํธ๋ฆผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ
- ํ์ ์ด ์์ง ์์ผ๋ฉด ์ง์ ์ธก์ ํ๋ผ. ์์ฐจ ์คํธ๋ฆผ์ ๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ์ฝ๊ฒ ๋ฐ๊ฟ ์ ์๋ค. ์ ์ ํ ๋ฒค์น๋งํฌ๋ก ์ง์ ์ฑ๋ฅ์ ์ธก์ ํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค.
- ๋ฐ์ฑ์ ์ฃผ์ํ๋ผ. ์๋ ๋ฐ์ฑ๊ณผ ์ธ๋ฐ์ฑ์ ์ฑ๋ฅ์ ํฌ๊ฒ ์ ํ์ํฌ ์ ์๋ ์์๋ค. ๋๋๋ก์ด๋ฉด ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ(IntStream, LongStream, DoubleStream)์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
์์ฐจ ์คํธ๋ฆผ๋ณด๋ค ๋ณ๋ ฌ ์คํธ๋ฆผ์์ ์ฑ๋ฅ์ด ๋จ์ด์ง๋ ์ฐ์ฐ์ด ์๋ค. ํนํ limit๋ findFirst์ฒ๋ผ ์์์ ์์์ ์์กดํ๋ ์ฐ์ฐ์ ๋ณ๋ ฌ ์คํธ๋ฆผ ์ํ ์ ๋น์ผ ๋น์ฉ์ ์น๋ฌ์ผ ํ๋ค.
์๋ฅผ ๋ค์ด firstAny๋ ์์์ ์์์ ์๊ด์์ด ์ฐ์ฐํ๋ฏ๋ก findFirst๋ณด๋ค ์ฑ๋ฅ์ด ์ข๋ค. ์ ๋ ฌ๋ ์คํธ๋ฆผ์ unordered๋ฅผ ํธ์ถํ๋ฉด ๋น ์ ๋ ฌ๋ ์คํธ๋ฆผ์ ์ป์ ์ ์๋ค. ์คํธ๋ฆผ์ N๊ฐ์ ์์๊ฐ ์์ ๋ ์์์ ์์๊ฐ ์๊ด์๋ค๋ฉด ๋น์ ๋ ฌ๋ ์คํธ๋ฆผ์ limit์ ํธ์ถํ๋ ๊ฒ์ด ๋ ํจ์จ์ ์ด๋ค.
์คํธ๋ฆผ์์ ์ํํ๋ ์ ์ฒด ํ์ดํ๋ผ์ธ ์ฐ์ฐ ๋น์ฉ์ ๊ณ ๋ คํ๋ผ.
์ฒ๋ฆฌํด์ผ ํ ์์ ์๊ฐ N์ด๊ณ ํ๋์ ์์๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ ๋๋ ๋น์ฉ์ด Q๋ผํ๋ฉด N*Q๋ก ์์ํ ์ ์๋ค. Q๊ฐ ๋๋ค๋ ๊ฒ์ ๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ์ฑ๋ฅ์ ๊ฐ์ ํ ์ ์๋ ๊ฐ๋ฅ์ฑ์ด ์์์ ์๋ฏธํ๋ค.
์๋์ ๋ฐ์ดํฐ์์๋ ๋ณ๋ ฌ ์คํธ๋ฆผ์ด ๋์ ๋์ง ์๋๋ค.
๋ณ๋ ฌํ ๊ณผ์ ์์ ์๊ธฐ๋ ๋ถ๊ฐ ๋น์ฉ์ ์์ํ ๋งํผ์ ์ด๋์ ์ป์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ด๋ค.
์คํธ๋ฆผ์ ๊ตฌ์ฑํ๋ ์๋ฃ๊ตฌ์กฐ๊ฐ ์ ์ ํ์ง ํ์ธํด๋ผ
์๋ฅผ ๋ค์ด ArrayList๋ฅผ LinkedList๋ณด๋ค ํจ์จ์ ์ผ๋ก ๋ถํ ํ ์ ์๋ค. LinkedList๋ฅผ ๋ถํ ํ๋ ค๋ฉด ๋ชจ๋ ์์๋ฅผ ํ์ํด์ผ ํ์ง๋ง ArrayList๋ ์์๋ฅผ ํ์ํ์ง ์๊ณ ๋ ๋ฆฌ์คํธ๋ฅผ ๋ถํ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ํ range ํฉํ ๋ฆฌ ๋ฉ์๋๋ก ๋ง๋ ๊ธฐ๋ณธํ ์คํธ๋ฆผ๋ ์ฝ๊ฒ ๋ถํดํ ์ ์๋ค.
์คํธ๋ฆผ์ ํน์ฑ๊ณผ ํ์ดํ๋ผ์ธ์ ์ค๊ฐ ์ฐ์ฐ์ด ์คํธ๋ฆผ์ ํน์ฑ์ ์ด๋ป๊ฒ ๋ฐ๊พธ๋์ง์ ๋ฐ๋ผ ๋ถํด ๊ณผ์ ์ ์ฑ๋ฅ์ด ๋ฌ๋ผ์ง ์ ์๋ค.
์๋ฅผ ๋ค์ด SIZED ์คํธ๋ฆผ์ ์ ํํ ๊ฐ์ ํฌ๊ธฐ์ ๋ ์คํธ๋ฆผ์ผ๋ก ๋ถํ ํ ์ ์์ผ๋ฏ๋ก ํจ๊ณผ์ ์ผ๋ก ์คํธ๋ฆผ์ ๋ณ๋ ฌ์ฒ๋ฆฌ ํ ์ ์๋ค. ํ์ง๋ง ํํฐ ์ฐ์ฐ์ด ์์ผ๋ฉด ์คํธ๋ฆผ์ ๊ธธ์ด๋ฅผ ์์ธกํ ์ ์์ผ๋ฏ๋ก ํจ๊ณผ์ ์ธ ์คํธ๋ฆผ์ ๋ณ๋ ฌ ์ฒ๋ฆฌํ ์ ์์์ง ์ ์ ์๊ฒ ๋๋ค.
์ต์ข ์ฐ์ฐ์ ๋ณํฉ ๊ณผ์ ๋น์ฉ์ ์ดํด๋ณด๋ผ.
๋ณํฉ ๊ณผ์ ์ ๋น์ฉ์ด ๋น์ธ๋ค๋ฉด ๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ์ป์ ์ฑ๋ฅ์ ์ด์ต์ด ์๋ธ ์คํธ๋ฆผ์ ๋ถ๋ถ ๊ฒฐ๊ณผ๋ฅผ ํฉ์น๋ ๊ณผ์ ์์ ์์๋ ์ ์๋ค.
์์ค | ๋ถํด์ฑ |
---|---|
ArrayList | ํ๋ฅญํจ |
LinkedList | ๋์จ |
IntStream.range | ํ๋ฅญํจ |
Stream.iterate | ๋์จ |
HashSet | ์ข์ |
TreeSet | ์ข์ |
7.2 ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ
โ ๋ณ๋ ฌํํ ์ ์๋ ์์ ์ ์ฌ๊ท์ ์ผ๋ก ์์ ์์ ์ผ๋ก ๋ถํ ํ ๋ค์์ ์๋ธํ์คํฌ ๊ฐ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ํฉ์ณ์ ์ ์ฒด ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค๋๋ก ์ค๊ณ๋์๋ค.
ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ์์๋ ์๋ธํ์คํฌ๋ฅผ ์ค๋ ๋ ํ(ForkJoinPool)์ ์์ ์ ์ค๋ ๋์ ๋ถ์ฐ ํ ๋นํ๋ ExecutorService ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ค.
7.2.1 Recursive Task ํ์ฉ
์ค๋ ๋ ํ์ ์ด์ฉํ๋ ค๋ฉด **RecursiveTask
R : ์ ๋ณ๋ ฌํ๋ ํ์คํฌ๊ฐ ์์ฑํ๋ ๊ฒฐ๊ณผ ํ์ ๋๋ ๊ฒฐ๊ณผ๊ฐ ์์ ๋ RecursiveAction ํ์
RecursiveTask๋ฅผ ์ ์ํ๋ ค๋ฉด ์ถ์ ๋ฉ์๋ compute๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
1
protected abstract R compute();
- compute ๋ฉ์๋
- ํ์คํฌ๋ฅผ ์๋ธํ์คํฌ๋ก ๋ถํ ํ๋ ๋ก์ง ์ ์
- ๋ ์ด์ ๋ถํ ํ ์ ์์ ๋ ๊ฐ๋ณ ์๋ธํ์คํฌ์ ๊ฒฐ๊ณผ๋ฅผ ์์ฐํ ์๊ณ ๋ฆฌ์ฆ ์ ์
๋ฐ๋ผ์ ๋๋ถ๋ถ์ compute ๋ฉ์๋ ๊ตฌํ์ ๋ค์๊ณผ ๊ฐ์ ์์ฌ์ฝ๋ ํ์์ ์ ์งํ๋ค.
1
2
3
4
5
6
7
8
if (ํ์คํฌ๊ฐ ์ถฉ๋ถํ ์๊ฑฐ๋ ๋ ์ด์ ๋ถํ ํ ์ ์์ผ๋ฉด) {
์์ฐจ์ ์ผ๋ก ํ์คํฌ ๊ณ์ฐ
} else {
ํ์คํฌ๋ฅผ ๋ ์๋ธํ์คํฌ๋ก ๋ถํ
ํ์คํฌ๊ฐ ๋ค์ ์๋ธํ์คํฌ๋ก ๋ถํ ๋๋๋ก ์ด ๋ฉ์๋๋ฅผ ์ฌ๊ท์ ์ผ๋ก ํธ์ถํจ
๋ชจ๋ ์๋ธํ์คํฌ์ ์ฐ์ฐ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆผ
๊ฐ ์๋ธํ์คํฌ์ ๊ฒฐ๊ณผ๋ฅผ ํฉ์นจ
}
์ด ์๊ณ ๋ฆฌ์ฆ์ ๋ถํ ํ ์ ๋ณต(divide-and-conquer) ์๊ณ ๋ฆฌ์ฆ์ ๋ณ๋ ฌํ ๊ณผ์ ์ด๋ค. ๋ค์ ์์ ์ ForkJoinSumCalculator ์ฝ๋์์ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ฒ๋ผ ๋จผ์ RecursiveTask๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// RecursiveTask๋ฅผ ์์๋ฐ์ ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ์์ ์ฌ์ฉํ ํ์คํฌ ์์ฑ
public class ForkJoinSumCalculator
extends java.util.concurrent.RecursiveTast<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);
// ForkJoinPool์ ๋ค๋ฅธ ์ค๋ ๋๋ก ์๋ก ์์ฑํ ํ์คํฌ๋ฅผ ๋น๋๊ธฐ๋ก ์คํ
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;
}
}
์ ๋ฉ์๋๋ n๊น์ง์ ์์ฐ์ ๋ง์ ์์ ์ ๋ณ๋ ฌ๋ก ์ํํ๋ ๋ฐฉ๋ฒ์ ๋ ์ง๊ด์ ์ผ๋ก ๋ณด์ฌ์ค๋ค. ForkJoinSumCalculator์ ์์ฑ์๋ก ์ํ๋ ์์ ๋ฐฐ์ด์ ๋๊ฒจ์ฃผ๋ฉด ๋๋ค.
1
2
3
4
5
public static long forkJoinSum(long n) {
long[] numbers = LongStream.rangeClosed(1, n).toArray();
ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
return new ForkJoinPool().invoke(task);
}
ForkJoinPool์ invoke ๋ฉ์๋๋ก ์์ฑํ ํ์คํฌ๋ฅผ ์ ๋ฌํ๋ค. ForkJoinPool์์ ์ซํ๋๋ ๋ง์ง๋ง invoke ๋ฉ์๋์ ๋ฐํ๊ฐ์ ForkJoinSumCalculator์์ ์ ์ํ ํ์คํฌ์ ๊ฒฐ๊ณผ๊ฐ ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ForkJoinPool๋ฅผ ํ์ํ ๊ณณ์์ ์ธ์ ๋ ๊ฐ์ ธ๋ค ์ธ ์ ์๋๋ก ํ ๋ฒ๋ง ์ธ์คํด์คํ ํด์ ์ ์ ํ๋์ ์ฑ๊ธํด์ผ๋ก ์ ์ฅํ๋ค. (ForkJoinPool์ ๋ง๋ค๋ฉด์ ์ธ์๊ฐ ์๋ ๋ํดํธ ์์ฑ์ ์ด์ฉ)
ForkJoinSumCalculator ์คํ
- ForkJoinSumCalculator๋ฅผ ForkJoinPool๋ก ์ ๋ฌ
- ํ์ ์ค๋ ๋๊ฐ ForkJoinSumCalculator์ compute ๋ฉ์๋๋ฅผ ์คํํ๋ฉด์ ์์ ์ํ
- compute ๋ฉ์๋๋ ๋ณ๋ ฌ๋ก ์คํํ ์ ์์๋งํผ ํ์คํฌ์ ํฌ๊ธฐ๊ฐ ์ถฉ๋ถํ ์์์ก๋์ง ํ์ธ
- ์์ง ํ์คํฌ์ ํฌ๊ธฐ๊ฐ ํฌ๋ค๊ณ ํ๋จ๋๋ฉด ์ซ์ ๋ฐฐ์ด์ ๋ฐ์ผ๋ก ๋ถํ ํด์ ๋ ๊ฐ์ ์๋ก์ด ForkJoinSumCalculator๋ก ํ ๋น
- ๋ค์ ForkJoinPool์ด ์๋ก ์์ฑ๋ ForkJoinSumCalculator๋ฅผ ์คํ
์ ๊ณผ์ ์ ์ฌ๊ท์ ์ผ๋ก ๋ฐ๋ณตํ๋ฉด์ ํ์คํฌ ๋ถํ ์ ๋ฐ๋ณตํ๋ค.
์ด์ ๊ฐ ์๋ธํ์คํฌ๋ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌ๋๋ฉฐ ํฌํน ํ๋ก์ธ์ค๋ก ๋ง๋ค์ด์ง ์ด์งํธ๋ฆฌ์ ํ์คํฌ๋ฅผ ๋ฃจํธ์์ ์ญ์์ผ๋ก ๋ฐฉ๋ฌธํ๋ค.
์ฆ, ๊ฐ ์๋ธํ์คํฌ์ ๋ถ๋ถ ๊ฒฐ๊ณผ๋ฅผ ํฉ์ณ ํ์คํฌ์ ์ต์ข ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ฐํ๋ค.
ํ๋์ค๋ก ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ์ ํฉ๊ณ ๋ฉ์๋ ์ฑ๋ฅ ํ์ธ
1
2
System.out.println("ForkJoin sum done in: " + measureSumPerf(
ForkJoinSumCalcouator::forkJoinSum, 10_000_000) + " msecs");
๋ค์์ ์ถ๋ ฅ ๊ฒฐ๊ณผ๋ค
1
ForkJoin sum done in: 41 msecs
๋ณ๋ ฌ ์คํธ๋ฆผ์ ์ด์ฉํ ๋๋ณด๋ค ์ฑ๋ฅ์ด ๋๋น ์ก๋ค. ํ์ง๋ง ์ด๋ ForkJoinSumCalculator ํ์คํฌ์์ ์ฌ์ฉํ ์ ์๋๋ก ์ ์ฒด ์คํธ๋ฆผ์ long[]์ผ๋ก ๋ณํํ๊ธฐ ๋๋ฌธ์ด๋ค.
7.2.2 ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ๋ฅผ ์ ๋๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
๋ ์๋ธํ์คํฌ๊ฐ ๋ชจ๋ ์์๋ ๋ค์์ join์ ํธ์ถํ๋ค
โ join ๋งค์๋๋ฅผ ํ์คํฌ์ ํธ์ถํ๋ฉด ํ์คํฌ๊ฐ ์์ฐํ๋ ๊ฒฐ๊ณผ๊ฐ ์ค๋น๋ ๋๊น์ง ํธ์ถ์๋ฅผ ๋ธ๋ก์ํค๊ธฐ ๋๋ฌธ
๊ทธ๋ ์ง ์์ผ๋ฉด ๊ฐ๊ฐ์ ์๋ธํ์คํฌ๊ฐ ๋ค๋ฅธ ํ์คํฌ๊ฐ ๋๋๊ธธ ๊ธฐ๋ค๋ฆฌ๋ ์ผ์ด ๋ฐ์ํ๋ฉฐ ์๋ ์์ฐจ ์๊ณ ๋ฆฌ์ฆ๋ณด๋ค ๋๋ฆฌ๊ณ ๋ณต์กํ ํ๋ก๊ทธ๋จ์ด ๋์ด๋ฒ๋ฆด ์ ์๋ค.
RecursiveTask ๋ด์์๋ ForkJoinPool์ invoke ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ๋ง์์ผ ํ๋ค.
โ ์์ฐจ ์ฝ๋์์ ๋ณ๋ ฌ ๊ณ์ฐ์ ์์ํ ๋๋ง invoke๋ฅผ ์ฌ์ฉํ๋ค.
์๋ธํ์คํฌ์ fork ๋ฉ์๋๋ฅผ ํธ์ถํด์ ForkJoinPool์ ์ผ์ ์ ์กฐ์ ํ ์ ์๋ค.
์ด๋ ์ผ์ชฝ ์์ ์ค๋ฅธ์ชฝ ์์ ๋ชจ๋์ fork ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ๋ณด๋ค ํ์ชฝ ์์ ์๋ compute๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ํจ์จ์ ์ด๋ค.
๊ทธ๋ฌ๋ฉด ๋ ์๋ธ ํ์คํฌ์ ํ ํ์คํฌ์์ ๊ฐ์ ์ค๋ ๋๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก ๋ถํ์ํ ํ์คํฌ๋ฅผ ํ ๋นํ๋ ์ค๋ฒํค๋๋ฅผ ํผํ ์ ์๋ค.
ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ๋ฅผ ์ด์ฉํ๋ ๋ณ๋ ฌ ๊ณ์ฐ์ ๋๋ฒ๊น ํ๊ธฐ ์ด๋ ต๋ค.
๋ณดํต IDE๋ก ๋๋ฒ๊น ํ ๋ ์คํ ํธ๋ ์ด์ค๋ก ๋ฌธ์ ๊ฐ ์ผ์ด๋ ๊ณผ์ ์ ์ฝ๊ฒ ํ์ธํ ์ ์๋๋ฐ, ํฌํฌ/์กฐ์ธ ํ๋ ์์ํฌ์์ fork๋ผ ๋ถ๋ฆฌ๋ ๋ค๋ฅธ ์ค๋ ๋์์ compute๋ฅผ ํธ์ถํ๋ฏ๋ก ์คํ ํธ๋ ์ด์ค๊ฐ ๋์์ด ๋์ง ์๋๋ค.
7.2.3 ์์ ํ์น๊ธฐ
๋๋ถ๋ถ์ ๊ธฐ๊ธฐ์ ์ฝ์ด๊ฐ 4๊ฐ ๋ฟ์ด๋ฏ๋ก ์ฒ ๊ฐ ์ด์์ ์๋ธํ์คํฌ๋ ์์๋ง ๋ญ๋นํ๋ ๊ฒ ์ฒ๋ผ ๋ณด์ธ๋ค.
ํ์ง๋ง ์ค์ ๋ก๋ ์ฝ์ด ๊ฐ์์ ๊ด๊ณ์์ด ์ ์ ํ ํฌ๊ธฐ๋ก ๋ถํ ๋ ๋ง์ ํ์คํฌ๋ฅผ ํฌํนํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค. ์ด๋ก ์ ์ผ๋ก๋ ์ฝ์ด ๊ฐ์๋งํผ ๋ณ๋ ฌํ๋ ํ์คํฌ๋ก ์์ ๋ถํ๋ฅผ ๋ถํ ํ๋ฉด ๋ชจ๋ CPU ์ฝ์ด์์ ํ์คํฌ๋ฅผ ์คํํ ๊ฒ์ด๊ณ ํฌ๊ธฐ๊ฐ ๊ฐ์ ๊ฐ๊ฐ์ ํ์คํฌ๋ ๊ฐ์ ์๊ฐ์ ์ข ๋ฃ๋ ๊ฒ์ด๋ผ ์๊ฐํ ์ ์๋ค.
ํ์ง๋ง ํ์ค์ ๊ฐ๊ฐ์ ์๋ธํ์คํฌ์ ์์ ์๋ฃ ์๊ฐ์ด ํฌ๊ฒ ๋ฌ๋ผ์ง ์ ์๋ค. ๋ถํ ๊ธฐ๋ฒ์ด ํจ์จ์ ์ด์ง ์์๊ธฐ ๋๋ฌธ์ผ ์๋ ์๊ณ ์๊ธฐ์น ์๊ฒ ๋์คํฌ ์ ๊ทผ ์๋๊ฐ ์ ํ๋์๊ฑฐ๋ ์ธ๋ถ ์๋น์ค์ ํ๋ ฅํ๋ ๊ณผ์ ์์ ์ง์ฐ์ด ์๊ธธ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์์ ํ์น๊ธฐ
ForkJoinPool์ ๋ชจ๋ ์ค๋ ๋๋ฅผ ๊ฑฐ์ ๊ณต์ ํ๊ฒ ๋ถํ ํ๋ค. ๊ฐ๊ฐ์ ์ค๋ ๋๋ ์์ ์๊ฒ ํ ๋น๋ ํ์คํฌ๋ฅผ ํฌํจํ๋ ์ด์ค ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ฅผ ์ฐธ์กฐํ๋ฉด์ ์์ ์ด ๋๋ ๋๋ง๋ค ํ์ ํค๋์์ ๋ค๋ฅธ ํ์คํฌ๋ฅผ ๊ฐ์ ธ์์ ์์ ์ ์ฒ๋ฆฌํ๋ค.
ํ ์ค๋ ๋์ ํ ์ผ์ด ๋ค ๋ ๋ฌ์ง ์ํ์์ ํด๋น ์ค๋ ๋์ ์์ ์ด ๋ฉ์ถ๋ ๊ฒ์ด ์๋๋ผ ๋ค๋ฅธ ์ค๋ ๋ ํ์ ๊ผฌ๋ฆฌ์์ ์์ ์ ํ์ณ์จ๋ค. ๋ชจ๋ ํ์คํฌ๊ฐ ์์ ์ ๋๋ ๋๊น์ง, ์ฆ ๋ชจ๋ ํ๊ฐ ๋น ๋๊น์ง ์ด ๊ณผ์ ์ ๋ฐ๋ณตํ๋ค.
๋ฐ๋ผ์ ํ์คํฌ์ ํฌ๊ธฐ๋ฅผ ์๊ฒ ๋๋์ด์ผ ์์ ์ ์ค๋ ๋ ๊ฐ์ ์์ ๋ถํ๋ฅผ ๋น์ทํ ์์ค์ผ๋ก ์ ์งํ ์ ์๋ค.
7.3 Spliterator ์ธํฐํ์ด์ค
์๋ฐ 8์ Spliterator๋ผ๋ ์๋ก์ด ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค. ์ปฌ๋ ์ ํ๋ ์์ํฌ์ ํฌํจ๋ ๋ชจ๋ ์๋ฃ๊ตฌ์กฐ์ ์ฌ์ฉํ ์ ์๋ ๋ํดํธ Spliterator ๊ตฌํ์ ์ ๊ณตํ๋ค.
Spliterator: ๋ถํ ํ ์ ์๋ ๋ฐ๋ณต์
Iterator์ฒ๋ผ Spliterator๋ ์์ค์ ์์ ํ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค๋ ์ ์ ๊ฐ์ง๋ง Spliterator๋ ๋ณ๋ ฌ ์์ ์ ํนํ๋์ด ์๋ค.
- Spliterator ์ธํฐํ์ด์ค
1
2
3
4
5
6
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
long estimateSize();
int characteristics();
}
์ฌ๊ธฐ์ T๋ Spliterator์์ ํ์ํ๋ ์์์ ํ์์ ๊ฐ๋ฆฌํจ๋ค.
tryAdvance
Spliterator์ ์์๋ฅผ ํ๋์ฉ ์์ฐจ์ ์ผ๋ก ์๋นํ๋ฉด์ ํ์ํด์ผ ํ ์์๊ฐ ๋จ์์์ผ๋ฉด ์ฐธ์ ๋ฐํํ๋ค. (์ฆ, ์ผ๋ฐ์ ์ธ Iterator ๋์๊ณผ ๊ฐ๋ค)
trySplit
Spliterator์ ์ผ๋ถ ์์(์์ ์ด ๋ฐํํ ์์)๋ฅผ ๋ถํ ํด์ ๋ ๋ฒ์งธ Spliterator๋ฅผ ์์ฑํ๋ ๋ฉ์๋๋ค.
estimateSize
Spliterator์์ ํ์ํด์ผ ํ ์์ ์ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค.
ํ์ํด์ผํ ์์ ์๊ฐ ์ ํํ์ง ์๋๋ผ๋ ์ ๊ณต๋ ๊ฐ์ ์ด์ฉํด์ ๋ ์ฝ๊ณ ๊ณตํํ๊ฒ Spliterator๋ฅผ ๋ถํ ํ ์ ์๋ค.
7.3.1 ๋ถํ ๊ณผ์
์ ๊ทธ๋ฆผ์ฒ๋ผ ์คํธ๋ฆผ ๋ถํ ๊ณผ์ ์ ์ฌ๊ท์ ์ผ๋ก ์ผ์ด๋๋ค.
- 1๋จ๊ณ - ์ฒซ ๋ฒ์งธ Spliterator์์ trySplit์ ํธ์ถํ๋ฉด ๋ ๋ฒ์งธ Spliterator๊ฐ ์์ฑ๋๋ค.
- 2๋จ๊ณ - ๋ ๊ฐ์ Spliterator์ trySplit์ ๋ค์ ํธ์ถํ๋ฉด ๋ค ๊ฐ์ Spliterator๊ฐ ์์ฑ๋๋ค.
์ด์ฒ๋ผ trySplit์ ๊ฒฐ๊ณผ๊ฐ null(๋ ์ด์ ์๋ฃ๊ตฌ์กฐ ๋ถํ ๋ชปํจ)์ด ๋ ๋๊น์ง ์ด ๊ณผ์ ์ ๋ฐ๋ณตํ๋ค.
- 4๋จ๊ณ - ๋ชจ๋ trySplit์ ๊ฒฐ๊ณผ๊ฐ null์ด๋ฉด ์ฌ๊ท ๋ถํ ๊ณผ์ ์ข ๋ฃ
์ด ๋ถํ ๊ณผ์ ์ characteristics ๋ฉ์๋๋ก ์ ์ํ๋ Spliterator์ ํน์ฑ์ ์ํฅ์ ๋ฐ๋๋ค.
Spliterator ํน์ฑ
- characteristics
- Spliterator ์์ฒด์ ํน์ฑ ์งํฉ์ ํฌํจํ๋ int ๋ฐํ
- ์ด ํน์ฑ์ ์ฐธ๊ณ ํด์ Spliterator๋ฅผ ๋ ์ ์ ์ดํ๊ณ ์ต์ ํ ๊ฐ๋ฅ
7.3.2 ์ปค์คํ Spliterator ๊ตฌํํ๊ธฐ
- ๋ฐ๋ณตํ์ผ๋ก ๋จ์ด ์๋ฅผ ์ธ๋ ๋ฉ์๋
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}
ํจ์ํ์ผ๋ก ๋จ์ด ์๋ฅผ ์ธ๋ ๋ฉ์๋ ์ฌ๊ตฌํํ๊ธฐ
์ฐ์ String์ ์คํธ๋ฆผ์ผ๋ก ๋ณํํด์ผ ํ๋ค. ์คํธ๋ฆผ์ int, long, double ๊ธฐ๋ณธํ๋ง ์ ๊ณตํ๋ฏ๋ก Stream
1
2
Stream<Character> stream = IntStream.range(0, SENTENCE.length())
.mapToObj(SENTENCE::charAt);
์คํธ๋ฆผ์ ๋ฆฌ๋์ฑ ์ฐ์ฐ์ ์คํํ๋ฉด์ ๋จ์ด ์๋ฅผ ๊ณ์ฐํ ์ ์๋ค.
- ๋ฌธ์์ด ์คํธ๋ฆผ์ ํ์ํ๋ฉด์ ๋จ์ด ์๋ฅผ ์ธ๋ ํด๋์ค
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}
}
public WordCounter combine(WordCounter wordCounter) {
return new WordCounter(counter + wordCounter.counter, wordCounter.lastSpace);
}
public int getCounter() {
return counter;
}
}
- accumulate ๋ฉ์๋
- WordCounter์ ์ํ๋ฅผ ์ด๋ป๊ฒ ๋ฐ๊ฟ ๊ฒ์ธ์ง, ๋๋ ์๋ฐํ WordCounter๋ ๋ถ๋ณ ํด๋์ค์ด๋ฏ๋ก ์๋ก์ด WordCounter ํด๋์ค๋ฅผ ์ด๋ค ์ํ๋ก ์์ฑํ ๊ฒ์ธ์ง ์ ์
- ์คํธ๋ฆผ์ ํ์ํ๋ฉด์ ์๋ก์ด ๋ฌธ์๋ฅผ ์ฐพ์ ๋๋ง๋ค accumulate ๋ฉ์๋๋ฅผ ํธ์ถ
- ์์ ๋ฐ๋ณตํ ๋ฉ์๋ countWordsIteratively์์์ฒ๋ผ ์๋ก์ด ๋น๊ณต๋ฐฑ ๋ฌธ์๋ฅผ ํ์ํ ๋ค์ ๋ง์ง๋ง ๋ฌธ์๊ฐ ๊ณต๋ฐฑ์ด๋ฉด counter๋ฅผ ์ฆ๊ฐ์ํจ๋ค.
- combine ๋ฉ์๋
- ๋ฌธ์์ด ์๋ธ์คํธ๋ฆผ์ผ๋ก ์ฒ๋ฆฌํ WordCounter์ ๊ฒฐ๊ณผ ํฉ์นจ
- ๋ฆฌ๋์ฑ ์ฐ์ฐ ๊ตฌํ
1
2
3
4
5
6
private int countWords(Stream<Character> stream) {
WordCounter wordCounter = stream.reduce(new WordCounter(0, true),
WordCounter::accumulate,
WordCounter::combine);
return wordCounter.getCounter();
}
- ๋ฉ์๋ ํธ์ถ
1
2
3
Stream<Character> stream = IntStream.range(0, SENTENCE.length())
.mapToObj(SENTENCE::charAt);
System.out.println("Found " + countWords(stream) + " words");
WordCounter ๋ณ๋ ฌ๋ก ์ํํ๊ธฐ
1
System.out.println("Found " + countWords(stream.parallel()) + " words");
๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ ํ์ง๋ง ๊ฒฐ๊ณผ๊ฐ ์ด์ํ๊ฒ ๋์จ๋ค.
์๋ ๋ฌธ์์ด์ ์์์ ์์น์์ ๋๋ก ๋๋๋ค๋ณด๋ ์์์น ๋ชปํ๊ฒ ํ๋์ ๋จ์ด๋ฅผ ๋๋ก ๊ณ์ฐํ๋ ์ํฉ์ด ๋ฐ์ํ ์ ์๋ค. ์์ฐจ ์คํธ๋ฆผ์ ๋ณ๋ ฌ ์คํธ๋ฆผ์ผ๋ก ๋ฐ๊ฟ ๋ ์คํธ๋ฆผ ๋ถํ ์์น์ ๋ฐ๋ผ ์๋ชป๋ ๊ฒฐ๊ณผ๊ฐ ๋์ฌ ์ ์๋ค.
โ ๋ฌธ์์ด์ ์์์ ์์น์์ ๋ถํ ํ์ง ๋ง๊ณ ๋จ์ด๊ฐ ๋๋๋ ์์น์์ ๋ถํ ํ๋ฉด ๋๋ค. ๋จ์ด ๋์์ ๋ฌธ์์ด์ ๋ถํ ํ๋ ๋ฌธ์ Spliterator๊ฐ ํ์ํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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(); // ์๋นํ ๋ฌธ์๊ฐ ๋จ์์์ผ๋ฉด true ๋ฐํ
}
@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;
}
}