Java Academy · 강의

병렬성이 도움이 되는 경우

작업량과 데이터 크기가 미치는 영향을 살펴봅니다

레슨 2/413개 단계

병렬성이 도움이 되는 경우은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

병렬성에는 비용이 듭니다

병렬 실행을 하면 데이터 분할, 작업 전달, 결과 병합에 따른 오버헤드가 추가됩니다. 절약되는 시간보다 이 비용이 작을 때만 이점이 있습니다.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long sum = IntStream.rangeClosed(1, 10_000_000)
            .parallel()
            .asLongStream()
            .sum();
        System.out.println(sum);
    }
}

요인 1: 데이터 크기(N)

큰 N은 병렬성의 고정 오버헤드를 분산시킵니다. 대략적으로 수만 개의 요소가 있어야 병렬 실행이 유용해지는 경우가 많습니다.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long count = IntStream.rangeClosed(1, 5_000_000)
            .parallel()
            .filter(n -> n % 7 == 0)
            .count();
        System.out.println(count);
    }
}

요인 2: 요소당 작업량(Q)

각 요소를 처리하는 비용인 Q가 중요합니다. 요소마다 비용이 큰 작업은 N이 더 작더라도 병렬성의 이점을 얻을 수 있습니다.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long primes = IntStream.rangeClosed(2, 200_000)
            .parallel()
            .filter(Main::isPrime)
            .count();
        System.out.println(primes);
    }

    static boolean isPrime(int n) {
        for (int i = 2; (long) i * i <= n; i++)
            if (n % i == 0) return false;
        return true;
    }
}

N 곱하기 Q로 생각하기

총 유용한 작업량을 N x Q로 생각해 보십시오. 이 곱이 클수록 병렬성이 더 큰 도움이 될 수 있습니다. N이 매우 작거나 Q가 사소한 경우에는 병렬 실행을 정당화하기 어렵습니다.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        double sum = IntStream.rangeClosed(1, 1_000_000)
            .parallel()
            .mapToDouble(n -> Math.sqrt(n) * Math.log(n + 1))
            .sum();
        System.out.println(sum);
    }
}

요인 3: 분할 가능성

저렴하고 균등하게 분할되는 데이터 원본은 병렬화가 잘 됩니다. 예로 배열, ArrayList, IntStream.range가 있습니다. LinkedList와 반복자 기반 원본은 분할이 잘 되지 않습니다.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1_000_000; i++) list.add(i);
        long even = list.parallelStream().filter(n -> n % 2 == 0).count();
        System.out.println(even);
    }
}

좋은 원본: 배열과 범위

기본형 범위는 크기가 알려져 있고 O(1)에 분할되므로 병렬 처리에 이상적인 원본입니다.

import java.util.stream.LongStream;

public class Main {
    public static void main(String[] args) {
        long sum = LongStream.rangeClosed(1, 20_000_000)
            .parallel()
            .sum();
        System.out.println(sum);
    }
}

좋지 않은 원본: 반복 생성기

Stream.iterate는 정의상 요소를 순차적으로 생성하므로 요소가 실제로 만들어지기 전에는 분할할 수 없습니다. 병렬 처리에 적합하지 않은 원본입니다.

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        long count = Stream.iterate(1, n -> n + 1)
            .limit(1_000_000)
            .parallel()
            .filter(n -> n % 2 == 0)
            .count();
        System.out.println(count);
    }
}

요인 4: 저렴한 병합 단계

병렬성에는 부분 결과를 결합하는 저렴한 방법이 필요합니다. sum과 count는 쉽게 병합되지만, 정렬된 목록이나 트리 구조가 많은 수집기를 만들려면 병합 비용이 큽니다.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int max = IntStream.rangeClosed(1, 5_000_000)
            .parallel()
            .map(n -> n % 1000)
            .max()
            .getAsInt();
        System.out.println(max);
    }
}

입출력 중심 작업에는 사용하지 않기

병렬 스트림은 포크-조인 풀에서 실행되는 CPU 중심 작업을 대상으로 합니다. 차단되는 입출력은 공유 풀의 자원을 고갈시켜 애플리케이션 전체에 악영향을 줍니다. 입출력에는 전용 실행기를 사용하십시오.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long total = IntStream.rangeClosed(1, 1_000_000)
            .parallel()
            .mapToLong(n -> (long) n * n)
            .sum();
        System.out.println(total);
    }
}

추측하지 말고 측정하기

병렬 실행이 도움이 되는지 알아보는 유일하게 신뢰할 수 있는 방법은 실제 상황에 가까운 데이터로 벤치마크를 수행하는 것입니다. 성능에 대한 직감은 자주 틀립니다.

import java.util.stream.LongStream;

public class Main {
    public static void main(String[] args) {
        long start = System.nanoTime();
        long sum = LongStream.rangeClosed(1, 50_000_000).parallel().sum();
        long ms = (System.nanoTime() - start) / 1_000_000;
        System.out.println("sum=" + sum + " took ~" + ms + "ms");
    }
}

실용적인 점검 목록

다음 조건이 모두 충족될 때 병렬 실행을 우선 고려하십시오:

  • 큰 N이거나 요소당 작업량이 많습니다.
  • 분할 가능한 원본(배열, ArrayList, 범위)입니다.
  • 저렴하고 결합법칙이 성립하는 병합입니다.
  • CPU 중심 작업이며 차단되는 입출력이 없습니다.
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        double avg = IntStream.rangeClosed(1, 10_000_000)
            .parallel()
            .mapToDouble(Math::sqrt)
            .average()
            .getAsDouble();
        System.out.println(avg);
    }
}

빠른 확인

어떤 상황이 병렬 스트림을 사용하기에 가장 적합할까요?

복습

병렬성이 도움이 되는 경우를 배웠습니다:

  • 이점은 N x Q(데이터 크기 곱하기 요소당 비용)가 커질수록 증가합니다.
  • 분할 가능한 원본(배열, ArrayList, 범위)이 필요합니다.
  • 저렴하고 결합법칙이 성립하는 병합이 필요합니다.
  • CPU 중심으로 유지하고 차단되는 입출력을 피하며 항상 측정해야 합니다.
무료로 시작

AI 튜터와 함께 Java을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
104
레슨
374

자주 묻는 질문

“병렬성이 도움이 되는 경우” 강의는 무료인가요?

네 — “병렬성이 도움이 되는 경우” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“병렬성이 도움이 되는 경우”에서 뭘 배우나요?

작업량과 데이터 크기가 미치는 영향을 살펴봅니다 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“병렬성이 도움이 되는 경우” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 병렬 스트림 생성
  2. 병렬성이 도움이 되는 경우
  3. 스레드 안전성과 부작용
  4. 흔한 함정
← Java Academy(으)로 돌아가기