Milloin rinnakkaisuus auttaa
Työmäärä ja tietojen koko vaikuttavat.
Milloin rinnakkaisuus auttaa on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.
Rinnakkaisuudella on hintansa
Rinnakkaisuus lisää yleiskustannusta: dataa jaetaan osiin, tehtäviä lähetetään suoritettaviksi ja tuloksia yhdistetään. Se kannattaa vain, jos kustannus on pienempi kuin saavutettu ajansäästö.
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);
}
}Tekijä 1: datan koko (N)
Suuri N jakaa rinnakkaisuuden kiinteän yleiskustannuksen suuremmalle määrälle alkioita. Karkean nyrkkisäännön mukaan rinnakkaisuus alkaa kannattaa vasta kymmenien tuhansien alkioiden kohdalla.
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);
}
}Tekijä 2: työ alkiota kohden (Q)
Kunkin alkion käsittelyyn kuluva kustannus Q on merkityksellinen. Raskas alkiokohtainen laskenta hyötyy rinnakkaisuudesta jo pienemmällä N:n arvolla.
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 kertaa Q -ajattelumalli
Ajatelkaa hyödyllisen kokonaistyön määräksi N × Q. Mitä suurempi tulo on, sitä enemmän rinnakkaisuudesta voi olla hyötyä. Pieni N tai vähäinen Q harvoin oikeuttaa rinnakkaisuuden käyttöön.
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);
}
}Tekijä 3: jaettavuus
Lähteet, jotka voidaan jakaa osiin nopeasti ja tasaisesti, soveltuvat hyvin rinnakkaistamiseen: taulukot, ArrayList ja IntStream.range. LinkedList ja iteraattoripohjaiset lähteet jakautuvat huonosti.
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);
}
}Hyvä lähde: taulukot ja alueet
Primitiivialueilla on tunnettu koko, ja ne voidaan jakaa vakioajassa O(1), joten ne ovat ihanteellisia rinnakkaisia lähteitä.
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);
}
}Huono lähde: iteratiiviset generaattorit
Stream.iterate tuottaa alkiot määritelmänsä mukaisesti peräkkäin, joten sitä ei voida jakaa ennen kuin alkiot on tuotettu. Se on heikko rinnakkaislähde.
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);
}
}Tekijä 4: edullinen yhdistämisvaihe
Rinnakkaisuus tarvitsee tehokkaan tavan yhdistää osatulokset. sum ja count yhdistyvät helposti, kun taas järjestetyn listan tai puumaisen kerääjärakenteen muodostaminen on kallista.
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);
}
}Välttäkää I/O-sidonnaisessa työssä
Rinnakkaiset virrat on tarkoitettu suorittimen kuormittamiin tehtäviin fork-join-poolissa. Estävä I/O kuluttaa jaetun poolin säikeet loppuun ja haittaa koko sovellusta. Käyttäkää I/O:ta varten erillisiä suorittajia.
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);
}
}Mitatkaa, älkää arvailko
Ainoa luotettava tapa selvittää, auttaako rinnakkaisuus, on tehdä suorituskykytesti realistisella datalla. Suorituskykyä koskeva intuitio on usein väärässä.
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");
}
}Käytännön tarkistuslista
Suosikaa rinnakkaisuutta, kun kaikki seuraavat ehdot täyttyvät:
- Suuri N ja/tai raskas alkiokohtainen työ.
- Jaettava lähde (taulukko, ArrayList tai alue).
- Edullinen ja assosiatiivinen yhdistämisoperaatio.
- Suoritinkeskeinen työ, ei estävää I/O:ta.
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);
}
}Pikatarkistus
Mikä tilanne soveltuu parhaiten rinnakkaisen virran käyttöön?
Kertaus
Opitte, milloin rinnakkaisuudesta on hyötyä:
- Hyöty kasvaa tulon N × Q mukana (datan koko kertaa alkiokohtainen kustannus).
- Lähteen on oltava jaettava (taulukot, ArrayList tai alueet).
- Yhdistämisoperaation on oltava edullinen ja assosiatiivinen.
- Pitäkää työ suoritinkeskeisenä, välttäkää estävää I/O:ta ja mitatkaa aina.
Opi Java tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 104
- Oppitunnit
- 374
Usein kysytyt kysymykset
Onko oppitunti ”Milloin rinnakkaisuus auttaa” ilmainen?
Kyllä – oppitunnin ”Milloin rinnakkaisuus auttaa” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Milloin rinnakkaisuus auttaa”?
Työmäärä ja tietojen koko vaikuttavat. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.
Kuinka kauan ”Milloin rinnakkaisuus auttaa”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?
Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Rinnakkaisvirtojen luonti
- Milloin rinnakkaisuus auttaa
- Säieturvallisuus ja sivuvaikutukset
- Yleiset sudenkuopat