0Pricing
Java Academy · Lekcja

Kiedy równoległość pomaga

Czynniki związane z obciążeniem i rozmiarem danych

Kiedy równoległość pomaga to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Równoległość ma swoją cenę

Wykonywanie równoległe dodaje narzut związany z dzieleniem danych, przydzielaniem zadań i scalaniem wyników. Opłaca się tylko wtedy, gdy ten koszt jest mniejszy od zaoszczędzonego czasu.

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);
    }
}

Czynnik 1: rozmiar danych (N)

Duża wartość N pozwala rozłożyć stały narzut równoległości. Przybliżona praktyczna zasada mówi, że równoległość zaczyna się opłacać przy dziesiątkach tysięcy elementów.

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);
    }
}

Czynnik 2: praca na element (Q)

Znaczenie ma koszt Q przetwarzania każdego elementu. Kosztowne operacje na elementach, wymagające intensywnych obliczeń, korzystają z równoległości nawet przy mniejszej wartości 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;
    }
}

Intuicja N razy Q

Całkowitą użyteczną pracę można rozumieć jako N x Q. Im większy jest ten iloczyn, tym bardziej równoległość może pomóc. Mała wartość N lub trywialna wartość Q rzadko uzasadnia jej użycie.

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);
    }
}

Czynnik 3: podatność na podział

Źródła danych, które można tanio i równomiernie dzielić, dobrze nadają się do przetwarzania równoległego: tablice, ArrayList i IntStream.range. LinkedList oraz źródła oparte na iteratorach dzielą się słabo.

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);
    }
}

Dobre źródła: tablice i zakresy

Zakresy typów prostych mają znany rozmiar i można je dzielić w czasie O(1), dzięki czemu są idealnymi źródłami dla przetwarzania równoległego.

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);
    }
}

Słabe źródło: generatory iteracyjne

Stream.iterate z definicji generuje elementy sekwencyjnie, więc nie można go podzielić, dopóki elementy nie zostaną utworzone. Jest słabym źródłem dla przetwarzania równoległego.

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);
    }
}

Czynnik 4: tanie scalanie

Równoległość wymaga niedrogiego sposobu łączenia częściowych wyników. sum i count można scalać w trywialny sposób, natomiast budowanie posortowanej listy lub kolektora opartego na złożonej strukturze drzewa jest kosztowne.

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);
    }
}

Należy unikać w przypadku operacji zależnych od wejścia-wyjścia

Strumienie równoległe są przeznaczone do zadań ograniczonych przez procesor i wykonywanych w puli fork-join. Blokujące operacje wejścia-wyjścia wyczerpują współdzieloną pulę i szkodzą całej aplikacji. W przypadku operacji wejścia-wyjścia należy zamiast tego używać dedykowanych executorów.

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);
    }
}

Mierz, nie zgaduj

Jedynym wiarygodnym sposobem sprawdzenia, czy równoległość pomaga, jest wykonanie benchmarku na realistycznych danych. Intuicja dotycząca wydajności często zawodzi.

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");
    }
}

Praktyczna lista kontrolna

Równoległość warto stosować, gdy spełnione są wszystkie warunki:

  • Duża wartość N lub kosztowne operacje na elementach.
  • Źródło podatne na podział (tablica, ArrayList, zakres).
  • Tanie, asocjacyjne scalanie.
  • Operacje ograniczone przez procesor, bez blokujących operacji wejścia-wyjścia.
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);
    }
}

Szybkie sprawdzenie

Który scenariusz jest najlepszym kandydatem do użycia strumienia równoległego?

Podsumowanie

Poznano sytuacje, w których równoległość pomaga:

  • Korzyść rośnie wraz z N x Q (rozmiarem danych pomnożonym przez koszt operacji na elemencie).
  • Potrzebne jest źródło podatne na podział (tablice, ArrayList, zakresy).
  • Potrzebne jest tanie, asocjacyjne scalanie.
  • Należy utrzymywać zadania w obszarze obliczeń procesora, unikać blokujących operacji wejścia-wyjścia i zawsze wykonywać pomiary.

Często zadawane pytania

Czy lekcja „Kiedy równoległość pomaga” jest bezpłatna?

Tak — pełny tekst „Kiedy równoległość pomaga” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Kiedy równoległość pomaga”?

Czynniki związane z obciążeniem i rozmiarem danych Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Kiedy równoległość pomaga”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Tworzenie strumieni równoległych
  2. Kiedy równoległość pomaga
  3. Bezpieczeństwo wątkowe i efekty uboczne
  4. Typowe pułapki
← Powrót do Java Academy