news40 parallel computing
TRANSCRIPT
Parallel Computing with .NET 4.0
Sacando partido al multi-core
Lluís Franco - AndorraDotNet
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
3
Let’s play!
Parallel Computing with .NET 4.0
4
1 - ¿Y eso queéloqueé?¿Esto?
5
1 - ¿Y eso queéloqueé?¿Acaso esto?
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”.
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).
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 unatecnología diferente
para seguir realizando cálculos a ese nivel».
Stephen Hawking
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
10
2 – Un poco de historia
¿Y a Deep Blue? 1997: El primer ordenador en batir a un gran maestro (Garry Kasparov)
11
2 – Algunos conceptos
Procesos & Threads en un sistema operativo
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
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.
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).
ThreadStart job = new ThreadStart(HacerAlgo); Thread thread = new Thread(job); thread.Start();
15
3 - ¿Multithreading vs. Parallism?
Escenario nº1
Escenario nº2
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.
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!
18
3 - ¿Multithreading vs. Parallism?
• Esto SI es paralelismo
19
3 - ¿Multithreading vs. Parallism?
• Y más vale que nos preparemos:
20
4 - Paralelismo en .NET 4.0
Microsoft Task Parallel Library (TPL)
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
22
LINQ
4 - Paralelismo en .NET 4.0
PLINQ
Devuelve colecciones IEnumerable<T> / IQueryable<T>
Devuelve colecciones ParallelQuery<T>
23
PLINQ
4 - Paralelismo en .NET 4.0
AsParallel();AsOrdered();ForAll(action);WithDegreeOfParallelism(ncores);WithCancellation(CancellationToken);WithMergeOptions(ParallelMergeOptions);WithExecutionMode(ParallelExecutionMode);
ParallelQuery
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.
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; }
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}
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}
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}
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}
30
Demo 1 online{PLINQ}
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);
32
Demo 2 {PLINQ}
private void printInfo(string filename) { FileInfo f = new FileInfo(filename); byte[] bytes = File.ReadAllBytes(f.FullName); FileSecurity fs = f.GetAccessControl(); IdentityReference identity = fs.GetOwner(typeof(NTAccount)); IdentityReference group = fs.GetGroup(typeof(NTAccount)); string s = string.Format( "file '{0}' ({1} kb), owner '{2}' ({3}), accesed at '{4}'", f.Name, (f.Length / 1024).ToString("n2"), identity.Value, group.Value, f.LastAccessTime); filesinfoListBox.Items.Add(s); }
33
Demo 2 {PLINQ}
private void getFilesForEach() { filesinfoListBox.Items.Clear(); clock.Restart(); var files = Directory.EnumerateFiles( Server.MapPath(""), "*.*", SearchOption.AllDirectories); foreach (string s in files) { printInfo(s); } clock.Stop(); elapsedTimeLabel2.Text = clock.
ElapsedMilliseconds.ToString("n2"); }
34
Demo 2 {PLINQ}
private void getFilesParallelForAll() { filesinfoListBox.Items.Clear(); clock.Restart(); var files = Directory.EnumerateFiles( Server.MapPath(""), "*.*", SearchOption.AllDirectories).AsParallel(); files.ForAll<string>(p => printInfo(p)); clock.Stop(); elapsedTimeLabel2.Text = clock.
ElapsedMilliseconds.ToString("n2"); }
35
Demo 2 online{PLINQ}
36
4 - Paralelismo en .NET 4.0
Parallel.ForParallel.ForEachParallel.Invoke
Parallel
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).
38
Demo 3 {Parallel}
static int Fibonacci(int x) { if (x <= 1) return 1; return
Fibonacci(x - 1) + Fibonacci(x - 2);
}
39
Demo 3 {Parallel}
private void calculateFibonacciFor() { clock.Restart(); fibonacciListBox.Items.Clear(); for (int i = 1; i <= 40; i++) { fibonacciListBox.Items.Add(
Fibonacci(i).ToString()); } clock.Stop(); elapsedTimeLabel.Text = clock.
ElapsedMilliseconds.ToString("n2"); }
40
Demo 3 {Parallel}
private void calculateFibonacciParallelFor() { clock.Restart(); fibonacciListBox.Items.Clear(); Parallel.For(1, 40, i => { fibonacciListBox.Items.Add(
Fibonacci(i).ToString()); }); clock.Stop(); elapsedTimeLabel.Text = clock.
ElapsedMilliseconds.ToString("n2"); }
41
Demo 3 online{Parallel}
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 ).
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; } }
Clase para almacenar información de cada uno de los posts:
44
Demo 4 {Parallel}
List<string> urls = new List<string>(); private void addUrls() { urls.Clear(); urls.Add("http://weblogs.asp.net/scottgu/rss.aspx"); urls.Add("http://andorradotnet.com/blogs/MainFeed.aspx"); urls.Add("http://geeks.ms/blogs/MainFeed.aspx"); urls.Add("http://feeds2.feedburner.com/CienciaKanija"); }
45
Demo 4 {Parallel} public List<FeedDefinition> loadFeeds(string path) { try { XDocument feedxml = XDocument.Load(path); var feeds = from feed in feedxml.Descendants("item") select new FeedDefinition { Name = feed.Element("title").Value, Url = feed.Element("link").Value, Date = DateTime.Parse(
feed.Element("pubDate").Value), NumComments = int.Parse(
feed.Element(slashNamespace + "comments").Value) }; return feeds.ToList(); } catch (Exception) return null; } }
46
Demo 4 {Parallel}
private void getFeedsForEach() { clock.Restart(); addUrls(); List<FeedDefinition> allfeeds =
new List<FeedDefinition>(); foreach (var url in urls) { allfeeds.AddRange(loadFeeds(url)); } showFeeds(allfeeds); clock.Stop(); elapsedTimeLabel2.Text = clock.
ElapsedMilliseconds.ToString("n2"); }
47
Demo 4 {Parallel}
private void getFeedsParallelForEach() { clock.Restart(); addUrls(); List<FeedDefinition> allfeeds =
new List<FeedDefinition>(); Parallel.ForEach(urls, url => { allfeeds.AddRange(loadFeeds(url)); }); showFeeds(allfeeds); clock.Stop(); elapsedTimeLabel2.Text = clock.
ElapsedMilliseconds.ToString("n2"); }
48
Demo 4 online{Parallel}
49
Demo 5 {TASK class}
• Ejecutar tareas implícitamente (Invoke): public void InvokeSample() { Parallel.Invoke(() => { Console.WriteLine("Begin first task..."); GetLongestWord(words); }, // close first Action () => { Console.WriteLine("Begin second task..."); GetMostCommonWords(words); }, //close second Action () => { Console.WriteLine("Begin third task..."); GetCountForWord(words, "species"); } //close third Action ); //close parallel.invoke }
50
Demo 5 {TASK class}
• Ejecutar tareas explícitamente (Task class):
Task t1 = Task.Factory.StartNew(() => DoSomething());
Task t1 = new Task(() => DoSomething()); t1.Start();
var t1 = Task.Factory.StartNew<int>(() => DoSomething()); Task.WaitAny(t1); int i = t1.Result;
51
Demo 5 {TASK class}
• Encadenar tareas (ContinueWith): static void SimpleContinuation() { string path = @"C:\users\public\TPLTestFolder\"; try { var firstTask = new Task(() => CopyDataIntoTempFolder(path)); var secondTask = firstTask.ContinueWith((t) => CreateSummaryFile(path)); firstTask.Start(); } catch (AggregateException e) { Console.WriteLine(e.Message); } }
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 ?
53
Demo 5 {TASK class}
private void ApplyFiltersSequentially() { clock.Restart(); ApplyFilterImage1(); ApplyFilterImage2(); ApplyFilterImage3(); ApplyFilterImage4(); ApplyFilterImage5(); ApplyFilterImage6(); clock.Stop(); elapsedTimeLabel.Text = clock.ElapsedMilliseconds.ToString("n"); }
54
private void ApplyFiltersParallel() { clock.Restart(); Task t1 = Task.Factory.StartNew(() => ApplyFilterImage1()); Task t2 = Task.Factory.StartNew(() => ApplyFilterImage2()); Task t3 = Task.Factory.StartNew(() => ApplyFilterImage3()); Task t4 = Task.Factory.StartNew(() => ApplyFilterImage4()); Task t5 = Task.Factory.StartNew(() => ApplyFilterImage5()); Task t6 = Task.Factory.StartNew(() => ApplyFilterImage6()); Task.WaitAll(t1, t2, t3, t4, t5, t6); clock.Stop(); elapsedTimeLabel.Text = clock.ElapsedMilliseconds.ToString("n"); }
Demo 5 {TASK class}
55
Demo 5 {TASK class}
private void ApplyFilterImage1() { Bitmap b = (Bitmap)System.Drawing.Image.FromFile( Server.MapPath("~/Images/Penguins.jpg")); b.Invert().Save(Server.MapPath("~/Images/PenguinsInvert.jpg")); img1.ImageUrl = "~/Images/PenguinsInvert.jpg"; } private void ApplyFilterImage2() { Bitmap b = (Bitmap)System.Drawing.Image.FromFile( Server.MapPath("~/Images/Desert.jpg")); b.Grayscale().Save(Server.MapPath("~/Images/DesertGrayscale.jpg")); img2.ImageUrl = "~/Images/DesertGrayscale.jpg"; }
56
Demo 5 online{TASK class}
57
Demo 6 {Thread.Priority}
• 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} 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, }
59
Demo 6 {Thread.Priority}
ThreadPriority rabbitPriority = (ThreadPriority)DropDownList1.SelectedIndex;
ThreadPriority turtlePriority = (ThreadPriority)DropDownList2.SelectedIndex;
Task<long> t1 = Task.Factory.StartNew<long> (() => priorityTest.StartCount(rabbitPriority)); Task<long> t2 = Task.Factory.StartNew<long> (() => priorityTest.StartCount(turtlePriority));
60
Demo 6 online{Thread.Priority}
61
Demo 7 {Concurrence}
• 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}
• 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}
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}
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); }
65
Demo 7 {Concurrence} private void UpdateDictionary() { if (points1.ContainsKey(5)) { Thread.Sleep(2000); try { Point p = points1[5]; //<- ERROR!!! p.X = 666; p.Y = 666; messagesListBox.Items.Insert(0, string.Format("Changed successfully: p.X = {0}", p.X)); } catch (Exception ex) { messagesListBox.Items.Insert(0, string.Format("Error, {0}", ex.Message)); } } }
66
Demo 7 {Concurrence}
private void UpdateConcurrentDictionary() { Point oldp = points2[5]; Point newp = new Point(666, 666); if (points2.TryUpdate(5, newp, oldp)) { messagesListBox.Items.Insert(0, string.Format(
"Changed successfully: p.X = {0}", newp.X)); } else { messagesListBox.Items.Insert(0, string.Format("Error, key and value not found!")); } }
67
Demo 7 online{Concurrence}
68
Demo 7 {Concurrence}
Thread-safe Collections in .NET Framework 4 and Their Performance Characteristics