Primeros pasos con MPI.NET

En estos días, estoy explorando la programación con MPI, usando la librería MPI.NET implementada sobre el MPI de Microsoft, que puede ser usada en Windows HPC Server 2008.

MPI es el acrónimo de Message Passing Interface, una API que soporta la programación de programas con paralelismo. Una apllicación MPI puede ejecutarse en varias instancias, llamadas “ranks”, y cada instancia puede recibir y enviar mensajes de y a las otras instancias. La API puede ser consumida desde lenguajes como C o Fortran. MPI.NET es un “wrapper” que facilita la escritura de programas MPI usando .NET.

No necesitamos un cluster para ejecutar un programa MPI. Cada programa puede ser probado localmente, lanzando varios ranks como procesos en nuestra máquina local.

Hace unos días, escribí algún código de ejemplo, para probar MPI.NET. Pueden bajarse el código desde mi Skydrive desde MpiNetFirstExamples.zip.

Si quieren probar otro camino, hace un tiempo escribí sobre otra implementación de MPI sobre .NET:

MPI Message Passing Interface in .NET

MPI

MPI (Message Passing Interface) está soportada por Windows HPC. Hay una implementación de Microsoft:

Microsoft MPI (Windows)

que puede ser invocada desde C++.

Hay una implementación .NET sobre Microsoft MPI en:

MPI.NET: High-Performance C# Library for Message Passing

Contiene el código fuente y ejemplos.

Para escribir y ejecutar los ejemplos de este post, instalé el HPC Pack que encontré en:

HPC Pack 2008 SDK download

y luego instalé el MPI.NET Software

(Instalé el MPI.NET SDK.msi pero también expandí el MPI.NET-1.0.0.zip: tiene mejores ejemplos, con soluciones VS)

Cuando se instala el HPC Pack 2008 SDK, nos quedan nuevos programas:

Y con MPI.NET:

Si expandimos el archivo adicional MPI.NET-1.0.0.zip obtenemos un directorio con más ejemplos y documentación:

Más sobre MPI en general:

MPI 2.0 Report
MPI Tutorials
Microsoft Messaging Passing Interface – Wikipedia, the free encyclopedia
Pure Mpi.NET

Hello World

Como siempre, un “Hello, World” es la primera aplicación a intentar. La solución es:

El código de program.cs es simple:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MpiNetHelloWorld { class Program { static void Main(string[] args) { using (new MPI.Environment(ref args)) { Console.WriteLine("I'm {0} of {1}", MPI.Communicator.world.Rank, MPI.Communicator.world.Size); } } } }

Notemos el uso de ref args en la inicialización del MPI.Environment. Un programa MPI recibe argumentos especiales que deben ser procesados y removidos del resto de los argumentos.

Si lo ejutamos en solitario, obtenemos:

No muy impresionante…. ;-)

Podemos invocar un comando mpiexec:

mpiexec -n 8 MpiNetHelloWorld.exe

y la salida es:

Hay 8 ranks (instancias) ejecutando en la misma máquina. Si tienen un cluster con soporte de MPI (como el  Windows HPC Server 2008) podría ejecutar el program en todos los nodos del cluster.

Ringing con los nodos

En el anterior ejemplo, no hay comunicaciones entre los nodos. Un clásico ejemplo es enviar mensajes en un anillo, desde el rank 0 al 1 al 2, y al final, volver al 0. Esta es la solución:

El código de program.cs es:

class Program { static void Main(string[] args) { using (MPI.Environment environment = new MPI.Environment(ref args)) { Intracommunicator comm = MPI.Communicator.world; if (comm.Size < 2) { Console.WriteLine("At least two processes are needed"); return; } Console.WriteLine("I'm {0} of {1}", MPI.Communicator.world.Rank, MPI.Communicator.world.Size); if (comm.Rank == 0) // It's the root { string sendmessage = string.Format("Hello from {0} to {1}", comm.Rank, comm.Rank + 1); comm.Send(sendmessage, comm.Rank + 1, 0); string recmessage; comm.Receive<string>(comm.Size - 1, 0, out recmessage); Console.WriteLine("Received: {0}", recmessage); } else { string recmessage; comm.Receive<string>(comm.Rank - 1, 0, out recmessage); Console.WriteLine("Received: {0}", recmessage); string sendmessage = string.Format("Hello from {0} to {1}", comm.Rank, (comm.Rank + 1) % comm.Size); comm.Send(sendmessage, (comm.Rank + 1) % comm.Size, 0); } } } }

Dispersando mensajes

Este es otro ejemplo (la solución MpiNetScatter), donde un arreglo de enteros es enviado a todos los ranks, un entero a cada uno, desde el rank 0:

class Program { static void Main(string[] args) { using (MPI.Environment environment = new MPI.Environment(ref args)) { Intracommunicator comm = Communicator.world; if (comm.Rank == 0) { int [] numbers = new int[comm.Size]; for (int k = 0; k < numbers.Length; k++) numbers[k] = k * k; int r = comm.Scatter(numbers); Console.WriteLine("Received {0} at {1}", r, comm.Rank); } else { int r = comm.Scatter<int>(0); Console.WriteLine("Received {0} at {1}", r, comm.Rank); } } } }

Threads y MPI

Podemos mejorar el ejemplo de anillo, usando una nueva característica de MPI2, soportada por la implementación de Microsoft: enviar y recibir mensajes usando múltiples threads. La solución es MpiNetMultiThreadRing. El código:

class Program { static void Main(string[] args) { using (MPI.Environment environment = new MPI.Environment(ref args)) { Intracommunicator comm = MPI.Communicator.world; if (comm.Size < 2) { Console.WriteLine("At least two processes are needed"); return; } MultiComm multicomm = new MultiComm(MPI.Communicator.world); Thread thread = new Thread(new ThreadStart(multicomm.Run)); thread.Start(); MultiComm multicomm2 = new MultiComm(MPI.Communicator.world); Thread thread2 = new Thread(new ThreadStart(multicomm2.Run)); thread2.Start(); thread.Join(); thread2.Join(); } } }

Escribí una clase ayudante, MultiComm, que tiene métodos para enviar y recibir mensajes. Usa un lock: la implementación MPI no soporta el uso de comandos MPI, desde más de un thread en simultáneo. Entonces, tuve que sincronizar los métodos de acceso a MPI desde diferentes threads. Es un incordio, pero es lo que está soportado (no sé si hay una implementación de MPI2CH que soporte un nivel de multithreads libres).


Conclusiones


MPI implica una nueva manera de pensar aplicaciones. No hay un camino fácil para convertir a MPI un algoritmos o aplicación. Podría jugar un poco más con el pasaje de mensajes de forma asincrónica. En los ejemplos de este post, cuando una instancia envia un mensaje, las otras partes deben estar escuchando para recibir el mensaje. Dejando sus idiosincracias, MPI es un interesante campo para explorar, con una amplia comunidad que hay producido interesantes aplicaciones.


Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

This entry was posted in 1389, 9294, 9409. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>