2. Agenda
1. Programación paralela ¿Queéloqueé?
2. Un poco de historia y algunos conceptos.
3. ¿Multithreading versus Parallel?
4. Paralelismo en .NET 4.0 (PLINQ, Parallel, Task)
5. Demos…
6. Demos……
7. Y si… más demos
2
6. 1 - ¿Y eso queéloqueé?
Wikipedia dixit:
“La computación paralela es una técnica de programación en la que muchas
instrucciones se ejecutan simultáneamente. Se basa en el principio de que los
problemas grandes se pueden dividir en partes más pequeñas que pueden resolverse
de forma concurrente”.
“Existen varios tipos de computación paralela: paralelismo a nivel de bit, paralelismo a
nivel de instrucción, paralelismo de datos y paralelismo de tareas”.
“Durante muchos años, la computación paralela se ha aplicado en la computación de
altas prestaciones, pero el interés en ella ha aumentado en los últimos años debido a
las restricciones físicas que impiden el escalado en frecuencia”.
6
7. 1 - ¿Y eso queéloqueé?
La Ley de Moore expresa
que aproximadamente
cada 18 meses (*) se
duplica el número de
transistores en un circuito
integrado. Se trata de una
ley empírica, formulada
por el co-fundador de Intel,
Gordon E. Moore el 19 de
abril de 1965, cuyo
cumplimiento se ha podido
constatar hasta hoy.
Ley de Moore:
(*) 2 años (1975).
7
8. 1 - ¿Y eso queéloqueé?
• La ley de Moore ha muerto:
– Tecnología actual 32 nanómetros
– Límite 18 nanómetros (cambios)
– Se alcanzará aprox. en 2014
• Solución:
– Más núcleos
– Computación en paralelo
– INTEL / AMD / NVIDIA (CUDA)
«La materia presenta
efectos cuánticos que
harían necesaria una
tecnología diferente
para seguir realizando
cálculos a ese nivel».
Stephen Hawking
8
9. 2 – Un poco de historia
¿Alguien recuerda los CRAY?
1976: Es uno de los supercomputadores más
conocidos y exitosos de la historia, y de los más
potentes en su época
9
10. 2 – Un poco de historia
¿Y a Deep Blue?
1997: El primer ordenador en batir
a un gran maestro (Garry Kasparov)
10
11. 2 – Algunos conceptos
Procesos & Threads en un sistema operativo
11
Un proceso proporciona los recursos
necesarios para ejecutar un programa.
Contiene un espacio de memoria virtual,
código ejecutable, un contexto de
seguridad, un identificador de proceso
único, variables de entorno, y al menos
un thread de ejecución.
Cada proceso se inicia con un único
thread, a menudo llamado thread
principal, pero puede crear threads
adicionales.
12. 2 – Algunos conceptos
Procesos & Threads en un sistema operativo
12
Un thread es la entidad dentro de un
proceso que realmente ejecuta el código.
Todos los threads de un proceso
comparten los recursos y memoria virtual.
Además, cada thread mantiene
controladores de excepciones, una
prioridad de programación,
almacenamiento local, y un identificador
de thread único.
A más threads, más tiempo de CPU.
Lógico, no?
13. 3 - ¿Multithreading vs. Parallism?
• Tipos de Multitarea:
– Cooperativa o No Preemptiva (Windows anteriores a
Win95/WinNT y MacOS): Cada proceso se ocupa de pasar
el control al siguiente proceso. Un mal diseño de una
aplicación y CATACRACK!
– Preferente o Preemptiva (Windows Win95 y superiores, el
resto de S.O. modernos): El sistema operativo es el
encargado de ceder un tiempo a cada thread de cada
proceso (time slice). Un mal diseño de una aplicación no
implica el derrumbe del S.O. ya que se ejecutan en modo
user.
13
14. 3 - ¿Multithreading vs. Parallism?
• Multithreading:
– Técnica que consiste en manejar varios threads en una aplicación:
– Permite realizar tareas asíncronas al ‘mismo tiempo’ (aunque
realmente no es así).
– No es trivial (más complejidad, depuración, acceso prohibido a la
interfaz de usuario).
14
ThreadStart job = new ThreadStart(HacerAlgo);
Thread thread = new Thread(job);
thread.Start();
16. 3 - ¿Multithreading vs. Parallism?
Una aplicación puede incrementar el número de threads
para producir la sensación de que varios procesos se
llevan a cabo al mismo tiempo.
Pero sólo es paralelismo REAL si existen varios cores que
los ejecuten en paralelo.
16
3 1 1 1
varios cores
17. 3 - ¿Multithreading vs. Parallism?
• Conclusión: Se parece pero no es lo mismo
• Podemos tener multithreading en una
estación con un solo core, pero sólo podemos
tener paralelismo real en una estación multi-
core.
• Error clásico: Como desarrolladores, utilizar
paralelismo en una máquina virtual (con un
solo core), y nos pensamos que estamos
haciendo paralelismo ->NOR!
17
20. 4 - Paralelismo en .NET 4.0
Microsoft Task Parallel Library (TPL)
20
21. 4 - Paralelismo en .NET 4.0
LINQ - PLINQ
• Proporciona
extensiones
del lenguaje
para poder
utilizar
paralelismo en
nuestras
consultas
LINQ.
DATA - Parallel
• Situaciones en
las que se
realiza la
misma
operación al
mismo tiempo
sobre los
elementos de
un origen de
colección o
matriz.
TASKS - Task
• Una tarea
representa
una operación
asíncrona, se
asemeja a la
creación de un
Thread o un
ThreadPool,
pero a un nivel
más alto de
abstracción.
Concurrent
• Proporcionan
mecanismos
para
compartir
colecciones y
elementos
entre varios
hilos de
ejecución
(Thread-safe).
Más sencillo Más complejo
21
24. Demo 1 {PLINQ}
• Partimos de una sencilla consulta LINQ, que
calcula los números primos hasta 10.000.000
• El objetivo es transformar esta consulta en
una consulta de ejecución paralela, mediante
el uso de PLINQ.
• Posteriormente veremos la diferencia de
tiempo de ejecución entre ambas.
24
25. Demo 1 {PLINQ}
public static bool IsPrime(int candidate)
{
if (candidate == 1) return true;
if ((candidate & 1) == 0)
{
if (candidate == 2) return true;
else return false;
}
for (int i = 3; (i * i) <= candidate; i += 2)
{
if ((candidate % i) == 0)
return false;
}
return candidate != 1;
}
25
26. private void getPrimesLINQ()
{
int[] array = Enumerable.Range(1, 10000000).ToArray();
clock.Restart();
int[] primesLINQ =
(from x in array
where IsPrime(x) select x).ToArray();
var list = primesLINQ.Take(100).ToList();
showValues(list);
clock.Stop();
elapsedTimeLabel.Text = clock.
ElapsedMilliseconds.ToString("n");
}
Demo 1 {PLINQ}
26
27. private void getPrimesPLINQ()
{
int[] array = Enumerable.Range(1, 10000000).ToArray();
clock.Restart();
int[] primesLINQ =
(from x in array.AsParallel()
where IsPrime(x) select x).ToArray();
var list = primesLINQ.Take(100).ToList();
showValues(list);
clock.Stop();
elapsedTimeLabel.Text = clock.
ElapsedMilliseconds.ToString("n");
}
Demo 1 {PLINQ}
27
28. private void getPrimesPLINQ()
{
int[] array = Enumerable.Range(1, 10000000).ToArray();
clock.Restart();
int[] primesLINQ =
(from x in array.AsParallel().AsOrdered()
where IsPrime(x) select x).ToArray();
var list = primesLINQ.Take(100).ToList();
showValues(list);
clock.Stop();
elapsedTimeLabel.Text = clock.
ElapsedMilliseconds.ToString("n");
}
Demo 1 {PLINQ}
28
29. private void getPrimesPLINQ()
{
int[] array = Enumerable.Range(1, 10000000).ToArray();
clock.Restart();
int[] primesLINQ =
(from x in array.AsParallel().AsOrdered()
.WithDegreeOfParallelism(numcores))
where IsPrime(x) select x).ToArray();
var list = primesLINQ.Take(100).ToList();
showValues(list);
clock.Stop();
elapsedTimeLabel.Text = clock.
ElapsedMilliseconds.ToString("n");
}
Demo 1 {PLINQ}
29
31. Demo 2 {PLINQ}
• Recorrer el árbol de directorios del sitio y
mostrar información de cada fichero.
• Uso de ForAll para aplicar una acción a todos
los elementos devueltos por un objeto de tipo
«ParallelQuery».
• Usado para sustituir a For / ForEach
ParallelQuery<T>.ForAll<T>(Action<T> action);
31
36. 4 - Paralelismo en .NET 4.0
Parallel.For
Parallel.ForEach
Parallel.Invoke
Parallel
36
Ejemplo en
la demo nº5
Extensiones para el trabajo secuencial sobre datos.
37. Demo 3 {Parallel}
• Calcular los n primeros números de
la serie de Fibbonacci.
• Veremos como usar la versión paralela del
clásico bucle For (Parallel.For)
• Compararemos la diferencia de tiempo entre
ambas (For vs. Parallel.For).
37
42. Demo 4 {Parallel}
• Crear un pequeño cliente de RSS para obtener los
feeds de algunos blogs mediante LINQ2XML.
• Veremos como usar la versión paralela del clásico
bucle ForEach (Parallel.ForEach)
• Compararemos la diferencia de tiempo entre
ambas (ForEach vs. Parallel.ForEach ).
42
43. Demo 4 {Parallel}
public class FeedDefinition
{
public string Name { get; set; }
public string Url { get; set; }
public DateTime Date { get; set; }
public int NumComments { get; set; }
}
43
Clase para almacenar información de cada uno de los posts:
52. Demo 5 {Task}
• Usar la clase Task para ejecutar varias tareas
en paralelo.
• Aplicar filtros a imágenes (1024x768 ~ 1Mb)
• El código de los filtros usa ‘unsafe’ para
ejecutar código con punteros
¿Alguien de VB en la sala ?
52
57. Demo 6 {Thread.Priority}
57
• Pregunta: ¿Es posible establecer la prioridad
de un thread en tiempo de ejecución?
• Respuesta: SI en función del entorno, pero en
un entorno multicore potente no tiene
demasiado sentido, ya que el S.O. siempre se
reserva la opción de modificarla.
• Aviso: Existen detractores de cambiar la
prioridad a nivel de thread:
stackoverflow.com , codinghorror.com
58. Demo 6 {Thread.Priority}
58
public long StartCount(ThreadPriority threadpriority)
{
Thread.CurrentThread.Priority = threadpriority;
long threadCount = 0;
while (LoopSwitch)
{
Thread.Sleep(1);
threadCount++;
}
return threadCount;
}
public enum ThreadPriority
{
Lowest = 0,
BelowNormal = 1,
Normal = 2,
AboveNormal = 3,
Highest = 4,
}
61. Demo 7 {Concurrence}
61
• En un entorno concurrente es bastante
probable encontrarse con situaciones de
bloqueos, o de elementos eliminados por
otros threads.
• NET 4.0 proviene de un conjunto de
colecciones specializadas thread-safe:
System.Collections.Concurrent
62. Demo 7 {Concurrence}
62
• Mostrar un ejemplo (exagerado!) de cómo en
un entorno multithread, los elementos de una
colección pueden ser modificados o eliminados
por otro thread y provocar un error en tiempo
de ejecución.
• Para prevenir estos errores veremos como usar
un ConcurrentDictionary y su método
TryUpdate.
63. Demo 7 {Concurrence}
63
Dictionary<int, Point> points1 = new Dictionary<int, Point>();
ConcurrentDictionary<int, Point> points2 =
new ConcurrentDictionary<int, Point>();
private void FillCollection()
{
points1.Clear();
points2.Clear();
Random r = new Random(DateTime.Today.Millisecond);
for (int i = 0; i < 100; i++)
{
int v = r.Next(1, 25);
points1.Add(i, new Point(v, v));
points2.TryAdd(i, new Point(v, v));
}
}
1 (20, 5)
2 (11,16)
3 (3,6)
4 (8,2)
5 (19,25)
64. Demo 7 {Concurrence}
64
Lanzamos dos tareas en paralelo, la primera intenta
modificar un elemento del diccionario (5), mientras que
la segunda borra todos los elementos del diccionario:
protected void Button1_Click(
object sender, EventArgs e)
{
FillCollection();
Task t1 = new Task(() => UpdateDictionary());
t1.Start();
Task t2 = new Task(() => ClearDictionary());
t2.Start();
Task.WaitAll(t1, t2);
}