W Javie 8 i lambdach łatwo jest iterować kolekcje jako strumienie, a równie łatwo korzystać z równoległego strumienia. Dwa przykłady z dokumentów , drugi z wykorzystaniem parallelStream:
myShapesCollection.stream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
myShapesCollection.parallelStream() // <-- This one uses parallel
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
Tak długo, jak długo nie dbam o zamówienie, czy zawsze byłoby korzystne korzystanie z równoległości? Można by pomyśleć, że szybciej dzieli się pracę na więcej rdzeni.
Czy są inne względy? Kiedy należy stosować strumień równoległy, a kiedy nie-równoległy?
(To pytanie ma na celu zainicjowanie dyskusji na temat tego, jak i kiedy używać strumieni równoległych, nie dlatego, że myślę, że zawsze korzystanie z nich jest dobrym pomysłem).
źródło
Runnable
które wywołuję,start()
aby ich użyć, ponieważ czy mogęThreads
to zmienić na używanie strumieni Java 8 w trybie.forEach()
równoległym? Wtedy będę mógł usunąć kod wątku z klasy. Ale czy są jakieś wady?Interfejs API Stream został zaprojektowany tak, aby ułatwić pisanie obliczeń w sposób oderwany od sposobu ich wykonywania, ułatwiając przełączanie między sekwencyjnym a równoległym.
Jednak tylko dlatego, że jest łatwy, nie oznacza, że zawsze jest to dobry pomysł, a tak naprawdę, to zły pomysł, aby po prostu rzucić się
.parallel()
w to miejsce tylko dlatego, że możesz.Po pierwsze, zauważ, że równoległość nie oferuje żadnych innych korzyści poza możliwością szybszego wykonania, gdy dostępnych jest więcej rdzeni. Wykonanie równoległe zawsze będzie wymagało więcej pracy niż wykonanie sekwencyjne, ponieważ oprócz rozwiązania problemu musi również wykonywać wysyłanie i koordynację pod-zadań. Mamy nadzieję, że szybciej uzyskasz odpowiedź, dzieląc pracę na wiele procesorów; to, czy tak się faktycznie dzieje, zależy od wielu rzeczy, w tym od wielkości zbioru danych, ilości obliczeń wykonywanych dla każdego elementu, charakteru obliczeń (w szczególności, czy przetwarzanie jednego elementu współdziała z przetwarzaniem innych?) , liczbę dostępnych procesorów i liczbę innych zadań konkurujących o te procesory.
Ponadto zauważ, że równoległość często ujawnia również niedeterminizm w obliczeniach, który często jest ukryty przez sekwencyjne implementacje; czasami nie ma to znaczenia lub można je złagodzić ograniczając związane z tym operacje (tj. operatory redukcji muszą być bezpaństwowcami i asocjatywne).
W rzeczywistości czasami paralelizm przyspieszy obliczenia, czasem nie, a czasem nawet spowolni. Najlepiej jest najpierw opracować przy użyciu wykonywania sekwencyjnego, a następnie zastosować równoległość gdzie
(A) wiesz, że tak naprawdę korzyści płyną ze zwiększonej wydajności i
(B) że faktycznie zapewni zwiększoną wydajność.
(A) to problem biznesowy, a nie techniczny. Jeśli jesteś ekspertem od wydajności, zwykle będziesz w stanie spojrzeć na kod i ustalić (B), ale inteligentną ścieżką jest pomiar. (I nawet nie zawracaj sobie głowy, dopóki nie przekonasz się o (A); jeśli kod jest wystarczająco szybki, lepiej zastosować cykle mózgowe w innym miejscu).
Najprostszym modelem wydajności dla równoległości jest model „NQ”, w którym N oznacza liczbę elementów, a Q jest obliczeniem na element. Ogólnie rzecz biorąc, potrzebujesz NQ produktu, aby przekroczyć pewien próg, zanim zaczniesz uzyskiwać korzyści z wydajności. W przypadku problemu o niskiej wartości Q, takiego jak „zsumowanie liczb od 1 do N”, generalnie widać wartość progową między N = 1000 a N = 10000. W przypadku problemów z wyższym Q zobaczysz progi rentowności przy niższych progach.
Ale rzeczywistość jest dość skomplikowana. Tak więc, dopóki nie osiągniesz stanu eksperymentalnego, najpierw określ, kiedy sekwencyjne przetwarzanie faktycznie cię kosztuje, a następnie zmierz, czy równoległość pomoże.
źródło
findAny
zamiastfindFirst
...myListOfURLs.stream().map((url) -> downloadPage(url))...
.).ForkJoinPool.commonPool()
i nie chcesz, aby blokowały Cię zadania.Patrzyłem jedną z prezentacji z Brian Goetz (Język Java Architect & specyfikacji dla ołowiu Lambda Expressions) . Wyjaśnia szczegółowo następujące 4 punkty, które należy rozważyć przed przystąpieniem do równoległości:
Koszty dzielenia / rozkładu
- Czasami dzielenie jest droższe niż wykonywanie pracy!
Koszty wysyłki / zarządzania
zadaniami - mogą wykonać dużo pracy w czasie potrzebnym na przekazanie pracy innemu wątkowi.
Koszty kombinacji wyników
- czasami kombinacja obejmuje kopiowanie dużej ilości danych. Na przykład dodawanie liczb jest tanie, a scalanie zestawów drogie.
Lokalizacja
- Słoń w pokoju. To ważna kwestia, której każdy może przegapić. Powinieneś rozważyć pominięcie pamięci podręcznej, jeśli procesor czeka na dane z powodu braków pamięci podręcznej, nic nie zyskasz przez równoległość. Dlatego źródła oparte na macierzach najlepiej zrównoleglają się najlepiej, gdy kolejne indeksy (w pobliżu bieżącego indeksu) są buforowane i istnieje mniejsze prawdopodobieństwo, że procesor odczuje brak pamięci podręcznej.
Wspomina także o stosunkowo prostej formule, aby określić szansę na równoległe przyspieszenie.
Model NQ :
gdzie
N = liczba elementów danych
Q = ilość pracy na element
źródło
JB uderzył w gwóźdź. Jedyne, co mogę dodać, to to, że Java 8 nie wykonuje czystego przetwarzania równoległego, robi parakwencjalne . Tak, napisałem ten artykuł i robię F / J od trzydziestu lat, więc rozumiem ten problem.
źródło
ArrayList
/HashMap
.Inne odpowiedzi obejmowały już profilowanie, aby uniknąć przedwczesnej optymalizacji i kosztów ogólnych w przetwarzaniu równoległym. Ta odpowiedź wyjaśnia idealny wybór struktur danych do równoległego przesyłania strumieniowego.
Źródło: Przedmiot nr 48 Zachowaj ostrożność podczas tworzenia równoległych strumieni, skuteczna Java 3e autorstwa Joshua Blocha
źródło
Nigdy nie równolegle nieskończonego strumienia z ograniczeniem. Oto co się dzieje:
Wynik
To samo, jeśli używasz
.limit(...)
Objaśnienie tutaj: Java 8, użycie .parallel w strumieniu powoduje błąd OOM
Podobnie nie używaj równolegle, jeśli strumień jest uporządkowany i zawiera znacznie więcej elementów niż chcesz przetworzyć, np
Może to działać znacznie dłużej, ponieważ wątki równoległe mogą działać na wielu zakresach liczb zamiast kluczowych 0-100, co powoduje, że zajmuje to bardzo dużo czasu.
źródło