Quand le parallélisme est utile
Facteurs liés à la charge et au volume de données
Quand le parallélisme est utile est une leçon Java Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Java Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Java Academy comprend 4 leçons au total.
Le parallel a un coût
Le passage en parallel ajoute des coûts : division des données, distribution des tâches et fusion des résultats. Il n’est rentable que lorsque ces coûts sont inférieurs au temps gagné.
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);
}
}Facteur 1 : taille des données (N)
Une valeur élevée de N amortit le coût fixe du parallel. Une règle approximative est d’attendre plusieurs dizaines de milliers d’éléments avant que le parallel devienne intéressant.
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);
}
}Facteur 2 : travail par élément (Q)
Le coût Q du traitement de chaque élément est important. Un travail coûteux par élément (calcul intensif) tire profit du parallel même pour une valeur de N plus faible.
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;
}
}L’intuition de N × Q
Considérez le travail utile total comme N × Q. Plus ce produit est grand, plus le parallel peut être avantageux. Une petite valeur de N ou un Q insignifiant le justifie rarement.
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);
}
}Facteur 3 : capacité de division
Les sources de données qui se divisent rapidement et uniformément se parallélisent bien : les tableaux, ArrayList et IntStream.range. LinkedList et les sources fondées sur des itérateurs se divisent mal.
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);
}
}Bonne source : tableaux et plages
Les plages de types primitifs ont une taille connue et se divisent en O(1), ce qui en fait des sources parallel idéales.
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);
}
}Source médiocre : générateurs itératifs
Stream.iterate produit les éléments séquentiellement par définition ; il ne peut donc pas se diviser avant que les éléments soient matérialisés. C’est une source parallel peu efficace.
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);
}
}Facteur 4 : étape de fusion peu coûteuse
Le parallel nécessite un moyen peu coûteux de combiner les résultats partiels. sum et count se fusionnent trivialement ; la construction d’une liste triée ou l’utilisation d’un collecteur nécessitant un arbre volumineux entraîne une fusion coûteuse.
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);
}
}Éviter pour les tâches limitées par les E/S
Les flux parallel ciblent les tâches limitées par le CPU dans le groupe fork-join. Les E/S bloquantes saturent le groupe partagé et nuisent à l’ensemble de l’application. Utilisez plutôt des exécuteurs dédiés aux E/S.
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);
}
}Mesurer plutôt que deviner
La seule manière fiable de savoir si le parallel est avantageux consiste à effectuer un test de performance avec des données réalistes. L’intuition concernant les performances est souvent erronée.
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");
}
}Liste de vérification pratique
Privilégiez le parallel lorsque toutes les conditions suivantes sont réunies :
- N est grand et/ou le travail par élément est coûteux.
- La source peut être divisée (tableau, ArrayList, plage).
- La fusion est peu coûteuse et associative.
- Le travail est limité par le CPU et n’effectue pas d’E/S bloquantes.
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);
}
}Vérification rapide
Quel scénario constitue le meilleur candidat pour un flux parallel ?
Récapitulatif
Vous avez appris quand le parallel est utile :
- L’avantage augmente avec N × Q (taille des données multipliée par le coût par élément).
- Il faut une source divisible (tableaux, ArrayList, plages).
- Il faut une fusion associative peu coûteuse.
- Limitez le travail au CPU, évitez les E/S bloquantes et mesurez toujours les performances.
Questions Fréquemment Posées
La leçon « Quand le parallélisme est utile » est-elle gratuite ?
Oui — le texte complet de « Quand le parallélisme est utile » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Java Academy, passe à CoddyKit PRO. Le cours Java Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Quand le parallélisme est utile » ?
Facteurs liés à la charge et au volume de données Tu pratiques Java Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Java Academy ?
Aucune expérience préalable n'est requise. Java Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Quand le parallélisme est utile » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Java Academy ?
Oui. Chaque leçon Java Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Créer des flux parallèles
- Quand le parallélisme est utile
- Sûreté vis-à-vis des threads et effets secondaires
- Pièges courants