Cuándo ayuda el paralelismo
Factores de carga de trabajo y tamaño de los datos
Cuándo ayuda el paralelismo es una lección gratuita de Java Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Java Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Java Academy incluye 4 lecciones en total.
El paralelismo tiene un coste
Ejecutar en paralelo añade costes: dividir los datos, asignar tareas y combinar resultados. Solo compensa cuando ese coste es inferior al tiempo ahorrado.
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);
}
}Factor 1: tamaño de los datos (N)
Un N grande amortiza el coste fijo del paralelismo. Como regla general aproximada, suelen hacer falta decenas de miles de elementos para que el modo paralelo resulte rentable.
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);
}
}Factor 2: trabajo por elemento (Q)
Importa el coste Q de procesar cada elemento. El trabajo costoso por elemento (cálculos intensivos) se beneficia del paralelismo incluso con valores de N más pequeños.
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;
}
}La intuición de N por Q
Considere que el trabajo útil total es N x Q. Cuanto mayor sea ese producto, más puede ayudar el paralelismo. Un N muy pequeño o un Q trivial rara vez lo justifican.
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);
}
}Factor 3: capacidad de división
Las fuentes de datos que se dividen de forma económica y uniforme se paralelizan bien: arrays, ArrayList e IntStream.range. LinkedList y las fuentes basadas en iteradores se dividen 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);
}
}Buena fuente: arrays y rangos
Los rangos de tipos primitivos tienen un tamaño conocido y se dividen en O(1), por lo que son fuentes paralelas ideales.
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);
}
}Mala fuente: generadores iterativos
Stream.iterate produce los elementos secuencialmente por definición, por lo que no puede dividirse hasta que los elementos se hayan materializado. Es una fuente paralela poco adecuada.
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);
}
}Factor 4: paso de combinación económico
El paralelismo necesita una forma económica de combinar los resultados parciales. sum y count se combinan de forma trivial; construir una lista ordenada o usar un collector con muchos nodos resulta costoso.
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);
}
}Evítelo en trabajos limitados por E/S
Los streams paralelos están pensados para tareas limitadas por la CPU en el pool fork-join. La E/S bloqueante satura el pool compartido y perjudica a toda la aplicación. Use ejecutores dedicados para la 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);
}
}Mida, no haga suposiciones
La única forma fiable de saber si el paralelismo ayuda es realizar un benchmark con datos realistas. La intuición sobre el rendimiento suele ser errónea.
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");
}
}Lista de comprobación práctica
Prefiera el modo paralelo cuando se cumplan todas estas condiciones:
- N grande o trabajo costoso por elemento.
- Una fuente que pueda dividirse (array, ArrayList, rango).
- Una combinación económica y asociativa.
- Trabajo limitado por la CPU, sin E/S bloqueante.
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);
}
}Comprobación rápida
¿Qué escenario es el mejor candidato para un stream paralelo?
Resumen
Ha aprendido cuándo ayuda el paralelismo:
- El beneficio aumenta con N x Q (tamaño de los datos por coste de cada elemento).
- Se necesita una fuente que pueda dividirse (arrays, ArrayList, rangos).
- Se necesita una combinación asociativa y económica.
- Mantenga el trabajo limitado por la CPU, evite la E/S bloqueante y mida siempre el rendimiento.
Preguntas frecuentes
¿La lección «Cuándo ayuda el paralelismo» es gratis?
Sí — el texto completo de «Cuándo ayuda el paralelismo» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Java Academy, actualiza a CoddyKit PRO. El curso de Java Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Cuándo ayuda el paralelismo»?
Factores de carga de trabajo y tamaño de los datos Practicas Java Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Java Academy?
No se requiere experiencia previa. Java Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Cuándo ayuda el paralelismo»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Java Academy?
Sí. Cada lección de Java Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Creación de streams paralelos
- Cuándo ayuda el paralelismo
- Seguridad de hilos y efectos secundarios
- Errores comunes