πŸ“‚ JAVA/μ΄νŽ™ν‹°λΈŒ μžλ°”

슀트림 λ³‘λ ¬ν™”λŠ” μ£Όμ˜ν•΄μ„œ μ μš©ν•˜λΌ - [7μž₯. λžŒλ‹€μ™€ 슀트림(μ•„μ΄ν…œ48)]

Amenable 2023. 12. 10. 11:45

πŸ“™ 1. μžλ°”μ˜ λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°

  μžλ°”λŠ” λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ° μΈ‘λ©΄μ—μ„œ 항상 μ•žμ„œκ°”λ‹€.

  • 처음 릴리슀된 1996λ…„
    μŠ€λ ˆλ“œ, 동기화, wait/notify 지원
  • μžλ°” 5
    λ™μ‹œμ„± μ»¬λ ‰μ…˜μΈ java.util.concurrent  λΌμ΄λΈŒλŸ¬λ¦¬μ™€ μ‹€ν–‰μž(Executor) ν”„λ ˆμž„μ›Œν¬ 지원
  • μžλ°” 7
    κ³ μ„±λŠ₯ 병렬 λΆ„ν•΄(parallel decom-position) ν”„λ ˆμž„μ›Œν¬μΈ 포크-쑰인(fork-join) νŒ¨ν‚€μ§€ μΆ”κ°€
  • μžλ°” 8
    parallel λ©”μ„œλ“œλ§Œ ν•œ 번 ν˜ΈμΆœν•˜λ©΄ νŒŒμ΄ν”„λΌμΈμ„ 병렬 μ‹€ν–‰ν•  수 μžˆλŠ” 슀트림 지원

  이처럼 μžλ°”λ‘œ λ™μ‹œμ„± ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜κΈ°κ°€ 점점 μ‰¬μ›Œμ§€κ³  μžˆλ‹€. ν•˜μ§€λ§Œ, λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°μ„ ν•  λ•ŒλŠ” μ•ˆμ „μ„±(safety)κ³Ό 응닡 κ°€λŠ₯(liveness) μƒνƒœλ₯Ό μœ μ§€ν•˜κΈ° μœ„ν•΄ 애써야 ν•œλ‹€. 병렬 μŠ€νŠΈλ¦Ό νŒŒμ΄ν”„라인 ν”„λ‘œκ·Έλž˜λ°μ—μ„œλ„ λ§ˆμ°¬κ°€μ§€λ‹€.

 

πŸ“™ 2. 성곡적인 슀트림 νŒŒμ΄ν”„λΌμΈ 병렬화

  슀트림 νŒŒμ΄ν”„λΌμΈ λ³‘λ ¬ν™”μ˜ 효과λ₯Ό λ¨Όμ € μ•Œμ•„λ³΄μž.

  λ‹€μŒκ³Ό 같이 n보닀 μž‘κ±°λ‚˜ 같은 μ†Œμˆ˜μ˜ 개수λ₯Ό κ³„μ‚°ν•˜λŠ” ν•¨μˆ˜κ°€ μžˆλ‹€κ³  ν•΄λ³΄μž. 병렬화λ₯Ό ν•˜μ§€ μ•Šμ€ κ²½μš°μ™€ 병렬화λ₯Ό ν•œ κ²½μš°λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

// 병렬화λ₯Ό ν•˜μ§€ μ•Šμ€ 경우
static long pi(long n) {
    return LongStream.rangeClosed(2, n)
            .mapToObj(BigInteger::valueOf)
            .filter(i -> i.isProbablePrime(50))
            .count();
}

// 병렬화λ₯Ό ν•œ 경우
static long pi(long n) {
    return LongStream.rangeClosed(2, n)
            .parallel()
            .mapToObj(BigInteger::valueOf)
            .filter(i -> i.isProbablePrime(50))
            .count();
}

  ν•΄λ‹Ή μ½”λ“œλ₯Ό μ΄μš©ν•˜μ—¬ 10^7(10_000_000)을 κ³„μ‚°ν•˜μ˜€μ„ λ•Œ, 병렬화 μ „μ—λŠ” 26μ΄ˆκ°€ κ±Έλ Έκ³  병렬화 ν›„μ—λŠ” 10μ΄ˆκ°€ κ±Έλ Έλ‹€.

즉, λ³‘λ ¬ν™”λ₯Ό ν†΅ν•΄ 2.6배의 μ„±λŠ₯ ν–₯상을 λ³Έ κ²ƒμ΄λ‹€.

 

πŸ“™ 3. 잘λͺ»λœ 슀트림 νŒŒμ΄ν”„λΌμΈ 병렬화

  λ‹€μŒμ€ 슀트림 νŒŒμ΄ν”„λΌμΈ 병렬화λ₯Ό 잘 λͺ» μ‚¬μš©ν•œ 예λ₯Ό μ‚΄νŽ΄λ³΄μž.

  μ˜ˆμ‹œλŠ” μ•„μ΄ν…œ 45(링크)μ—μ„œ λ‹€λ£¨μ—ˆλ˜ λ©”λ₯΄μ„Ό μ†Œμˆ˜λ₯Ό μƒμ„±ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ΄λ‹€.

private static final int COUNT = 20;

public static void main(String[] args) {

    primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE))
            .filter(mersenne -> mersenne.isProbablePrime(50))
            .limit(COUNT)
            .forEach(System.out::println);
}

  이 μ½”λ“œλ„ 병렬화λ₯Ό ν•œ κ²½μš°κ°€ 병렬화λ₯Ό ν•˜κΈ° 전보닀 더 λΉ¨λΌμ‘Œμ„κΉŒ?

  μ•„λ‹ˆλ‹€. 병렬화λ₯Ό ν•˜κ²Œ λœλ‹€λ©΄ 이 경우 아무것도 좜λ ₯ν•˜μ§€ λͺ»ν•˜λ©΄μ„œ μ‘λ‹΅λΆˆκ°€(liveness) μƒνƒœκ°€ λœλ‹€. κ·Έ μ΄μœ λŠ” 슀트림 λΌμ΄λΈŒλŸ¬λ¦¬κ°€ 이 νŒŒμ΄ν”„λΌμΈμ„ λ³‘λ ¬ν™”ν•˜λŠ” 방법을 μ°Ύμ•„λ‚΄μ§€ λͺ»ν–ˆκΈ° λ•Œλ¬Έμ΄λ‹€.

  데이터 μ†ŒμŠ€κ°€ Stream.iterate κ±°λ‚˜ 쀑간 μ—°μ‚°μœΌλ‘œ limit을 μ“°λ©΄ νŒŒμ΄ν”„λΌμΈ λ³‘λ ¬ν™”λ‘œλŠ” μ„±λŠ₯ κ°œμ„ μ„ κΈ°λŒ€ν•  수 μ—†λ‹€. νŒŒμ΄ν”„λΌμΈ λ³‘λ ¬ν™”λŠ” limit을 λ‹€λ£° λ•Œ CPU μ½”μ–΄κ°€ λ‚¨λŠ”λ‹€λ©΄ μ›μ†Œλ₯Ό λͺ‡ 개 더 μ²˜λ¦¬ν•œ ν›„ μ œν•œλœ 개수 μ΄ν›„μ˜ κ²°κ³Όλ₯Ό 버렀도 μ•„λ¬΄λŸ° ν•΄κ°€ μ—†λ‹€κ³  κ°€μ •ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. λ‹€μŒ μ†Œμˆ˜λ₯Ό μ°ΎλŠ” λ° κ±Έλ¦¬λŠ” μ‹œκ°„이 μ΄μ „ μ†Œμˆ˜λ₯Ό μ°ΎλŠ” 데 κ±Έλ¦¬λŠ” μ‹œκ°„보닀 λ” λ§Žμ΄ κ±°λ¦¬κΈ° λ•Œλ¬Έμ— λ³‘λ ¬ν™”κ°€ μ œ κΈ°λŠ₯을 λ°œνœ˜ν•˜μ§€ λͺ»ν•œ κ²ƒμ΄λ‹€.

 

πŸ“™ 4. 슀트림 νŒŒμ΄ν”„λΌμΈ 병렬화λ₯Ό μ μš©ν•˜λŠ” κΈ°μ€€

  λŒ€μ²΄λ‘œ 슀트림 μ†ŒμŠ€κ°€ ArrayList, HashMap, HashSet, ConcurrentHashMap의 μΈμŠ€ν„΄μŠ€ κ±°λ‚˜ λ°°μ—΄, int λ²”μœ„, long λ²”μœ„μΌ λ•Œ λ³‘λ ¬ν™”μ˜ νš¨κ³Όκ°€ κ°€μž₯ μ’‹λ‹€. 이 μžλ£Œκ΅¬μ‘°λ“€μ€ λͺ¨λ‘ 데이터λ₯Ό μ›ν•˜λŠ” 크기둜 μ •ν™•ν•˜κ³  μ†μ‰½κ²Œ λ‚˜λˆŒ 수 μžˆμ–΄μ„œ 일을 λ‹€μˆ˜μ˜ μŠ€λ ˆλ“œμ— λΆ„λ°°ν•˜κΈ° μ’‹λ‹€λŠ” νŠΉμ§•μ΄ μžˆλ‹€.

  λ˜ν•œ, 이 μžλ£Œκ΅¬μ‘°λ“€μ€ μ›μ†Œλ“€μ„ 순차적으둜 μ‹€ν–‰ν•  λ•Œ μ°Έμ‘° μ§€μ—­μ„±(locality of reference)이 λ›°μ–΄λ‚˜κΈ° λ•Œλ¬Έμ— λ‹€λŸ‰μ˜ 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” 벌크 연산을 병렬화할 λ•Œ 이점이 μžˆλ‹€.

 

  슀트림 νŒŒμ΄ν”„λΌμΈμ˜ 쒅단 μ—°μ‚°μ˜ λ™μž‘ 방식 μ—­μ‹œ 병렬 μˆ˜ν–‰ νš¨μœ¨μ— 영ν–₯을 μ€€λ‹€. 쒅단 μ—°μ‚°μ—μ„œ μˆ˜ν–‰ν•˜λŠ” μž‘μ—…λŸ‰μ΄ νŒŒμ΄ν”„λΌμΈ 전체 μž‘μ—…μ—μ„œ 상당 비쀑을 μ°¨μ§€ν•˜λ©΄μ„œ 순차적인 연산이라면 νŒŒμ΄ν”„λΌμΈ 병렬 μˆ˜ν–‰μ˜ νš¨κ³ΌλŠ” μ œν•œλ  μˆ˜λ°–μ— μ—†λ‹€.

  • νŒŒμ΄ν”„λΌμΈμ—μ„œ λ§Œλ“€μ–΄μ§„ λͺ¨λ“  μ›μ†Œλ₯Ό ν•˜λ‚˜λ‘œ ν•©μΉ˜λŠ” μž‘μ—…μΈ μΆ•μ†Œ(reduction)κ°€ 병렬화에 μ ν•©ν•œ 쒅단 연산이닀. (ex. min, max, count, sum)
  • 쑰건에 맞으면 λ°”λ‘œ λ°˜ν™˜λ˜λŠ” λ©”μ„œλ“œλ„ 병렬화에 μ ν•©ν•˜λ‹€. (ex. anyMatch, allMatch, noneMatch)
  • μ»¬λ ‰μ…˜μ„ ν•©μΉ˜λŠ” 뢀담이 큰 κ°€λ³€ μΆ•μ†Œ(mutable reduction)λŠ” 병렬화에 μ ν•©ν•˜μ§€ μ•Šλ‹€.

 

  μŠ€νŠΈλ¦Όμ„ 잘λͺ» λ³‘λ ¬ν™”ν•˜λ©΄ (응닡 λΆˆκ°€λ₯Ό 포함해) μ„±λŠ₯이 λ‚˜λΉ μ§ˆ 뿐만 μ•„λ‹ˆλΌ κ²°κ³Ό μžμ²΄κ°€ 잘λͺ»λ˜κ±°λ‚˜ μ˜ˆμƒ λͺ»ν•œ λ™μž‘μ΄ λ°œμƒν•  수 μžˆλ‹€. λ”°λΌμ„œ, 계산도 μ˜¬λ°”λ₯΄κ³  μ„±λŠ₯도 빨라질 κ±°λΌλŠ” ν™•μ‹  μ—†μ΄λŠ” 슀트림 νŒŒμ΄ν”„λΌμΈ λ³‘λ ¬ν™”λŠ” μ‹œλ„μ‘°μ°¨ ν•˜μ§€ 말자. μŠ€νŠΈλ¦Όμ„ 잘λͺ» λ³‘λ ¬ν™”ν•˜λ©΄ ν”„λ‘œκ·Έλž¨μ„ μ˜€λ™μž‘ν•˜κ²Œ ν•˜κ±°λ‚˜ μ„±λŠ₯을 κΈ‰κ²©νžˆ λ–¨μ–΄λœ¨λ¦°λ‹€.

  λ˜ν•œ, 병렬화λ₯Ό ν•˜λŠ” 게 λ‚«λ‹€κ³  νŒλ‹¨μ΄ 되면, 운영 μ‹œμŠ€ν…œκ³Ό ν‘μ‚¬ν•œ ν™˜κ²½μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜λ„λ‘ ν•˜μž.

 

 

ν•΄λ‹Ή 글은 Joshua Bloch λ‹˜μ˜ 'Effective Java 3/E'λ₯Ό μ°Έκ³ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.