Optimización de las Comunicaciones Colectivas en Paso

Transcripción

Optimización de las Comunicaciones Colectivas en Paso
FACULTADE DE INFORMÁTICA
UNIVERSIDADE DA CORUÑA
Departamento de Electrónica e Sistemas
Proyecto Fin de Carrera de Ingenierı́a Informática
Optimización de las Comunicaciones Colectivas
en Paso de Mensajes para Java en Sistemas
Multi-core
Autor: Sabela Ramos Garea
Directores: Guillermo López Taboada
Juan Touriño Domı́nguez
A Coruña, 2 de julio de 2009
Especificación
Tı́tulo:
Optimización de las Comunicaciones Colectivas en Paso de Mensajes para Java en Sistemas Multi-core
Clase:
Investigación y Desarrollo
Autor :
Sabela Ramos Garea
Director :
Guillermo López Taboada
Juan Touriño Domı́nguez
Tribunal :
Fecha de lectura:
Calificación:
i
Agradecimientos
Me gustarı́a dar las gracias a todos los que me han apoyado y han contribuido a que
este proyecto haya salido adelante. Desde mis directores hasta mi familia, pasando por
Pablo, Lucı́a, Chus y muchos otros que han estado conmigo cuando lo he necesitado.
Muchas gracias.
iii
Resumen
Este proyecto presenta el análisis, diseño e implementación de una biblioteca de
operaciones colectivas en paso de mensajes para Java sobre sistemas multi-core. El objetivo perseguido es una mejora de la eficiencia de los códigos Java a la hora de explotar
el paralelismo proporcionado por el aumento del número de cores en los microprocesadores actuales y el auge de las arquitecturas clúster. Estos dos últimos factores, unidos
al uso extendido del lenguaje Java, llevan a la necesidad de incrementar el rendimiento,
lo cual es posible mediante la adaptación de los patrones de comunicación en estos
sistemas.
El hecho de utilizar el lenguaje Java no se debe solamente a su amplia difusión,
sino que viene reforzado por la inclusión, en el núcleo del lenguaje, de un completo
soporte multithread y de comunicaciones en red. Por ello, la programación mediante el
paradigma de paso de mensajes, el más utilizado en computación de altas prestaciones
(High Performance Computing, HPC), en Java es una alternativa emergente a la hora
de trabajar en arquitecturas clúster con nodos multi-core. En este escenario la eficiencia
de las operaciones colectivas es crı́tica a la hora de obtener rendimientos escalables en
Java, siendo crucial la optimización de las primitivas colectivas de paso de mensajes.
Para la realización del proyecto, buscando un diseño portable, se han utilizado como
base las primitivas de comunicación punto a punto del estándar de paso de mensajes
en Java, MPJ (Message Passing in Java), y se han desarrollado una serie de algoritmos
con los que se pretende mejorar el rendimiento de las comunicaciones colectivas en sistemas multi-core. La evaluación experimental de la biblioteca desarrollada ha mostrado
aumentos significativos del rendimiento en un clúster multi-core, de hasta dos órdenes
de magnitud, comparados con otras bibliotecas existentes. Además, se proporciona un
v
mecanismo automático de selección de algoritmos, adaptable a distintas configuraciones de número de procesos y de tamaño de mensaje. La portabilidad de la biblioteca
de operaciones colectivas desarrollada se ha puesto de manifiesto con su integración en
MPJ Express, una implementación del estándar MPJ muy extendida, permitiendo su
uso a una amplia comunidad de desarrolladores en Java para paso de mensajes.
Palabras Clave
Java, Paso de Mensajes, Sistemas Multi-core, Clúster, MPJ, Comunicaciones Colectivas.
Índice general
1. Introducción
1
1.1. Motivación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
1.2. Programación mediante Paso de Mensajes en Java . . . . . . . . . . . .
3
1.2.1. Soluciones Wrappers Basadas en JNI . . . . . . . . . . . . . . . .
5
1.2.2. Implementaciones Java Puro (Java 100 %) . . . . . . . . . . . . .
6
1.2.3. Implementaciones Hı́bridas (Comunicaciones Java Puro/Nativas)
7
1.2.4. Extensiones del Lenguaje Java . . . . . . . . . . . . . . . . . . .
8
1.3. Acerca de esta Memoria . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
11
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ . . . . . . . .
11
2.1.1. Descripción de las Operaciones Colectivas . . . . . . . . . . . . .
11
2.2. Diseño de Operaciones Colectivas en MPJ . . . . . . . . . . . . . . . . .
27
2.2.1. Estructura General de las Bibliotecas MPJ . . . . . . . . . . . .
27
2.2.2. Integración de la Biblioteca Desarrollada . . . . . . . . . . . . . .
30
2.2.3. Primitivas Básicas Utilizadas en la Implementación . . . . . . . .
31
3. Implementación de Primitivas Colectivas MPJ
3.1. Algoritmos Implementados
35
. . . . . . . . . . . . . . . . . . . . . . . . .
36
3.1.1. Broadcast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
3.1.2. Gather y Gatherv . . . . . . . . . . . . . . . . . . . . . . . . . .
41
3.1.3. Scatter y Scatterv . . . . . . . . . . . . . . . . . . . . . . . . . .
44
3.1.4. Allgather y Allgatherv . . . . . . . . . . . . . . . . . . . . . . . .
46
vii
ÍNDICE GENERAL
viii
3.1.5. Alltoall y Alltoallv . . . . . . . . . . . . . . . . . . . . . . . . . .
50
3.1.6. Reduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
52
3.1.7. Allreduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
55
3.1.8. Reduce-Scatter . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
3.1.9. Scan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
3.1.10. Barrier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
4. Integración y Optimización en Sistemas Multi-core
63
4.1. MPJ Express como Marco para la Integración . . . . . . . . . . . . . . .
63
4.1.1. Correcciones sobre MPJ Express . . . . . . . . . . . . . . . . . .
64
4.2. Integración y Selección Automática de Algoritmos . . . . . . . . . . . .
66
4.3. Código de Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
4.4. Incremento de la Escalabilidad en Operaciones Colectivas . . . . . . . .
72
5. Evaluación del Rendimiento
75
5.1. Configuración Experimental . . . . . . . . . . . . . . . . . . . . . . . . .
75
5.2. Análisis del Rendimiento de la Biblioteca de Colectivas Implementada .
75
5.3. Análisis Comparativo del Rendimiento de Colectivas MPJ vs. MPI . . .
81
5.4. Resultados con una Aplicación Java HPC (jGadget) . . . . . . . . . . .
85
6. Principales Aportaciones y Conclusiones
87
6.1. Principales Aportaciones . . . . . . . . . . . . . . . . . . . . . . . . . . .
88
6.2. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
89
A. Planificación
91
B. Resultados Experimentales Adicionales
97
Índice de figuras
2.1. Funcionamiento de la colectiva Broadcast . . . . . . . . . . . . . . . . .
13
2.2. Funcionamiento de la colectiva Scatter . . . . . . . . . . . . . . . . . . .
14
2.3. Funcionamiento de la colectiva Gather . . . . . . . . . . . . . . . . . . .
16
2.4. Funcionamiento de la colectiva Allgather . . . . . . . . . . . . . . . . . .
19
2.5. Funcionamiento de la colectiva Reduce . . . . . . . . . . . . . . . . . . .
20
2.6. Funcionamiento de la colectiva Allreduce . . . . . . . . . . . . . . . . . .
22
2.7. Funcionamiento de la colectiva Reduce-scatter . . . . . . . . . . . . . . .
23
2.8. Funcionamiento de la colectiva Alltoall . . . . . . . . . . . . . . . . . . .
24
2.9. Funcionamiento de la colectiva Scan . . . . . . . . . . . . . . . . . . . .
26
2.10. Clases principales en MPJ . . . . . . . . . . . . . . . . . . . . . . . . . .
28
3.1. Pseudocódigo de Broadcast bFT . . . . . . . . . . . . . . . . . . . . . .
36
3.2. Pseudocódigo de Broadcast nbFT . . . . . . . . . . . . . . . . . . . . . .
37
3.3. Funcionamiento de Broadcast en MPJ Express . . . . . . . . . . . . . .
38
3.4. Pseudocódigo de Broadcast con Minimum Spanning Tree (MST) . . . .
39
3.5. Funcionamiento de Broadcast MST . . . . . . . . . . . . . . . . . . . . .
39
3.6. Pseudocódigo de Gather nbFT . . . . . . . . . . . . . . . . . . . . . . .
41
3.7. Pseudocódigo de Gather FT . . . . . . . . . . . . . . . . . . . . . . . . .
42
3.8. Pseudocódigo de Gather con Minimum Spanning Tree (MST) . . . . . .
43
3.9. Funcionamiento de Gather MST . . . . . . . . . . . . . . . . . . . . . .
43
3.10. Pseudocódigo de Scatter nbFT . . . . . . . . . . . . . . . . . . . . . . .
44
3.11. Pseudocódigo de Scatter con Minimum Spanning Tree (MST) . . . . . .
45
3.12. Funcionamiento de Scatter MST . . . . . . . . . . . . . . . . . . . . . .
45
ix
x
ÍNDICE DE FIGURAS
3.13. Pseudocódigo de Allgather nbFT . . . . . . . . . . . . . . . . . . . . . .
46
3.14. Pseudocódigo Allgather BDE . . . . . . . . . . . . . . . . . . . . . . . .
47
3.15. Funcionamiento de Allgather BDE . . . . . . . . . . . . . . . . . . . . .
48
3.16. Pseudocódigo de Allgather BKT . . . . . . . . . . . . . . . . . . . . . .
48
3.17. Funcionamiento de Allgather BKT . . . . . . . . . . . . . . . . . . . . .
49
3.18. Pseudocódigo Alltoall nbFT . . . . . . . . . . . . . . . . . . . . . . . . .
50
3.19. Pseudocódigo de Alltoall bFT . . . . . . . . . . . . . . . . . . . . . . . .
51
3.20. Pseudocódigo de Alltoall nb1FT . . . . . . . . . . . . . . . . . . . . . .
51
3.21. Pseudocódigo de Alltoall nb2FT . . . . . . . . . . . . . . . . . . . . . .
51
3.22. Pseudocódigo de Reduce nbFT . . . . . . . . . . . . . . . . . . . . . . .
53
3.23. Pseudocódigo de Reduce bFT . . . . . . . . . . . . . . . . . . . . . . . .
53
3.24. Pseudocódigo de Reduce con Minimum Spanning Tree (MST) . . . . . .
54
3.25. Funcionamiento de Reduce MST . . . . . . . . . . . . . . . . . . . . . .
54
3.26. Pseudocódigo de Allreduce nbFT . . . . . . . . . . . . . . . . . . . . . .
55
3.27. Pseudocódigo de Allreduce BDE . . . . . . . . . . . . . . . . . . . . . .
56
3.28. Funcionamiento de Allreduce BDE . . . . . . . . . . . . . . . . . . . . .
56
3.29. Pseudocódigo de Reduce-Scatter BDE . . . . . . . . . . . . . . . . . . .
58
3.30. Funcionamiento de Reduce-Scatter BDE . . . . . . . . . . . . . . . . . .
59
3.31. Pseudocódigo de Reduce-Scatter BKT . . . . . . . . . . . . . . . . . . .
59
3.32. Reduce-Scatter BKT . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
60
3.33. Pseudocódigo de Scan nbFT . . . . . . . . . . . . . . . . . . . . . . . . .
61
3.34. Pseudocódigo de Scan Secuencial . . . . . . . . . . . . . . . . . . . . . .
61
3.35. Funcionamiento de la Barrera usando un árbol binario . . . . . . . . . .
62
4.1. Clases principales en MPJ . . . . . . . . . . . . . . . . . . . . . . . . . .
66
5.1. Resultados experimentales de Broadcast con 32 cores . . . . . . . . . . .
77
5.2. Resultados experimentales de Scatter con 32 cores . . . . . . . . . . . .
78
5.3. Resultados experimentales de Allgather con 32 cores . . . . . . . . . . .
79
5.4. Resultados experimentales de Alltoall con 32 cores . . . . . . . . . . . .
79
5.5. Resultados experimentales de Reduce con 32 cores . . . . . . . . . . . .
80
ÍNDICE DE FIGURAS
xi
5.6. Resultados experimentales de Allreduce con 32 cores . . . . . . . . . . .
80
5.7. Comparación MPI y MPJ para Broadcast con 32 cores . . . . . . . . . .
82
5.8. Comparación MPI y MPJ para Scatter con 32 cores . . . . . . . . . . .
82
5.9. Comparación MPI y MPJ para Allgather con 32 cores . . . . . . . . . .
83
5.10. Comparación MPI y MPJ para Alltoall con 32 cores . . . . . . . . . . .
83
5.11. Comparación MPI y MPJ para Reduce con 32 cores . . . . . . . . . . .
84
5.12. Comparación MPI y MPJ para Allreduce con 32 cores . . . . . . . . . .
84
5.13. Resultados obtenidos con la aplicación jGadget . . . . . . . . . . . . . .
86
A.1. Actividades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
93
A.2. Diagrama de Gantt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
94
A.3. Detalle de planificación de realización de colectivas . . . . . . . . . . . .
95
B.1. Comparación de algoritmos para Broadcast con 16 cores . . . . . . . . .
99
B.2. Comparación de algoritmos para Scatter con 16 cores . . . . . . . . . . . 100
B.3. Comparación de algoritmos para Gather con 16 cores . . . . . . . . . . . 101
B.4. Comparación de algoritmos para Allgather con 16 cores . . . . . . . . . 102
B.5. Comparación de algoritmos para Alltoall con 16 cores . . . . . . . . . . 103
B.6. Comparación de algoritmos para Reduce con 16 cores . . . . . . . . . . 104
B.7. Comparación de algoritmos para Reduce-Scatter con 16 cores . . . . . . 105
B.8. Comparación de algoritmos para Allreduce con 16 cores . . . . . . . . . 106
B.9. Comparación de algoritmos para Scan con 16 cores . . . . . . . . . . . . 107
Índice de cuadros
3.1. Algoritmos para primitivas colectivas en bibliotecas MPJ. . . . . . . . .
40
4.1. Ejemplo de Configuración de Algoritmos para las Operaciones Colectivas 70
A.1. Coste estimado de realización del proyecto. . . . . . . . . . . . . . . . .
xiii
92
Capı́tulo 1
Introducción
1.1.
Motivación
La imposibilidad de seguir incrementando el rendimiendo de un procesador monocore, dado su elevado consumo, ha conducido en los últimos años al aumento de capacidad de procesamiento en base al incremento del número de procesadores presentes
en una máquina y/o el número de máquinas que cooperan entre sı́ para llevar a cabo
un trabajo común. Debido al amplio uso de este tipo de arquitecturas multi-core, es
necesario utilizar paradigmas de programación que las tengan en cuenta y sean capaces
de aprovechar sus ventajas. Aparecen ası́ dos aproximaciones, dependiendo de si los
procesadores implicados comparten o no memoria.
En el caso de arquitecturas de múltiples procesadores con memoria compartida,
la programación se basa en la comunicación de los diferentes threads o procesos mediante la lectura de variables compartidas situadas en el espacio global de direcciones.
Además, es necesaria la sincronización externa de los procesos usando semáforos, secciones crı́ticas, etc. Un ejemplo de este paradigma de programación serı́a OpenMP, que
permite, mediante el uso de directivas que se pueden incluir en código fuente Fortran o
C, trabajar con memoria compartida a través del reparto de trabajo entre los distintos
threads y su gestión (creación, destrucción, sincronización, etc).
Cuando hablamos de memoria distribuida, es decir, cuando no hay un espacio global
de direcciones, sino que cada procesador mantiene una memoria local y privada, la co1
2
1. Introducción
municación y sincronización se realiza mediante el intercambio de mensajes entre ellos.
El estándar en este caso es MPI (Message Passing Interface) [1], que proporciona una
serie de llamadas a primitivas de paso de mensajes que se pueden incluı́r en programas
escritos en Fortran o C.
El paradigma de memoria compartida permite una programación más sencilla, mientras que, en memoria distribuida, el programador debe manejar explı́citamente toda las
comunicaciones entre procesadores. Además, en el segundo caso, es necesario un reparto
inicial del trabajo y de los datos, lo que puede implicar replicación de los mismos, ya
que cada procesador sólo puede acceder a su memoria local. Sin embargo, este lı́mite
en el acceso puede ser beneficioso, pues el uso de memoria compartida da lugar a que,
al aumentar el número de procesadores, se incremente el número de accesos a memoria
que entren en conflicto y, por lo tanto, disminuya la escalabilidad. En un modelo de
memoria distribuida, la localidad de los datos proporciona rendimientos más escalables.
Sobre estos dos tipos básicos de modelos de memoria, aparecen variaciones cuyo
objetivo es aunar ventajas e incrementar la eficiencia, como son las arquitecturas de
memoria fı́sicamente distribuida y lógicamente compartida, que pretenden mantener
las ventajas de una arquitectura de memoria distribuida (sobre todo, escalabilidad) a
la vez que proporciona la facilidad de programación de las arquitecturas de memoria
compartida. Sin embargo, en algunos casos, dependiendo de la implementación de esta
solución, es más eficiente utilizar también programación mediante paso de mensajes.
Con la aparición de los procesadores multi-core y su utilización en clusters, resulta de sumo interés combinar la programación paralela a nivel de nodo con memoria
compartida, con la programación mediante paso de mensajes a nivel de cluster. Como
Java implementa los threads de modo nativo, una biblioteca de paso de mensajes sobre
Java resulta de gran utilidad. Además, Java presenta numerosas ventajas frente a alternativas más tradicionales en programación paralela (C y Fortran), como seguridad y
robustez, portabilidad, expresividad, sencillez, gestión automática de memoria, orientación a objetos, etc., lo que le ha llevado a convertirse en una de las plataformas más
extendidas en la actualidad. No obstante, en ámbitos donde el rendimiento es crı́tico,
como en computación de altas prestaciones (High Performance Computing, HPC), no
1.2. Programación mediante Paso de Mensajes en Java
3
es tan popular, aunque su rendimiento se ha ido incrementando significativamente al
pasar de una ejecución interpretada a la compilación a código nativo en tiempo de ejeTM
cución realizada por compiladores JIT (Just-In-Time) y máquinas virtuales HotSpot
.
Ası́, hoy en dı́a Java alcanza rendimientos similares a los del código nativo, siendo una
alternativa competitiva en HPC.
A pesar de que son varias las implementaciones de bibliotecas de paso de mensajes
disponibles para Java [2], cada una incluyendo su propia biblioteca de operaciones colectivas, éstas suelen ofrecer un rendimiento limitado. Esto es debido, especialmente, a
que implementan algoritmos poco escalables y a que no aprovechan las particularidades del sistema sobre el que se ejecutan. La implementación y selección de algoritmos
eficientes para primitivas colectivas en tiempo de ejecución ya fue objeto de estudio
y discusión en bibliotecas nativas [3] [4] [5] [6], pero nunca hasta la fecha en Java. El
objetivo de la biblioteca desarrollada es mejorar la eficiencia de las primitivas colectivas
de paso de mensajes mediante el uso de algoritmos escalables, seleccionables en tiempo
de ejecución, proporcionando mayor eficiencia en operaciones colectivas en Java.
1.2.
Programación mediante Paso de Mensajes en Java
En los lenguajes compilados a código nativo (C o Fortran), MPI es la interfaz
estándar para bibliotecas de paso de mensajes. En cuanto a Java, existen numerosas
bibliotecas de paso de mensajes [2], aunque la mayorı́a ha optado por implementar su
propia API, similar a la de MPI. Las dos propuestas más relevantes para tener un API
estándar son mpiJava 1.2 API [7] y JGF MPJ (Message-Passing interface for Java)
API [8] [9] [10] propuesta por el Java Grande Forum (JGF) [10] para estandarizar un
API similar a MPI en Java. Las principales diferencias entre estas dos APIS aparecen
en las convenciones de nombrado de variables y métodos.
Java es un lenguaje con un desarrollo orientado a las redes de computadores, cosa
bastante obvia si nos fijamos en la posibilidad de programar directamente con sockets
usando el API del lenguaje, la invocación de métodos remotos (RMI) o los métodos
de serialización de objetos, permitiendo el envı́o de los mismos a través de la red y la
carga dinámica de clases.
4
1. Introducción
De los mecanismos presentes en el API de Java, podemos destacar dos, debido
a su importancia en la implementación del paradigma de paso de mensajes, además
de la programación con sockets, que son JNI (Java Native Interface) y RMI (Remote
Method Invocation). JNI, o interfaz nativa de Java, es un mecanismo bidireccional de
conexión entre Java y código nativo. Esto permite que podamos invocar código Java
desde aplicaciones escritas en otros lenguajes como C, o bien, utilizar código nativo
desde un código Java. En su segunda forma de uso, permite, por ejemplo, invocar
código más eficiente en zonas de rendimiento crı́tico. No obstante, hay que tener en
cuenta que puede tener consecuencias sobre la seguridad.
RMI, o la invocación de métodos remotos, es lo que nos permite invocar métodos de
objetos creados en otras máquinas y, por lo tanto, el manejo distribuido de los objetos,
evitando el uso de sockets. Es necesario definir interfaces para indicar cuáles son los
métodos que es posible invocar y el registro del objeto como remoto.
Por lo tanto, para la implementación de soluciones de paso de mensajes en Java
tenemos tres opciones: Java RMI, JNI, o bibliotecas de sockets en Java. Cada solución
presenta sus propias ventajas e inconvenientes. El uso de Java RMI, al ser una aproximación 100 % Java, asegura portabilidad, pero puede no ser la solución más eficiente,
especialmente en presencia de redes de altas prestaciones como Infiniband o Myrinet. En
cuanto al uso de JNI, puede presentar problemas de portabilidad, aunque normalmente
proporciona altos rendimientos. Finalmente, la utilización de sockets en Java requiere
un gran esfuerzo de desarrollo, sobre todo para proporcionar soluciones escalables, pero
permite implementaciones 100 % Java mucho más eficientes que las bibliotecas basadas
en RMI.
A pesar de que la mayorı́a del middleware de comunicaciones existente en Java
está basado en RMI, las bibliotecas de paso de mensajes en Java, buscando mayor
eficiencia, se han decantado, sobre todo en los últimos años, por las otras dos aproximaciones (wrapper y basada en sockets).
1.2. Programación mediante Paso de Mensajes en Java
1.2.1.
5
Soluciones Wrappers Basadas en JNI
JavaPVM [11] ofrece una interfaz similar a PVM e invoca las funciones del mismo
utilizando JNI. El proyecto parece llevar inactivo bastante tiempo (al menos,
desde 1998).
mpiJava [7] [12] [13] [14] es una interfaz Java para MPI desarrollada por el NPAC
(Northeast Parallel Architectures Center) de la Universidad de Syracusa en EEUU.
En la actualidad, forma parte de HPJava, un entorno para programación cientı́fica y orientado al paralelismo de datos que está basado en una versión extendida
de Java. mpiJava está implementado con JNI para acceder al código nativo de
MPI. La última versión incluida en HPJava data del 2003, aunque a partir de
marzo del 2007 se comenzó un proceso de liberación para que la comunidad se
encargue de mejorarla.
La especificación de MPJ, referente para las implementaciones de paso de mensajes en Java, realizada por el Message Passing Working Group (del Java Grande
Forum), se ha basado fuertemente en mpiJava.
JavaMPI [9] [13] es otra implementación que utiliza JNI (o mejor dicho, su predecesor
NMI) sobre MPI. Soportaba LAM-MPI o MPICH. El proyecto fue abandonado
en 1998.
M-JavaMPI [13] es un proyecto no disponible al público. Su objetivo era proveer de
un marco de desarrollo para aplicaciones MPI con tolerancia a fallos y soporte
para balanceo de carga dinámico. Al igual que los anteriores, utiliza código nativo
de MPI, pero su forma de enlace es diferente, ya que se hace mediante demonios
y memoria compartida. Ha dado lugar al proyecto G-JavaMPI para Grid.
JavaWMPI [13] proporciona una interfaz sobre WMPI (implementación de MPI para
Windows).
6
1. Introducción
1.2.2.
Implementaciones Java Puro (Java 100 %)
JPVM [15] presenta una interfaz similar a la que proporciona PVM para C y Fortran.
Aquı́ las comunicaciones se basan en la utilización directa de sockets. El proyecto
fue desarrollado en la Universidad de Virginia y permanece inactivo desde Febrero
de 2000.
P2P-MPI [16] [17] es un proyecto de software libre actualmente activo que sigue
las especificaciones de MPJ. Presenta tolerancia a fallos ya que permite ejecutar
tareas de forma redundante. Puede usarse tanto en clústers como en grids.
JMPI [13] aparece con fines académicos en la Universidad de Massachussets. Cumple
las especificaciones de MPJ con la excepción de que no implementa primitivas
colectivas de reducción. Utiliza RMI y serialización de objetos.
JMPI [13] [18], con el mismo nombre que la anterior, es una aproximación comercial
de MPI Software Technology (actualmente Verari).
MPIJ [13] [9] formó parte del proyecto DOGMA (Distributed Object Group Metacomputing Architecture) hasta la versión 2.0, e implementaba un gran subconjunto de MPJ.
CCJ [13] es un proyecto que incluye varias caracterı́sticas de MPI pero sin seguir la
especificación MPJ en detalle. Diverge de MPJ en que intenta ser “más natural”
para Java, integrándose con los threads y objetos, en lugar de trabajar con arrays.
Utiliza RMI y para mejorar el rendimiento se apoya en el sistema de compilación
de Manta y en Manta RMI. La última versión es del 2001.
PJMPI [13] es una biblioteca que sigue el estándar MPJ. Fue desarrollado en la
Universidad de Adelaida conjuntamente al entorno de paso de mensajes JUMP
que le sirve de base.
jmpi [13] [19] es una implementación ya abandonada que utiliza JPVM.
Parallel Java [20] [21], o PJ, es una API y middleware para programación paralela
1.2. Programación mediante Paso de Mensajes en Java
7
en memoria compartida y/o distribuida desarrollado en el Rochester Institute of
Technology como un proyecto de software libre.
Jcluster [22] [23] proporciona un entorno de programación paralela en Java. Balancea
la carga de forma automática con un algoritmo aleatorio y presenta interfaces de
paso de mensajes similares a PVM y MPI. Permite el uso de múltiples nodos y
utiliza UDP como protocolo de comunicación.
MPJava [24], diseñado en la Universidad de Maryland, proporciona una interfaz
basada en MPJ que utiliza las capacidades de I/O mejoradas usando el paquete
java.nio (Java New I/O).
JOPI [25] [26] (Java Object-Passing Interface) proporciona un interfaz similar a MPI
para intercambiar objetos entre procesos.
1.2.3.
Implementaciones Hı́bridas (Comunicaciones Java Puro/Nativas)
Este tipo de implementaciones pueden ser usadas con comunicaciones Java puro o
delegando en implementaciones nativas.
MPJ Express [27] [28] es una implementación MPJ desarrollada en la Universidad
de Reading. Utiliza Java NIO y Myrinet en comunicaciones, con la posibilidad de
cambiar el protocolo de comunicación en tiempo de ejecución. La implementación
inicial se basaba en mpiJava, con su misma API. El soporte para el API de MPJ
se proporcionó en versiones posteriores.
MPJ/Ibis [29] es parte del proyecto Ibis, un proyecto de software libre de la Vrije
Universiteit de Amsterdam, cuyo objetivo es crear una plataforma eficiente para
la computación distribuida. MPJ/Ibis presenta una implementación de MPJ en
Java usando como base la capa de comunicaciones IPL (Ibis Portability Layer). Al
igual que MPJ Express, presenta la posibilidad de delegar en implementaciones
nativas MPI (actualmente compatible con GM para redes Myrinet).
F-MPJ (Fast MPJ) [30] es un proyecto del Grupo de Arquitectura de Computadores
de la Universidad de A Coruña para cubrir la necesidad de una biblioteca de
comunicaciones no bloqueantes eficientes en Java. Implementa un subconjunto de
8
1. Introducción
MPJ utilizando sockets y puede evitar la serialización si utiliza Java Fast Sockets
(JFS) [31].
1.2.4.
Extensiones del Lenguaje Java
Hay otro tipo de proyectos que no pretenden desarrollar una interfaz de paso de
mensajes en Java, sino extender el lenguaje para su utilización en computación de
altas prestaciones. Entre estos, podrı́amos citar JavaNOW [32] [33], que presenta un
entorno de trabajo en redes de ordenadores y que provee primitivas de comunicaciones
colectivas, multithreading, etc. Los mismos objetivos se buscan con el entorno IceT
[34], de la Emory University de Atlanta, que pretende explotar la portabilidad y las
ventajas de Java añadiendo técnicas de computación concurrente y distribuida.
Otro de estos proyectos es JavaParty [35], que extiende las capacidades de Java
para utilizarlo en entornos de computación distribuida utilizando clases remotas sin
RMI. El entorno JavaParty podrı́a ser visto como una JVM (Java Virtual Machine)
distribuida.
El gran número de proyectos realizados (y en actual desarrollo) en Java para paso
de mensajes, hace patente el interés en este paradigma de programación en Java. Sin
embargo, una caracterı́stica común a todos ellos es que el objetivo fundamental es proporcionar una interfaz de paso de mensajes relegando la optimización del rendimiento
a un segundo plano. Por esta razón, este proyecto se centrará en el desarrollo de una
biblioteca de primitivas colectivas de paso de mensajes en Java utilizando algoritmos
eficientes y escalables, buscando además la portabilidad de las mismas, es decir, que
sean utilizables en el mayor número de bibliotecas de paso de mensajes existentes,
siempre y cuando sigan el estándar MPJ del Java Grande Forum [8] [9] [10].
1.3.
Acerca de esta Memoria
Esta memoria se compone de los siguientes apartados:
1.3. Acerca de esta Memoria
9
Introducción : Breve descripción de los objetivos, contextualización del trabajo y
motivación.
Análisis y Diseño de la Biblioteca de Colectivas MPJ : Análisis detallado del
estándar MPJ en cuanto a primitivas colectivas, primitivas punto a punto necesarias en la implementación y estructura de una biblioteca MPJ completa. A
continuación, se describe el diseño de la integración de la biblioteca de primitivas
basada en el estándar.
Implementación de Primitivas Colectivas MPJ : Definición y discusión de los
algoritmos implementados para cada función colectiva.
Integración y Optimización en Sistemas Multi-core : Descripción de cómo se
ha llevado a cabo la integración de las funciones colectivas en una biblioteca
MPJ existente, optimización de comunicaciones en clusters multi-core y selección
de algoritmos en tiempo de ejecución.
Evaluación del Rendimiento : Benchmarking de la biblioteca y análisis de los resultados obtenidos en comparación con MPI y con una implementación MPJ
existente. Rendimiento obtenido con una aplicación Java HPC utilizada en Cosmologı́a (jGadget).
Principales Aportaciones y Conclusiones : Recopilación de las novedades más relevantes que aporta este proyecto y conclusiones.
Anexo: Planificación y Memoria Económica : Planificación temporal y estimación del coste del trabajo.
Anexo: Resultados Experimentales Adicionales : Resultados previos obtenidos
en el computador Finis Terrae del Cesga.
Capı́tulo 2
Análisis y Diseño de la Biblioteca
de Colectivas MPJ
2.1.
Análisis de Requisitos en Operaciones Colectivas en
MPJ
En las bibliotecas de paso de mensajes, además de las funciones básicas de comunicación punto a punto, es decir, envı́os y recepciones que implican únicamente a dos
procesos (emisor y receptor), existen una serie de funciones que permiten englobar en
una llamada patrones de comunicación complejos. Estas operaciones, conocidas como
“colectivas”, implementan casos habituales de intercambio de información entre más
de dos procesos. Ya que la eficiencia que presentan estas funciones en las bibliotecas
actuales de MPJ es limitada y su uso es muy frecuente, nos centraremos en su optimización. Además, debido a que el estándar de operaciones colectivas en paso de mensajes
es ampliamente utilizado y satisface las necesidades de los desarrolladores, se estima
que no es necesaria una extensión de su funcionalidad.
2.1.1.
Descripción de las Operaciones Colectivas
En primer lugar, veremos una descripción de las funciones colectivas implementadas, tanto de relocalización o movimiento de datos (Broadcast, Scatter, Scatterv, Gather,
11
12
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
Gatherv, Allgather, Allgatherv, Alltoall y Alltoallv), como de computación o reducción
(Reduce, Allreduce, Reduce-scatter y Scan). Las primeras se limitan a realizar la transferencia de datos entre procesos mientras que las segundas aplican una determinada
operación a los datos una vez que se ha realizado la oportuna relocalización de los
mismos. Finalmente, se incluye la primitiva colectiva Barrier que realiza una sincronización de procesos. Más adelante, analizaremos diferentes algoritmos para cada una de
ellas.
Las primitivas colectivas se utilizan dentro de un contexto formado por varios procesos. Este contexto recibe el nombre de Comunicador. El Comunicador representa un
grupo de procesos que intercambian información. La forma de indicar cuál es el Comunicador en el que se va a realizar la operación es expresar la primitiva colectiva como
método del Comunicador a utilizar. Es decir, suponiendo que nuestro Comunicador es
Comm, la llamada a una barrera serı́a de la forma Comm.Barrier().
Cada descripción viene acompañada de un dibujo explicativo, como el de la Figura
2.1, excepto en la colectiva Barrier, puesto que su funcionamiento es muy intuitivo. En
cada diagrama, los cuadros de colores dispuestos en vertical representan las posiciones
de un vector. Los cuadros dispuestos en fila dentro de un mismo procesador (Pi ) representan una misma posición del vector resultado de aplicar una operación de reducción
sobre esos datos (como ejemplo, ver Figura 2.5).
También aparece, con cada colectiva, la firma del método tal y como se especifica
en el estándar MPJ [9] junto a la descripción de cada atributo y de su funcionamiento.
Esta descripción es una aportación del presente proyecto, buscando proporcionar una
adecuada documentación para la biblioteca desarrollada. Se ha redactado en inglés
debido a que se ha integrado en una implementación de MPJ (MPJ Express) con
amplia difusión internacional.
Broadcast : implementa el envı́o de un mensaje desde un proceso raı́z al resto de
procesos. Al terminar, todos los procesos poseen una copia de ese mismo mensaje.
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ
13
Figura 2.1: Funcionamiento de la colectiva Broadcast
La documentación que se ha redactado para ser incluida en la biblioteca desarrollada es la siguiente:
public void Bcast( java.lang.Object
mpi.Datatype
datatype, int
buf, int
offset, int
count,
root )
Usage
• It broadcasts a message from the process with rank root to all processes
of the group, itself included. It is called by all members of the group using
the same arguments. On return, the contents of root’s communication
buffer have been copied to all processes. The type signature of count,
datatype on any process must be equal to the type signature of count,
datatype at the root.
buf
inout buffer array
offset
initial offset in buffer
count
number of items in buffer
datatype
datatype of each item in buffer
root
rank of broadcast root
Scatter : al igual que la anterior, el emisor es un único proceso, llamado raı́z, y todos los
demás actúan de receptores. La diferencia estriba en que, en el caso del Broadcast,
todos reciben el mismo mensaje; mientras que, en el Scatter, a cada proceso
llega un fragmento del mensaje enviado. Los fragmentos son enviados por orden,
de forma que el n-ésimo proceso recibe el n-ésimo trozo de mensaje. Hay una
variante (Scatterv) en la que el tamaño del mensaje enviado a cada nodo puede
14
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
ser diferente. Además, en esta variante, se permite especificar el desplazamiento
de cada fragmento con respecto a la posición del primer elemento del mensaje.
Figura 2.2: Funcionamiento de la colectiva Scatter
Las firmas y documentación de los métodos se describen a continuación.
public void Scatter( java.lang.Object
int
sendcount, mpi.Datatype
int
recvoffset, int
sendbuf, int
sendoffset,
sendtype, java.lang.Object
recvcount, mpi.Datatype
recvbuf,
recvtype, int
root )
Usage
• Scatter is the inverse of the operation Gather . It is similar to Bcast but
every process receives different data. The root sends a message which
is split into n equal segments and the ith segment is received by the
ith process in the group. The type signature associated with sendcount,
sendtype at the root must be equal to the type signature associated
with recvcount, recvtype at all processes. The argument root must have
identical values on all processes.
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ
15
sendbuf
send buffer array (significant only at root)
sendoffset
initial offset in send buffer
sendcount
number of items to send to each process (significant only at
root
sendtype
datatype of each item in send buffer (significant only at root)
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
recvcount
number of items to receive
recvtype
datatype of each item in receive buffer
root
rank of sending process
public void Scatterv( java.lang.Object
sendbuf, int
int [] sendcount, int [] displs, mpi.Datatype
java.lang.Object
mpi.Datatype
recvbuf, int
recvtype, int
recvoffset, int
sendoffset,
sendtype,
recvcount,
root )
Usage
• Scatterv is the inverse of the operation Gatherv. It extends the operation Scatter by allowing sending different counts of data to each
process, since sendcounts is now an array. It also allows more flexibility
as to where the data is taken from on the root, by providing a new
argument, displs. The type signature implied by sendcount[i], sendtype
at the root must be equal to the type signature implied by recvcount,
recvtype at process i.
16
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
sendbuf
send buffer array (significant only at root)
sendoffset
initial offset in send buffer
sendcounts
number of items to send to each process
displs
displacements from which to take outgoing data to each process
sendtype
datatype of each item in send buffer
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
recvcount
number of items to receive
recvtype
datatype of each item in receive buffer
root
rank of sending process
Gather : es la operación inversa a Scatter. Todos los procesos envı́an a un único
receptor o raı́z. Este proceso raı́z recoge los mensajes que le envı́a cada uno de
los demás y compone un único objeto destino ordenando los mensajes recibidos
según el número o rango del procesador emisor. Al igual que para la primitiva
anterior, existe un Gatherv en que los mensajes pueden ser de diverso tamaño y
es posible especificar el desplazamiento, con respecto al inicio del mensaje, para
indicar la posición que ocupará la información recibida dentro del objeto destino.
Figura 2.3: Funcionamiento de la colectiva Gather
Aquı́ se pueden ver las firmas de los métodos con la documentación redactada
para la biblioteca desarrollada:
public void Gather( java.lang.Object
int
sendcount, mpi.Datatype
int
recvoffset, int
sendbuf, int
sendoffset,
sendtype, java.lang.Object
recvcount, mpi.Datatype
recvbuf,
recvtype, int
root )
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ
17
Usage
• Each process (root process included) sends the contents of its send buffer to the root process, which receives these contents in its recv-buffer.
The messages are concatenated in rank order. The type signature of
sendcount, sendtype on any process must be equal to the type signature
of recvcount, recvtype at the root.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
sendcount
number of items to send
sendtype
datatype of each item in send buffer
recvbuf
receive buffer array (significant only at root)
recvoffset
initial offset in receive buffer (significant only at root)
recvcount
number of items to receive from each process
recvtype
datatype of each item in receive buffer
root
rank of receiving process
public void Gatherv( java.lang.Object
sendbuf, int
int
sendcount, mpi.Datatype
int
recvoffset, int [] recvcount, int [] displs,
mpi.Datatype
recvtype, int
sendoffset,
sendtype, java.lang.Object
recvbuf,
root )
Usage
• It extends the functionality of Gather by allowing varying counts of data from each process. It also allows more flexibility as to where the data
is placed on the root, by providing a new argument, displs. Messages
are placed in the receive buffer of the root process in rank order.
18
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
sendbuf
send buffer array
sendoffset
initial offset in send buffer
sendcount
number of items to send
sendtype
datatype of each item in send buffer
recvbuf
receive buffer array (significant only at root)
recvoffset
initial offset in receive buffer (significant only at root)
recvcount
number of elements received from each process (significant
only at root)
displs
displacements at which to place incoming data from each
process (significant only at root)
recvtype
datatype of each item in receive buffer (significant only at
root)
root
rank of receiving process
The size of arrays recvcounts and displs should be the size of the
group. Entry i of displs specifies the displacement relative to element
recvoffset of recvbuf at which to place incoming data. sendtype and
recvtype must be the same.
Allgather : representa una variante de la función anterior en la que el resultado del
Gather aparece en todos los procesos. Serı́a lo equivalente a realizar un Gather
seguido de un Broadcast usando en ambos el mismo proceso raı́z. Al final, todos
los participantes obtienen el mismo mensaje, resultado de unir las informaciones
enviadas por cada uno de ellos. También presenta una variante, Allgatherv, en la
que los tamaños de los mensajes enviados por cada proceso pueden variar y se
puede especificar el desplazamiento de cada fragmento con respecto al inicio del
mensaje final.
Esta es la documentación que se ha redactado en este proyecto para la biblioteca
desarrollada:
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ
19
Figura 2.4: Funcionamiento de la colectiva Allgather
public void Allgather( java.lang.Object
int
sendcount, mpi.Datatype
int
recvoffset, int
sendbuf, int
sendoffset,
sendtype, java.lang.Object
recvcount, mpi.Datatype
recvbuf,
recvtype )
Usage
• It is similar to Gather, but all processes receive the result. The block of
data sent from the process jth is received by every process and placed in
the jth block of the buffer recvbuf. The type signature associated with
sendcount, sendtype, at a process must be equal to the type signature
associated with recvcount, recvtype at any other process.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
sendcount
number of items to send
sendtype
datatype of each item in send buffer
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
recvcount
number of items to receive from each process
recvtype
datatype of each item in receive buffer
public void Allgatherv( java.lang.Object
sendbuf, int
sendoffset,
int
sendcount, mpi.Datatype
int
recvoffset, int [] recvcount, int [] displs, mpi.Datatype recvty-
pe )
sendtype, java.lang.Object
recvbuf,
20
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
Usage
• It is similar to Gatherv, but all processes receive the result. The block of
data sent from jth process is received by every process and placed in the
jth block of the buffer recvbuf. These blocks need not all be the same
size. The type signature associated with sendcount, sendtype, at process j must be equal to the type signature associated with recvcounts[j],
recvtype at any other process.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
sendcount
number of items to send
sendtype
datatype of each item in send buffer
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
recvcounts
number of received elements from each process
displs
displacements at which to place incoming data
recvtype
datatype of each item in receive buffer
Reduce : todos los procesos envı́an un mensaje al proceso raı́z, pero éste no se limita
a recibirlos, sino que el resultado final es obtenido mediante la aplicación una
operación de reducción sobre todos los mensajes recibidos. La función de reducción
puede estar predefinida o ser implementada por el usuario.
Figura 2.5: Funcionamiento de la colectiva Reduce
Se muestra a continuación la firma del método descrito acompañado de la documentación que ha sido redactada para la biblioteca desarrollada:
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ
public void Reduce( java.lang.Object
java.lang.Object
mpi.Datatype
recvbuf, int
datatype, mpi.Op
21
sendbuf, int
recvoffset, int
op, int
sendoffset,
count,
root )
Usage
• It combines elements in input buffer of each process using the reduce
operation, and it returns the combined value in the output buffer of the
root process. Arguments count, datatype, op and root must be the same in all processes. Input and output buffers have the same length and
elements of the same type. Each process can provide one element, or a
sequence of elements, in which case the combine operation is executed
element-wise on each entry of the sequence.
.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
recvbuf
receive buffer array (significant only at root)
recvoffset
initial offset in receive buffer
count
number of items in send buffer
datatype
data type of each item in send buffer
op
reduce operation
root
rank of root process
op can be a predefined operation or a user-defined operation. The predefined operations are available in Java as MPI.MAX, MPI.MIN, MPI.SUM,
MPI.PROD, MPI.LAND, MPI.BAND, MPI.LOR, MPI.BOR, MPI.LXOR, MPI.BXOR,
MPI.MINLOC and MPI.MAXLOC. The operation is always assumed to be
associative. The datatype must be compatible with op.
Allreduce : en este caso, el resultado de la reducción aparece, no sólo en un proceso,
sino en todos. El resultado final debe ser el mismo en todos los procesos. Serı́a
equivalente a la realización de un Reduce seguido de un Broadcast del resultado.
22
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
Las operaciones de reducción aplicables son las mismas que en la función anterior.
Figura 2.6: Funcionamiento de la colectiva Allreduce
La firma y la documentación redactada para la biblioteca desarrollada, son los
siguientes:
public void Allreduce( java.lang.Object
java.lang.Object
mpi.Datatype
recvbuf, int
datatype, mpi.Op
sendbuf, int
recvoffset, int
sendoffset,
count,
op )
Usage
• Same as Reduce except that the result appears in the receive buffer of
all processes in the group. All processes must receive identical results.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
count
number of items in send buffer
datatype
data type of each item in send buffer
op
reduce operation
Reduce-scatter : con esta colectiva, el resultado de la reducción aparece repartido
entre los procesos. Como su nombre indica, tiene el mismo efecto que realizar
primero un Reduce y luego un Scatterv del resultado.
A continuación, se incluye la firma del método y la documentación:
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ
23
Figura 2.7: Funcionamiento de la colectiva Reduce-scatter
public void Reduce scatter( java.lang.Object
int
sendoffset, java.lang.Object
int [] recvcounts, mpi.Datatype
sendbuf,
recvbuf, int
recvoffset,
datatype, mpi.Op
op )
Usage
• It combines elements in input buffer of each process using the reduce
operation, and scatters the combined values over the output buffers of
the processes. The ith segment in result vector is sent to process with
rank i and stored in the receive buffer defined by recvbuf, recvcounts[i]
and datatype.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
recvcounts
numbers of result elements distributed to each process. Array
must be identical on all calling processes.
datatype
datatype of each item in send buffer
op
reduce operation
Alltoall : los datos se envı́an “desde todos y hacia todos”. Podrı́a considerarse como
una serie de Scatters desde cada uno de los procesadores. Existe también la colectiva Alltoallv en que los mensajes enviados pueden ser de diferentes tamaños
y se puede especificar un desplazamiento con respecto a la posición inicial del
24
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
mensaje, tanto para los fragmentos enviados como para los recibidos.
Figura 2.8: Funcionamiento de la colectiva Alltoall
Éstas son las firmas de los métodos descritos, acompañados de la documentación
que se incluye en la biblioteca desarrollada:
public void Alltoall( java.lang.Object
int
sendcount, mpi.Datatype
int
recvoffset, int
sendbuf, int
sendoffset,
sendtype, java.lang.Object
recvcount, mpi.Datatype
recvbuf,
recvtype )
Usage
• Extension of Allgather to the case where each process sends distinct
data to each of the receivers.The jth block sent from process i is received
by process j and is placed in the ith block of recvbuf. The type signature
associated with sendcount, sendtype, at a process must be equal to the
type signature associated with recvcount, recvtype at any other process.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
sendcount
number of items sent to each process
sendtype
datatype send buffer items
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
recvcount
number of items received from any process
recvtype
datatype of receive buffer items
2.1. Análisis de Requisitos en Operaciones Colectivas en MPJ
public void Alltoallv( java.lang.Object
sendbuf, int
int [] sendcounts, int [] sdispls, mpi.Datatype
java.lang.Object
recvbuf, int
int [] rdispls, mpi.Datatype
25
sendoffset,
sendtype,
recvoffset, int [] recvcounts,
recvtype )
Usage
• It adds flexibility to Alltoall: location of data for send is specified
by sdispls and location to place data on receive side is specified by
rdispls. The jth block sent from process i is received by process j and
is placed in the ith block of recvbuf. These blocks need not all have the
same size. The type signature associated with sendcount[j], sendtype
at process i must be equal to the type signature associated with recvcount[i], recvtype at process j.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
sendcounts
number of items sent to each process
sdispls
displacements from which to take outgoing data. Entry j specifies the displacement from which to take the outgoing data
destined for process j
sendtype
datatype send buffer items
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
recvcounts
number of elements received from each process
rdispls
displacements at which to place incoming data. Entry i specifies the displacement at which to place the incoming data
from process i
recvtype
datatype of each item in receive buffer
26
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
Scan : realiza una reducción de prefijo paralelo. Es decir, en cada proceso, aparecerá el
resultado de aplicar la operación de reducción a los mensajes enviados desde el
primer proceso hasta él mismo (incluido).
Figura 2.9: Funcionamiento de la colectiva Scan
La firma del método y la documentación redactada para la biblioteca desarrollada
se describen a continuación:
public void Scan( java.lang.Object
java.lang.Object
mpi.Datatype
recvbuf, int
datatype, mpi.Op
sendbuf, int
recvoffset, int
sendoffset,
count,
op )
Usage
• It performs a prefix reduction on data distributed across the group. The
operation returns, in the recvbuf of the process with rank i, the reduction
of the values in the send buffers of processes with ranks 0,...,i (inclusive). Operations supported, semantics and constraints of arguments are
as for Reduce.
sendbuf
send buffer array
sendoffset
initial offset in send buffer
recvbuf
receive buffer array
recvoffset
initial offset in receive buffer
count
number of items in input buffer
datatype
data type of each item in input buffer
op
reduce operation
2.2. Diseño de Operaciones Colectivas en MPJ
27
Barrier : implementa una sincronización entre todos los procesos implicados, los cuales, a medida que llegan a la barrera, esperan hasta que todos los demás hayan
alcanzado ese punto. Una vez que esto ocurre, continuan su ejecución.
A continuación, se pueden observar la firma y la documentación incluidas en la
biblioteca desarrollada:
public void Barrier( )
Usage
• A call to Barrier blocks the caller until all processes in the group have
called it. The call returns at any process only after all group members
have entered the call.
This function can be used when a synchronization of all processes is
needed.
2.2.
Diseño de Operaciones Colectivas en MPJ
Una vez visto cómo funciona cada una de las funciones a implementar y su descripción en el estándar a través de su firma, es el momento de diseñar dicha implementación.
2.2.1.
Estructura General de las Bibliotecas MPJ
El API de MPJ se basa claramente en el del estándar MPI de C y Fortran. No obstante, MPJ sigue un enfoque orientado a objetos para capturar mejor las caracterı́sticas
del lenguaje. Para ello, la especificación se basa en el estándar de MPI-2 para C++, en
el que se definen ciertas jerarquı́as y aparecen determinadas funciones de librerı́a como
métodos de clases. A pesar de haber buscado un diseño con cierta orientación a objetos,
hay algunas clases que engloban las principales funcionalidades, y el diseño orientado
a objetos sólo se centra en separar tipos de datos, operaciones y conceptos diferentes
que, en un enfoque procedimental, se dividirı́an en módulos. Esto es ası́ porque con
MPJ se pretende obtener, ante todo, una implementación eficiente cuyo rendimiento
se verı́a mermado con una mayor profusión de objetos con menores funcionalidades y
28
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
delegación entre ellos. Por lo tanto, se intenta mantener cierta eficiencia en detrimento
de un diseño más elaborado.
mpj
MPJ
Group
Datatype
Comm
Intracomm
Cartcomm
Status
Intercomm
Request
Op
Prequest
Graphcomm
UserFunction
Figura 2.10: Clases principales en MPJ
En la Figura 2.10 podemos ver las principales clases que componen el estándar
MPJ. En algunas implementaciones, como por ejemplo, MPJ Express [27] [28], tanto
el paquete como la clase MPJ, reciben el nombre MPI. Esta clase MPJ contiene sólo
variables estáticas de configuración. Entre ellas, una instancia de un Communicator que
constituye un marco general de comunicaciones para una configuración de procesos por
defecto, más conocido como “COMM WORLD”, ya que contiene a todos los procesos
MPJ existentes.
La clase Datatype define los diferentes tipos de datos encapsulados en las comunicaciones. Los elementos enviados entre procesos deben ser de tipos primitivos o sus
contrapartidas objetuales, o bien objetos serializables. Datatype indicará un tipo MPIcompatible consistente con el tipo de los elementos del mensaje. Los tipos básicos MPIcompatibles incluidos en el API de MPJ, son: MPJ.BYTE, MPJ.CHAR, MPJ.SHORT,
MPJ.BOOLEAN, MPJ.INT, MPJ.LONG, MPJ.FLOAT, MPJ.DOUBLE y MPJ.OBJECT.
También ofrece métodos para crear tipos derivados.
La parte más importante y compleja es la que atañe a la jerarquı́a de comunicadores, ya que es donde realmente se implementa el paso de mensajes. En la superclase
“Comm” se definen las comunicaciones básicas punto a punto, mientras que en las
2.2. Diseño de Operaciones Colectivas en MPJ
29
subclases, aparecen comunicaciones más complejas y/o especı́ficas. Todas las comunicaciones se basan en el intercambio de mensajes entre procesos que forman parte de un
mismo Comunicador. Asimismo, presenta utilidades para diferentes empaquetamientos
de datos en el envı́o. Los grupos de procesos se definen en la clase Group.
La subclase Intracomm es la que contiene las primitivas colectivas definidas anteriormente, con lo cual, es en la que se centra el presente proyecto ya que representa
comunicaciones entre procesos del mismo grupo. Entre estas operaciones se encuentran
las que realizan operaciones de reducción. Para este tipo de operaciones se utiliza la
clase Op. Esta clase presenta diferentes extensiones que se dividen en dos grupos: las
operaciones predefinidas y las operaciones de usuario. Las operaciones disponibles en
Java son: MPJ.SUM, MPJ.PROD, MPJ.MIN, MPJ.MAX, MPJ.BOR, MPJ.BAND,
MPJ.BXOR, MPJ.LOR, MPJ.LAND, MPJ.LXOR, MPJ.MINLOC y MPJ.MAXLOC.
Estas dos últimas (MINLOC y MAXLOC) se modelan de la misma manera que en
Fortran, por ello, aparecen otros tipos predefinidos que son MPI.SHORT2, MPJ.INT2,
MPJ.LONG2, MPJ.FLOAT2 y MPJ.DOUBLE2. Para las operaciones de usuario, existe un método en Op que permite ligar una función, que tendrá que extender la clase
abstracta UserFunction, a un objeto de este tipo. Graphcomm y Cartcomm ofrecen
funciones especı́ficas para comunicadores con topologı́as en grafo y cartesianas respectivamente. La subclase Intercomm se utiliza para intercambio de información entre
procesos de distintos grupos.
Status y Request se usan para encapsular el estado de una operación de intercambio
de mensajes. La primera de ellas es la que representa realmente el estado. La segunda,
como se ha visto en el apartado anterior, permite, en operaciones en las que el proceso
no se queda bloqueado esperando su finalización, comprobar que sus envı́os/recepciones
han terminado y, si es necesario, suspender la ejecución hasta que eso ocurra. La subclase Prequest se utiliza en comunicaciones persistentes.
Para el tratamiento de las excepciones, se proporciona una clase MPJException,
que hereda de la clase estándar Exception. También existen subclases para representar
los códigos de error de MPI.
30
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
2.2.2.
Integración de la Biblioteca Desarrollada
La biblioteca de primitivas colectivas, siguiendo el diseño expuesto en el estándar,
ha de integrarse en la clase Intracomm. Una de las aportaciones de este trabajo, es la posibilidad de seleccionar diferentes algoritmos en tiempo de ejecución escogiendo el más
adecuado para una determinada situación. Para ello, podemos incorporar a la interfaz
pública de Intracomm una serie de métodos que implementen los diferentes algoritmos.
También podrı́amos añadir una subclase con los nuevos métodos. Sin embargo, la aproximación que se acerca más al estándar, que interfiere menos en la jerarquı́a y que es
la implementada en este proyecto, es la que consiste en incorporar los métodos a la
clase Intracomm y hacer que el método público ofrecido por la interfaz definida en el
estándar sea el que elija el método a ejecutar. Para ello, podemos configurar una serie
de variables de entorno o ficheros de configuración que indiquen el algoritmo a escoger
en cada caso.
Además, se habilita el soporte para la selección automática del algoritmo a utilizar,
lo que permite despreocuparse de la configuración y evitar el control de la especificación
de los algoritmos usados. No obstante, esto no impide el control exhaustivo del algoritmo
a ejecutar, facilitando la tarea de pruebas de los algoritmos. Para ello, el enfoque que se
ha seguido es mantener una serie de variables estáticas que indicarán un algoritmo por
defecto para cada colectiva. Estas variables son configurables por el usuario, que puede
ası́ decidir cuál será el algoritmo a ejecutar en cada caso. Por otro lado, otra variable
estática permite decidir si la selección del algoritmo en cada colectiva será automática
o manual.
En el caso de que queramos tener una selección automática, tendremos que configurar unos parámetros que indiquen qué algoritmo escoger en cada caso. Los parámetros
más adecuados serı́an el tamaño de mensaje y el número de procesadores involucrados. El caso ideal serı́a tener unas clases de pruebas que permitan crear un fichero de
configuración automáticamente para ser utilizado más adelante en las ejecuciones. El
desarrollo de este soporte es una de las lı́neas de investigación que se podrı́an seguir en
base al presente proyecto.
2.2. Diseño de Operaciones Colectivas en MPJ
2.2.3.
31
Primitivas Básicas Utilizadas en la Implementación
Uno de los objetivos más relevantes del diseño a realizar es la portabilidad. Ası́,
nuestra implementación se basará en primitivas punto a punto del estándar MPJ. Otro
objetivo importante del diseño es incrementar la escalabilidad de la solución mediante
el aprovechamiento de operaciones no bloqueantes. Las primitivas punto a punto del
estándar MPJ comprenden envı́os y recepciones tanto bloqueantes (send/recv) como no
bloqueantes (isend/irecv). En las primeras, el proceso que llama a la función se queda
suspendido esperando a que ésta se lleve a cabo. Por el contrario, en las operaciones no
bloqueantes, el proceso emisor/receptor continúa su ejecución tras realizar la llamada
a la función de envı́o o recepción. Para comprobar que se ha ejecutado correctamente,
existe una función Wait que suspende la ejecución del proceso hasta que se completa
el envı́o o recepción del mensaje según proceda. Con este tipo de primitivas, es posible
solapar la ejecución de tareas que no se ven afectadas por el intercambio de mensajes,
tanto si son tareas de computación como de comunicación. La firma de estos métodos
es la siguiente [9]:1
public void Comm.Send (java.lang.Object
Datatype
datatype, int
dest, int
buf, int
send buffer array
offset
initial offset in send buffer
count
number of items in send buffer
datatype
data type of each item in input buffer
dest
rank of destination
tag
message tag
public Status Comm.Recv (java.lang.Object
1
datatype, int
count,
tag)
buf
Datatype
offset, int
source, int
buf, int
offset, int
count,
tag)
En todos ellos, se hace referencia a un objeto de tipo Comm que se refiere a un grupo de procesos
que intercambian información, es decir, un Comunicador
32
2. Análisis y Diseño de la Biblioteca de Colectivas MPJ
buf
receive buffer array
offset
initial offset in receive buffer
count
number of items in receive buffer
datatype
data type of each item in input buffer
source
rank of source
tag
message tag
returns
status object
La primitiva de recepción, devuelve un objeto de tipo Status que encapsula el estado
de una operación de este tipo.
public Request Comm.Isend (java.lang.Object
Datatype
datatype, int
dest, int
buf, int
send buffer array
offset
initial offset in send buffer
count
number of items in send buffer
datatype
data type of each item in input buffer
dest
rank of destination
tag
message tag
returns
communication request
public Request Comm.Irecv (java.lang.Object
datatype, int
count,
offset, int
count,
tag)
buf
Datatype
offset, int
source, int
buf, int
tag)
buf
receive buffer array
offset
initial offset in receive buffer
count
number of items in receive buffer
datatype
data type of each item in input buffer
source
rank of source
tag
message tag
returns
communication request
Las primitivas no bloqueantes devuelven un objeto Request asociado a la operación.
2.2. Diseño de Operaciones Colectivas en MPJ
33
Este objeto presenta los métodos Wait() y Test(), que devuelven otro objeto de tipo
Status indicando si la operación ha finalizado con éxito. La primera de las funciones,
además, deja al proceso inactivo (evitando el consumo de recursos como tiempo de
CPU) hasta que se completa la función a la que está asociada.
Además de este tipo de funciones, también se utilizan otras definidas en el estándar
como la que permite obtener el número de procesos involucrados en una comunicación
(public int Comm.Size()), es decir, el número de procesos que integran un Comunicador;
o la que obtiene el rango o identificador de un proceso determinado dentro del grupo
(public int Comm.Rank()).
Capı́tulo 3
Implementación de Primitivas
Colectivas MPJ
Una vez analizadas las necesidades a resolver por una biblioteca de operaciones
colectivas en paso de mensajes en Java, y diseñadas las soluciones a aplicar, puede acometerse la fase de implementación. Para cada primitiva colectiva se han implementado
y analizado diferentes algoritmos de modo que sea posible escoger el más conveniente
en función de las caracterı́sticas de la aplicación y el entorno de ejecución.
El Cuadro 3 presenta una lista de los algoritmos implementados y permite compararlos con los incluidos en las bibliotecas de operaciones colectivas de MPJ Express
y MPJ/Ibis. El prefijo “b” se refiere al uso de primitivas punto a punto bloqueantes, mientras que el prefijo “nb” indica que se usan comunicaciones punto a punto no
bloqueantes. Los algoritmos de MPJ Express y MPJ/Ibis, por lo general, son poco escalables, especialmente los implementados en MPJ/Ibis. Los algoritmos que se incluyen
en la implementación de la biblioteca presentada en este proyecto son los que aparecen
en la última columna, etiquetados bajo “Nueva Biblioteca”. Cuando existen variantes
de una determinada implementación, éstas aparecen numeradas (por ejemplo nb1FT y
nb2FT). Los algoritmos utilizados pueden ser clasificados en siete tipos: Flat Tree (FT)
o lineal, Minimun-Spanning Tree (MST), Binomial Tree (BT) o árbol binario, Four-ary
Tree (FaT), Bucket (BKT) o cı́clico, BiDirectional Exchange (BDE) y secuencial. A
continuación, se describen estos algoritmos para cada colectiva.
35
36
3. Implementación de Primitivas Colectivas MPJ
Aunque la biblioteca de colectivas es portable porque sigue el estándar MPJ, para
poder realizar pruebas y tener una implementación real, es necesario utilizar una biblioteca MPJ existente que proporcione una API estándar sobre la que trabajar. En
este caso, como veremos más adelante, se ha escogido MPJ Express por ser una de las
más utilizadas. Al integrar nuestra biblioteca, se tuvieron en consideración las colectivas implementadas en MPJ Express cuando éstas presentaban algoritmos relativamente
escalables. En esos casos, la biblioteca desarrollada incluye también esas implementaciones.
3.1.
3.1.1.
Algoritmos Implementados
Broadcast
Para implementar esta colectiva, un algoritmo muy simple y directo es el que usa
un árbol plano o Flat Tree, enviando los datos secuencialmente desde la raı́z a todos
los demás procesos. Para comprobar cómo se comporta en las pruebas y por ser el
algoritmo más sencillo, la primera implementación del Broadcast utiliza un árbol plano
(FT) con envı́os y recepciones bloqueantes (bFT, ver pseudocódigo en Figura 3.1).
Procedure Bcast(x,root)(bFT)
if me = root then
for i=0,...,npes-1 do
if me ! = i then
Send (x,pi );
else
Recv (x,root);
Figura 3.1: Pseudocódigo de Broadcast bFT
Analizando un poco este algoritmo, vemos que es el proceso raı́z (root en la Figura
3.1) el que siempre envı́a el mensaje x a todos los demás (me representa el proceso que
ejecuta el pseudocódigo y npes el número total de procesos). Al usar primitivas punto
a punto bloqueantes, en cada envı́o hay que esperar a que el proceso receptor haya
3.1. Algoritmos Implementados
37
completado la operación antes de continuar con el siguiente. Una posible optimización
es que el proceso raı́z pueda realizar todos los envı́os sin esperas entre ellos, pues cada
envı́o no depende del anterior, y que espere a que todos los datos se hayan recibido al
final. Esto serı́a posible con una aproximación que sustituyese los envı́os y recepciones
bloqueantes por primitivas no bloqueantes. Ası́, en el proceso raı́z es posible solapar
varios envı́os sin tener que esperar a que cada mensaje sea recibido. En los demás
procesos, sin embargo, no aporta ninguna mejora, ya que, inmediatamente después
de la recepción no bloqueante, tendrı́amos que añadir una llamada Wait() que dejase
dormido al proceso hasta que se completase la recepción. Por lo tanto, no se podrı́a
solapar ninguna actividad entre la recepción no bloqueante y la comprobación de que
la operación se haya completado (nbFT, ver pseudocódigo en Figura 3.2).
Procedure Bcast(x,root)(nbFT)
if me = root then
for i=0,...,npes-1 do
if me ! = i then
sreqi =Isend (x,pi );
for i=0,...,npes-1 do
if me ! = i then
Wait (sreqi );
else
Recv (x,root);
Figura 3.2: Pseudocódigo de Broadcast nbFT
En MPJ Express, el algoritmo por defecto es una variante de Flat Tree (FT) con
envı́os y recepciones bloqueantes. Utiliza un árbol de procesos configurado en el constructor de la clase en el que cada proceso tiene como máximo 4 sucesores (Four-ary Tree
o FaT). Con esta configuración, los envı́os los realiza cada proceso a sus descendientes
y ası́ sucesivamente hasta llegar a los procesos hoja, tal y como se puede observar en
la Figura 3.3. Además, se lleva a cabo un envı́o/recepción inicial si el proceso raı́z de
la operación no es el proceso con rango 0.
38
3. Implementación de Primitivas Colectivas MPJ
1
2
3
3
3
2
2
2
3
root
Figura 3.3: Funcionamiento de Broadcast en MPJ Express
Otro algoritmo con el que se puede implementar la operación de broadcast es el
conocido como Minimum-Spanning Tree, o MST. Para describir este algoritmo, imaginemos que tenemos un conjunto de p nodos numerados de 0 a p-1. En primer lugar, se
particiona el conjunto en dos subconjuntos disjuntos de la forma {0, . . . , m} y {m+1,
. . . , p-1}, siendo m = ⌊ p/2 ⌋ el proceso intermedio (mid en el pseudocódigo). Como
destino, se escoge un nodo que esté en el subconjunto que no contiene al proceso raı́z. El
mensaje es enviado desde el nodo raı́z al destino, tras lo cual, tanto raı́z como destino,
se convierten en raı́ces de una llamada recursiva a broadcast en sus respectivos subconjuntos de nodos. En la Figura 3.4 se puede ver el pseudocódigo de este algoritmo, cuyos
parámetros, además del mensaje, incluye el root, y dos nodos auxiliares, left y right.
Estos dos últimos se refieren, respectivamente, al nodo más a la izquierda y al nodo más
a la derecha del subconjunto actual. Para comenzar, cada proceso tiene que realizar
la llamada MSTBcast(x, root, 0, p-1), donde root es el proceso raı́z de la operación de
Broadcast. En la Figura 3.5 se muestra un ejemplo del funcionamiento de este algoritmo. MST reduce el número de comunicaciones entre procesadores de distintos grupos,
pero puede ser más lento que un nbFT en caso de que el número de procesadores sea
muy pequeño, ya que tiene una latencia inicial mayor. Estos dos algoritmos podrı́an
combinarse de forma que se utilizase un árbol plano dentro de cada grupo y un MST
entre grupos.
3.1. Algoritmos Implementados
39
Procedure MSTBcast(x,root,left,right)
if left = right then return;
mid = ⌊(left+right)/2⌋;
if root ≤ mid then dest=right; else dest = left;
if me = root then Send (x,dest);
if me = dest then Recv (x,root);
if me ≤ mid and root ≤ mid then MSTBcast (x,root,left,mid);
else if me ≤ mid and root > mid then MSTBcast (x,dest,left,mid);
else if me > mid and root ≤ mid then MSTBcast (x,dest,mid+1,right);
else if me > mid and root > mid then MSTBcast (x,root,mid+1,right);
Figura 3.4: Pseudocódigo de Broadcast con Minimum Spanning Tree (MST)
P0
P1
P2
P3
P0
P2
P3
x
x
Situación Inicial
P0
P1
P1
P2
x
x
Paso 2
Paso 1
P3
P0
P1
P2
P3
x
x
x
x
Situación Final
Figura 3.5: Funcionamiento de Broadcast MST
40
3. Implementación de Primitivas Colectivas MPJ
Primitiva
Bcast
MPJ Express
FaT
MPJ/Ibis
BT
Gather
nbFT
bFT
Gatherv
nbFT
bFT
Scatter
nbFT
bFT
Scatterv
nbFT
bFT
Allgather
nbFT
BT
BKT (double ring)
Allgatherv
nbFT
BT
BKT
Alltoall
nbFT
nbFT
Alltoallv
nbFT
nbFT
Reduce
bFT
BT(op. conmutativa)
bFT(op. no conmutativa)
Allreduce
nbFT
BT
BDE
bFTReduce+
nbFTScatter
{BTReduce or bFTReduce}
+
bFTScatterv
nbFT
bFT
nbFTGather + bFaTBcast
BT (Exotic)
bFT
ReduceScatter
Scan
Barrier
Nueva Biblioteca
bFT
nbFT
FaT
MST
bFT
nbFT
nb1FT
MST
bFT
nbFT
nb1FT
MST
nbFT
MST
nbFT
MST
nbFT
BT (Exotic)
nbBDE
bBKT
nbBKT
Gather + Bcast
nbFT
BT (Exotic)
nbBDE
bBKT
nbBKT
Gather + Bcast
bFT
nbFT
nb1FT
nb2FT
bFT
nbFT
nb1FT
nb2FT
bFT
nbFT
MST
nbFT
BT (Exotic)
bBDE
nbBDE
Reduce + Bcast
Reduce + Scatter
bBDE
nbBDE
bBKT
nbBKT
nbFT
secuencial
nbFTGather + bFaTBcast
Gather + Bcast
Árbol binario
Cuadro 3.1: Algoritmos para primitivas colectivas en bibliotecas MPJ.
3.1. Algoritmos Implementados
3.1.2.
41
Gather y Gatherv
La implementación por defecto que se presenta en MPJ Express de la función Gather
utiliza un árbol plano con envı́os no bloqueantes y recepciones bloqueantes (nbFT). El
pseudocódigo correspondiente a esta implementación es el que se muestra en la Figura
3.6
Procedure Gather(x,root)(nbFT)
if me ! = root then
sreq =Isend (xme ,root);
Wait (sreq);
else
for i=0,...,npes-1 do
if me ! = i then
Recv (xi ,pi );
Figura 3.6: Pseudocódigo de Gather nbFT
No obstante, esta implementación no es muy eficiente, como puede observarse en el
pseudocódigo: la primitiva no bloqueante no permite el solapamiento de trabajo entre
su utilización y la espera por su finalización. Serı́a más óptimo que la colectiva no
bloqueante se utilizase dentro del bucle, solapando comunicaciones. De esta forma, se
permite que el proceso que recibe todos los mensajes pueda realizar todas las llamadas
antes de esperar a que termine cada una de ellas. El uso de una función no bloqueante
en el envı́o, en este caso, no reporta ningún beneficio. En la biblioteca desarrollada
en este proyecto, además de proporcionar esta nueva versión no bloqueante (nb1FT)
teóricamente más eficiente, se ha implementado un árbol grueso con primitivas punto a
punto bloqueantes (bFT). El pseudocódigo de ambas funciones se muestra en la Figura
3.7.
Al igual que en el Broadcast, se puede utilizar el algoritmo MST. En este caso,
primero se realizan todas las subdivisiones posibles y después se realizan los envı́os
y recepciones, para, posteriormente, ir componiendo el mensaje final. Además, otra
diferencia con la función anterior, es que se distribuye la tarea de recibir datos (desde
42
3. Implementación de Primitivas Colectivas MPJ
Procedure Gather(x,root)(nb1FT)
Procedure Gather(x,root)(bFT)
if me = root then
for i=0,...,npes-1 do
if me ! = i then
Recv (xi ,pi );
else
Send (xme ,root);
if me = root then
for i=0,...,npes-1 do
if me ! = i then
rreqi =Irecv (xi ,pi );
for i=0,...,npes-1 do
if me ! = i then
Wait (rreqi );
else
Send (xme ,root);
Figura 3.7: Pseudocódigo de Gather FT
el proceso srce en cada caso). Es decir, el proceso raı́z no tiene que recibir directamente
de todos los demás. También hay que tener en cuenta que no se transmite siempre
el mismo mensaje, sino que se van aglutinando mensajes a medida que se avanza en
el árbol de procesos. El pseudocódigo de esta función se puede ver en la Figura 3.8
y su funcionamiento en la Figura 3.9. Además de las consideraciones hechas para el
MST del Broadcast, es necesario buffering en nodos intermedios, pues aunque el único
destinatario de todos los mensajes es el nodo raı́z, las comunicaciones se distribuyen.
Para la variante Gatherv se pueden implementar los mismos algoritmos con la salvedad de que hay que tener en cuenta que no todos los procesos envı́an mensajes del
mismo tamaño y que pueden tener diferentes desplazamientos. La versión del algoritmo
implementada en MPJ Express es también similar a la implementación de la función
Gather (nbFT).
3.1. Algoritmos Implementados
43
Procedure MSTGather(x,root,left,right)
if left = right then return;
mid = ⌊(left+right)/2⌋;
if root ≤ mid then srce=right; else srce = left;
if me ≤ mid and root ≤ mid then MSTGather (x,root,left,mid);
else if me ≤ mid and root > mid then MSTGather (x,srce,left,mid);
else if me > mid and root ≤ mid then MSTGather (x,srce,mid+1,right);
else if me > mid and root > mid then MSTGather (x,root,mid+1,right);
if root ≤
if me
if me
else
if me
if me
mid then
= srce then Send (xmid+1:right ,root);
= root then Recv (xmid+1:right ,srce);
= srce then Send (xlef t:mid ,root);
= root then Recv (xlef t:mid ,srce);
Figura 3.8: Pseudocódigo de Gather con Minimum Spanning Tree (MST)
P0
P1
P2
P3
P0
Situación inicial
P0
P1
P2
Paso 2
P1
P2
P3
Paso 1
P3
P0
P1
P2
Situación final
Figura 3.9: Funcionamiento de Gather MST
P3
44
3. Implementación de Primitivas Colectivas MPJ
3.1.3.
Scatter y Scatterv
Scatter representa la operación inversa a Gather, con lo cual, todos los algoritmos
vistos en la sección anterior son válidos si invertimos las operaciones. En este caso, la
implementación por defecto combina de forma adecuada las primitivas bloqueantes y
no bloqueantes en un algoritmo de árbol plano. Las recepciones son bloqueantes, puesto
que cada proceso sólo tiene que recibir una vez. Los envı́os, dentro del bucle, son no
bloqueantes, de forma que se pueden solapar y sólo se espera por su finalización tras
realizar el último envı́o. El pseudocódigo de este algoritmo (nbFT) es el que aparece
en la Figura 3.10.
Procedure Scatter(x,root)(nbFT)
if me ! = root then
for i=0,...,npes-1 do
if me ! = i then
sreqi =Isend (xi ,pi );
for i=0,...,npes-1 do
Wait (sreqi );
else
Recv (xme ,root);
Figura 3.10: Pseudocódigo de Scatter nbFT
El algoritmo MST es muy parecido al utilizado en Broadcast, teniendo en cuenta
que cada proceso recibe un mensaje distinto. Las Figuras 3.11 y 3.12 muestran su
pseudocódigo y funcionamiento. Al igual que en Gather, los nodos van a almacenar, de
forma temporal, más datos que los que van dirigidos finalmente a ellos.
En Scatterv es posible utilizar los algoritmos planteados para Scatter teniendo en
cuenta que los mensajes enviados a cada proceso pueden tener diferente tamaño y
desplazamiento.
3.1. Algoritmos Implementados
45
Procedure MSTScatter(x,root,left,right)
if left = right then return;
mid = ⌊(left+right)/2⌋;
if root ≤ mid then dest=right; else dest = left;
if root ≤
if me
if me
else
if me
if me
mid then
= root then Send (xmid+1:right ,dest);
= dest then Recv (xmid+1:right ,root);
= root then Send (xlef t:mid ,dest);
= dest then Recv (xlef t:mid ,root);
if me ≤ mid and root ≤ mid then MSTScatter (x,root,left,mid);
else if me ≤ mid and root > mid then MSTScatter (x,dest,left,mid);
else if me > mid and root ≤ mid then MSTScatter (x,dest,mid+1,right);
else if me > mid and root > mid then MSTScatter (x,root,mid+1,right);
Figura 3.11: Pseudocódigo de Scatter con Minimum Spanning Tree (MST)
P0
P1
P2
P3
P0
Situación Inicial
P0
P1
P2
Paso 2
P1
P2
P3
Paso 1
P3
P0
P1
P2
Situación Final
Figura 3.12: Funcionamiento de Scatter MST
P3
46
3. Implementación de Primitivas Colectivas MPJ
3.1.4.
Allgather y Allgatherv
La primitiva Allgather es una variante del Gather en el que todos los procesos
implicados reciben una copia del resultado final. En MPJ Express, la implementación
por defecto (nbFT) utiliza envı́os no bloqueantes desde cada procesador hacia todos
los demás y recepciones bloqueantes (ver pseudocódigo en la Figura 3.13).
Procedure Allgather(x,root)(nbFT)
for i=0,...,npes-1 do
if me ! = i then
sreqi =Isend (xi ,pi );
for i=0,...,npes-1 do
if me ! = i then
Recv (xi ,pi );
for i=0,...,npes-1 do
if me ! = i then
Wait (sreqi );
Figura 3.13: Pseudocódigo de Allgather nbFT
No obstante, podemos encontrar también implementada una versión que, en MPJ
Express, denominan “Exotic”, sólo útil en caso de que el número de procesos sea
potencia de 2. Este algoritmo realmente implementa un patrón de comunicaciones BT
(Binomial Tree) con envı́os y recepciones bloqueantes en el que en cada paso i (desde
1 hasta ⌈log2 (npes)⌉) el proceso pj se comunica con el proceso pj+2i−1 . Por defecto
está desactivada (se activarı́a con una variable tipo flag estática). Uno de los problemas
que presenta es que no indica ningún fallo en caso de que el número de procesos no sea
el adecuado. Aunque la implementación es diferente, el funcionamiento resulta similar
al del algoritmo BDE que hemos implementado y que se muestra a continuación.
Un algoritmo ampliamente difundido para Allgather, también basado en asumir que
el número de procesos es potencia de 2, es el llamado BiDirectional Exchange o BDE.
Este algoritmo particiona el comunicador o grupo de procesos en dos mitades y, recursivamente, realiza un Allgather BDE de todos los datos en cada mitad. A continuación,
3.1. Algoritmos Implementados
47
intercambia estos datos entre pares disjuntos de nodos (un nodo de cada mitad). El
problema de los algoritmos BDE estriba en que, cuando el número de nodos no es
potencia de 2, llegamos a subconjuntos con un número impar de nodos. En este caso, el
nodo que sobra no tendrı́a pareja en la otra mitad del subconjunto. La solución pasarı́a
porque uno de los nodos enviase dos veces la información (una vez a su pareja y otra
al nodo sobrante). Sin embargo, esto duplicarı́a el coste del algoritmo. En la práctica,
el resultado no serı́a tan malo porque el proceso que envı́a dos veces no es siempre
el mismo, sin embargo, no se suele usar esta variante debido a su complejidad (ver
pseudocódigo en la Figura 3.14 y funcionamiento en la Figura 3.15).
Procedure BDEAllgather(x,left,right)
if left = right then return;
size = right-left+1;
mid = ⌊(left+right)/2⌋;
if me ≤ mid then
partner=me+⌊size/2⌋;
else
partner=me-⌊size/2⌋;
if me ≤ mid then
BDEAllgather (x,left,mid);
else
BDEAllgather (x,mid+1,right);
if me ≤ mid then
Send (xlef t:mid ,partner);
Recv (xmid+1:right , partner);
else
Send (xmid+1:right ,partner);
Recv (xlef t:mid , partner);
Figura 3.14: Pseudocódigo Allgather BDE
Finalmente, la implementación desarrollada de Allgather BDE incluye envı́os no
bloqueantes y recepciones bloqueantes (implementación nBDE), al postularse como la
opción más eficiente en la evaluación de distintas alternativas existentes.
48
3. Implementación de Primitivas Colectivas MPJ
P0
P1
P2
P3
P0
Situación inicial
P0
P1
P2
P1
P2
P3
Paso 1
P3
P0
Paso 2
P1
P2
P3
Situación final
Figura 3.15: Funcionamiento de Allgather BDE
El algoritmo BDE se puede ver como un caso particular de los algoritmos BKT o
Bucket. Estos ven el conjunto de nodos como un anillo implementado como un array
lineal en el cual se aprovecha la ventaja de que los mensajes que atraviesan un enlace
en sentido opuesto no entran en conflicto. En cada paso, todos los procesos envı́an
un mensaje al nodo de su derecha. De este modo, los subvectores, que son enviados
inicialmente por un nodo de forma individual, acaban finalmente distribuidos por todo
el anillo. Este algoritmo también es conocido como Algoritmo cı́clico. Las Figuras 3.16
y 3.17 proporcionan respectivamente el pseudocódigo y un ejemplo de funcionamiento.
Procedure BKTAllgather(x )
prev = me - 1;
if prev < 0 then prev= npes - 1;
next = me + 1;
if next = npes then next=0;
current i = me;
for i=0,...,npes-2 do
Send (xcurrent i ,next);
current i = current i - 1;
if current i < 0 then current i=npes-1;
Recv (xcurrent i ,prev);
Figura 3.16: Pseudocódigo de Allgather BKT
3.1. Algoritmos Implementados
P0
P1
P2
49
P3
P0
Situación inicial
P0
P1
P2
P1
P2
P3
Paso 1
P3
P0
P1
P2
P3
Paso 3
Paso 2
P0
P1
P2
P3
Situación final
Figura 3.17: Funcionamiento de Allgather BKT
Tras realizar una evaluación preliminar de la implementación de este algoritmo
ejecutado sobre MPJ Express, se detectó cierta inestabilidad que producı́a errores de
ejecución a partir de ciertos tamaños de mensaje, dependiendo del número de procesadores. Para solucionarlo, se introdujeron envı́os no bloqueantes sustituyendo a los que
ya habı́a, solventando de este modo la incidencia mencionada, además de mejorar la
escalabilidad de esta solución (implementación nbBKT).
Finalmente, una opción de implementación adicional es la que parte de la definición
de la operación como combinación de Gather y Broadcast. En esta opción se harı́a
una llamada inicial a la función Gather, configurando cualquier proceso como raı́z (por
ejemplo, el proceso con rango 0) y luego, utilizando la misma raı́z, se llevarı́a a cabo
un Broadcast del mensaje recibido.
Para implementar el Allgatherv se han versionado los anteriores algoritmos para
que tengan en cuenta las particularidades de esta variante. En cuanto a la opción por
defecto, lo único que cambia es que se utilizan primitivas no bloqueantes tanto en envı́os
como en recepciones en el algoritmo de árbol plano.
50
3. Implementación de Primitivas Colectivas MPJ
3.1.5.
Alltoall y Alltoallv
Esta función implementa un patrón de comunicación en el que todos los procesos
envı́an datos distintos a todos los demás. También es conocida como “intercambio total”
(total exchange). En este caso, no se ha implementado ningún algoritmo especı́fico
además de los de árbol plano. La versión por defecto en MPJ Express utiliza también
este enfoque, combinando envı́os no bloqueantes con recepciones bloqueantes, (nbFT)
como se puede apreciar en la Figura 3.18.
Procedure Alltoall(x,root)(nbFT)
for i=0,...,npes-1 do
if me ! = i then sreqi =Isend (xme,i ,pi );
for i=0,...,npes-1 do
if me ! = i then Recv (xi,me ,pi );
for i=0,...,npes-1 do
if me ! = i then Wait (sreqi );
Figura 3.18: Pseudocódigo Alltoall nbFT
Con el objeto de evaluar la eficiencia de las posibles implementaciones de esta primitiva, se implementaron tres versiones más en las que se combinan primitivas bloqueantes
y no bloqueantes (ver pseudocódigos en las Figuras 3.19, 3.20 y 3.21):
envı́os y recepciones bloqueantes (bFT)
envı́os y recepciones no bloqueantes (nb1FT)
envı́os bloqueantes y recepciones no bloqueantes (nb2FT)
En cuanto a las implementaciones de Alltoallv, se han utilizado los mismos algoritmos que para Alltoall teniendo en cuenta las peculiaridades de esta primitiva colectiva.
3.1. Algoritmos Implementados
Procedure Alltoall(x,root)(bFT)
for i=0,...,npes-1 do
if me ! = i then Send (xme,i ,pi );
for i=0,...,npes-1 do
if me ! = i then Recv (xi,me ,pi );
Figura 3.19: Pseudocódigo de Alltoall bFT
Procedure Alltoall(x,root)(nb1FT)
for i=0,...,npes-1 do
if me ! = i then sreqi =Isend (xme,i ,pi );
if me ! = i then rreqi =Irecv (xi,me ,pi );
for i=0,...,npes-1 do
if me ! = i then Wait (sreqi );
for i=0,...,npes-1 do
if me ! = i then Wait (rreqi );
Figura 3.20: Pseudocódigo de Alltoall nb1FT
Procedure Alltoall(x,root)(nb2FT)
for i=0,...,npes-1 do
if me ! = i then Send (xme,i ,pi );
if me ! = i then rreqi =Irecv (xi,me ,pi );
for i=0,...,npes-1 do
if me ! = i then Wait (rreqi );
Figura 3.21: Pseudocódigo de Alltoall nb2FT
51
52
3. Implementación de Primitivas Colectivas MPJ
3.1.6.
Reduce
La función Reduce lleva a cabo una operación de reducción (aplicación de una
operación como puede ser la suma o la multiplicación sobre un determinado conjunto de
valores para obtener un único resultado) entre datos de todos los procesos y proporciona
el resultado en un proceso raı́z. En MPJ Express se implementa utilizando envı́os no
bloqueantes desde todos los procesos, y recepciones bloqueantes en el proceso raı́z
conformando un árbol plano (nbFT, ver pseudocódigo en la Figura 3.22). A la inversa
(envı́os bloqueantes desde todos los procesos y recepciones no bloqueantes en el proceso
raı́z) no tendrı́a sentido, ya que, para hacer la operación, es necesario esperar a haber
recibido los operandos, ası́ que no se podrı́a solapar ningún cálculo. Además, de esta
forma tampoco serı́a esperable un beneficio tangible, ya que los procesos que envı́an no
realizan ninguna otra actividad, con lo cual no hay solapamiento de tareas.
Hay que tener en cuenta también que las operaciones definidas por el usuario y las
proporcionadas por el API MPJ se tratan de distinta manera. En el primer caso, es
preciso realizar una llamada a la función Call que añade al resultado parcial acumulado
los nuevos datos y devuelve el resultado final. Esta función recibe como parámetro el
resultado acumulado y el valor a incorporar. El nuevo resultado acumulado se sobreescribe sobre el antiguo. En el segundo, es necesaria una llamada a Perform, para realizar
las operaciones intermedias e ir acumulando los resultados parciales, y otra a GetResultant para obtener el resultado final. Aquı́, Perform acumula el resultado en un buffer
interno temporal, por ello, es necesario llamar a la segunda función cuando queremos
obtener el resultado. En el pseudocódigo, estas llamadas no se muestran, en aras de
mantener la claridad de la presentación. En el código, se añadirı́a una comprobación
para ver el tipo de función utilizada y se realizarı́a el cálculo dependiendo del resultado
de dicha comprobación.
Para evaluar la eficiencia de la implementación que se acaba de presentar y ver
si compensa utilizar primitivas no bloqueantes en este caso, se ha implementado una
versión en árbol plano y envı́os y recepciones bloqueantes (bFT, Figura 3.23).
Por otro lado, si nos fijamos, el esquema de envı́os es el mismo que en un Scatter.
Es decir, cada proceso envı́a a un proceso raı́z un mensaje diferente, con la salvedad
3.1. Algoritmos Implementados
53
Procedure Reduce(x,root) (nbFT)
if me ! = root then
sreq =Isend ( xme ,root);
Wait (sreq);
else
for i=0,...,npes-1 do
if me ! = i then
Recv (xi ,i);
res = op (res, xi );
Figura 3.22: Pseudocódigo de Reduce nbFT
Procedure Reduce(x,root) (bFT)
if me ! = root then
Send (xme ,root);
else
for i=0,...,npes-1 do
if me ! = i then
Recv (xi , i);
res = op (res, xi);
Figura 3.23: Pseudocódigo de Reduce bFT
de que en este caso el proceso raı́z va a usar estos mensajes para llevar a cabo una
operación de reducción en lugar de almacenarlos en un array. Por este motivo, se ha
implementado el Reduce con un algoritmo MST en que los nodos intermedios van
realizando las operaciones con los datos recibidos. En las Figuras 3.24 y 3.25 se muestran
el pseudocódigo y un ejemplo de funcionamiento.
54
3. Implementación de Primitivas Colectivas MPJ
Procedure MSTReduce(x,root,left,right)
if left = right then return;
mid = ⌊(left+right)/2⌋;
if root ≤ mid then srce=right; else srce = left;
if me ≤ mid and root ≤ mid then MSTReduce (x,root,left,mid);
else if me ≤ mid and root > mid then MSTReduce (x,dest,left,mid);
else if me > mid and root ≤ mid then MSTReduce (x,dest,mid+1,right);
else if me > mid and root > mid then MSTReduce (x,root,mid+1,right);
if me = srce then
Send (x,root);
if me = root then
Recv (tmp,srce);
x = op (x, tmp);
Figura 3.24: Pseudocódigo de Reduce con Minimum Spanning Tree (MST)
P0
P1
P2
P3
P0
Situación Inicial
P0
P1
P2
Paso 2
P1
P2
P3
Paso 1
P3
P0
P1
P2
Situación Final
Figura 3.25: Funcionamiento de Reduce MST
P3
3.1. Algoritmos Implementados
3.1.7.
55
Allreduce
La primitiva Allreduce, al igual que ocurrı́a con Allgather y Gather, puede verse
como un Reduce en el cual el resultado final está presente en todos los procesos.
La implementación por defecto en MPJ Express utiliza un árbol plano con envı́os
no bloqueantes y recepciones bloqueantes (nbFT, ver pseudocódigo en la Figura 3.26).
Al igual que en el Allgather, MPJ Express también proporciona una implementación
basada en árboles binarios (BT).
Procedure Allreduce(x,op) (nbFT)
for i=0,...,npes-1 do
if me ! = i then
sreqi =Isend ( xme ,i);
for i=0,...,npes-1 do
if me ! = i then
Recv (xi ,i);
res = op (res, xi );
for i=0,...,npes-1 do
if me ! = i then
Wait (sreqi );
Figura 3.26: Pseudocódigo de Allreduce nbFT
Para esta operación colectiva es posible la utilización del algoritmo BDE, de igual
manera que para el Allgather. En este caso, en cada paso se intercambia el vector
completo y se opera con el resultado local. En las Figuras 3.27 y 3.28 se pueden observar
el pseudocódigo y un ejemplo de funcionamiento. Por motivos de eficiencia, además de
la versión del algoritmo con primitivas bloqueantes, se ha implementado una variante
con envı́os no bloqueantes (nbBDE).
Por último, atendiendo a la definición de esta primitiva, es fácil darse cuenta de que
es equivalente a un Reduce seguido de un Broadcast. Nótese que es preciso indicar
el mismo proceso raı́z en las llamadas a ambas operaciones colectivas.
56
3. Implementación de Primitivas Colectivas MPJ
Procedure BDEAllreduce(x,left,right)
if left = right then return;
size = right-left+1;
mid = ⌊(left+right)/2⌋;
if me ≤ mid then
partner=me+⌊size/2⌋;
else
partner=me-⌊size/2⌋;
if me ≤ mid then
Send (x,partner);
Recv (tmp, partner);
x = op (x, tmp);
else
Send (x,partner);
Recv (tmp, partner);
x = op (x, tmp);
if me ≤ mid then
BDEAllreduce (x,left,mid);
else
BDEAllreduce (x,mid+1,right);
Figura 3.27: Pseudocódigo de Allreduce BDE
P0
P1
P2
p3
P0
Situación Inicial
P0
P1
P2
Paso 2
P1
P2
P3
Paso 1
P3
P0
P1
P2
Situación Final
Figura 3.28: Funcionamiento de Allreduce BDE
P3
3.1. Algoritmos Implementados
3.1.8.
57
Reduce-Scatter
El funcionamiento de esta primitiva es equivalente a tomar el resultado del Reduce
y distribuirlo como si se hiciese un Scatterv del resultado. Esta implementación es la
incluida en MPJ Express , donde inicialmente se elige al proceso con rango 0 como
raı́z y se realiza una llamada a una operación de Reduce (bFT). Seguidamente, con
el resultado obtenido, se hace un Scatter (nbFT). No obstante, esta implementación
no es del todo correcta, ya que se utiliza Scatter en lugar de Scatterv, con lo cual, se
limita la funcionalidad obligando a que todos los procesos reciban, al final, mensajes
del mismo tamaño.
Otra opción es implementarlo directamente sobre comunicaciones punto a punto sin
recurrir al uso de otras colectivas. De este modo, es posible implementar esta colectiva
haciendo que cada proceso envı́e un mensaje diferente a cada uno de los demás y éstos
apliquen una operación de reducción sobre los datos recibidos. El patrón de comunicaciones podrı́a ser un algoritmo BDE, como se ve en las Figuras 3.29 y 3.30. Por las
mismas razones expuestas para otras colectivas, además de una implementación con
primitivas punto a punto bloqueantes, se proporciona una con envı́os no bloqueantes
nbBDE.
También es posible implementar esta función es usando un algoritmo cı́clico o BKT.
El resultado parcial de la reducción se va acumulando a través del anillo hasta obtener
el resultado final (ver Figuras 3.31 y 3.32). Al igual que en Allgather y debido también
a motivos de estabilidad del algoritmo, se proporciona una implementación con envı́os
no bloqueantes (nbBKT).
58
3. Implementación de Primitivas Colectivas MPJ
Procedure BDEReduceScatter(x,left,right)
if left = right then return;
size = right-left+1;
mid = ⌊(left+right)/2⌋;
if me ≤ mid then
partner=me+⌊size/2⌋;
else
partner=me-⌊size/2⌋;
if me ≤ mid then
Send (xmid+1:right ,partner);
Recv (tmp, partner);
xlef t:mid = op (xlef t:mid , tmp);
else
Send (xlef t:mid ,partner);
Recv (tmp, partner);
xmid+1:right = op (xmid+1:right , tmp);
if me ≤ mid then
BDEReduceScatter (x,left,mid);
else
BDEReduceScatter (x,mid+1,right);
Figura 3.29: Pseudocódigo de Reduce-Scatter BDE
3.1. Algoritmos Implementados
59
P0
P1
P2
P3
P0
P1
P2
P3
0
0
0
0
0
0
0
0
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
Situación Inicial
P0
P1
P2
Paso 1
P3
0
0
2
2
1
1
3
3
P0
0
Paso 2
P1
1
P2
2
Situación Final
Figura 3.30: Funcionamiento de Reduce-Scatter BDE
Procedure BKTReduceScatter(x )
prev = me - 1;
if prev < 0 then prev= npes - 1;
next = me + 1;
if next = npes then next=0;
current i = next;
for i=npes-2,...,0 do
Send (xcurrent i ,prev);
current i = current i + 1;
if current i = npes then current i = 0;
xcurrent i = op (xcurrent i , tmp);
Figura 3.31: Pseudocódigo de Reduce-Scatter BKT
P3
3
60
3. Implementación de Primitivas Colectivas MPJ
P0
P1
P1
P2
P3
0
0
0
0
1
1
1
1
1
2
2
2
2
2
2
3
3
3
3
3
3
P0
P3
P2
0
0
0
0
1
1
1
2
2
3
3
Situación Inicial
P0
0
P1
1
P1
P0
P3
P2
P2
P3
0
0
3
0
1
0
2
3
Paso 1
1
2
2
3
1
1
2
3
3
Paso 2
Paso 3
P0
0
P1
1
P2
2
2
P3
3
Situación Final
Figura 3.32: Reduce-Scatter BKT
3.1. Algoritmos Implementados
3.1.9.
61
Scan
La primitiva Scan es una colectiva de reducción o computacional en la cual cada nodo realiza una operación de reducción con los mensajes enviados por todos los procesos
de rango inferior o igual al suyo. Existen dos enfoque básicos para implementar esta
operación. En el primero, cada uno de los procesos envı́a su mensaje a todos aquellos
que tienen un rango superior. La recepción en cada nodo se realiza para cada uno de
los mensajes enviados por los procesos con rango inferior. Ésta es la versión por defecto
(nbFT) cuyo pseudocódigo se muestra en la figura 3.33, en la que los envı́os son no
bloqueantes. Las recepciones en cambio sı́ son bloqueantes porque es necesario esperar
a obtener los datos para realizar el cálculo.
Procedure Scan(x ) nbFT
for i=npes-1,...,me+1 do
sreqi = Isend(xi ,i );
for i=0,...,me-1 do
Recv(tmp,i );
res = op (res, tmp);
res = op (res, xme );
for i=npes-1,...,me+1 do
Wait(sreqi );
Figura 3.33: Pseudocódigo de Scan nbFT
Otra alternativa en la implementación del Scan es la utilización de un algoritmo
secuencial. En este caso, un proceso envı́a al siguiente su resultado, calculado como
resultado de acumular su mensaje al que recibe del proceso anterior, que, a su vez, es
el resultado de aplicar la operación de reducción a los mensajes de todos los procesos
anteriores. De esta manera, sólo se realiza un envı́o y una recepción en cada nodo
(excepto en el primero, que sólo envı́a, y el último, que sólo recibe). El pseudocódigo
se puede ver en la Figura 3.34.
Procedure Scan(x ) Secuencial
if me > 0 then
Recv (xme−1 ,me-1);
xme = op (xme , xme−1 );
if me<npes-1 then Send (xme ,me+1);
Figura 3.34: Pseudocódigo de Scan Secuencial
62
3. Implementación de Primitivas Colectivas MPJ
3.1.10.
Barrier
Una barrera supone un punto de sincronización para todos los procesos implicados.
Su implementación presenta numerosas alternativas. El método tradicional es el uso de
envı́os y recepciones bloqueantes de mensajes muy pequeños que impliquen a todos los
procesadores. Siguiendo este enfoque, una posible implementación es utilizar un Antibroadcast (todos envı́an a un procesador dado) seguido de un Broadcast (todos
reciben de ese mismo proceso). Es decir, que todos los procesos envı́en a uno raı́z y
éste les devuelva el mensaje a continuación. Esta implementación se puede realizar con
un Gather seguido de un Broadcast. En la versión por defecto de MPJ Express, se
utiliza este algoritmo implementado directamente con comunicaciones punto a punto,
construyendo un patrón de comunicaciones en árbol de grado cuatro en lugar de hacer
llamadas a las primitivas colectivas correspondientes.
Otra alternativa implementada es la utilización de un árbol binariopara los envı́os y
recepciones (ver ejemplo en la Figura 3.35). En este caso, cuando el número de procesos
no es potencia de 2, los que “sobran” se comunican con uno de los nodos hoja. El efecto
que se produce es como si el conjunto de procesos se fuese desgranando por la mitad
recursivamente y cada nodo se comunicase con otro perteneciente a la mitad opuesta.
P0
P1
P2
P3
P4
P5
P6
P7
Paso 1
P0
P1
P2
P3
P4
P5
P6
p1
P7
p2
P5
P6
P7
Paso 2
P0
P1
P2
P3
P4
Paso 3
Figura 3.35: Funcionamiento de la Barrera usando un árbol binario
En MPJ Express existe la posibilidad de utilizar una implementación en árbol binario “Exotic”, similar a las que se vieron para otras colectivas como Allgather.
Capı́tulo 4
Integración y Optimización en
Sistemas Multi-core
4.1.
MPJ Express como Marco para la Integración
Tal y como se dijo al principio del capı́tulo anterior, la biblioteca de operaciones
colectivas desarrollada es portable, con lo que podrá ser integrada en cualquier implementación de MPJ. Sin embargo, para su validación experimental es necesario escoger
una biblioteca de referencia en la que integrarla.
La biblioteca MPJ más implantada es mpiJava. No obstante, sus colectivas son
un wrapper a las de MPI, es decir, delegan en la implementación nativa, por lo que
la biblioteca desarrollada no serı́a aplicable. De hecho, la biblioteca que se presenta
en este proyecto está orientada a resolver los problemas de escalabilidad de las implementaciones Java puro (100 % Java). Entre estas bibliotecas, la que cuenta con mayor
aceptación, con un desarrollo más activo y mayor número de usuarios, es MPJ Express
[27] [28]. Aunque son numerosas las implementaciones existentes de MPJ [2] [30], la
mayorı́a pertenecen a proyectos cuyo desarrollo ha sido abandonado. Entre las bibliotecas activas en la actualidad, destacamos MPJ/Ibis [29], que no ha sido seleccionado
porque tiene problemas de configuración, pocos usuarios y no es totalmente portable.
Otro ejemplo es P2P-MPI, orientada a soporte para Grid. Hay también otros proyectos
que no implementan el API de MPJ, sino APIs similares, por ejemplo, Parallel Java o
Jcluster. Otro proyecto interesante es F-MPJ [30], fruto de la investigación realizada
63
64
4. Integración y Optimización en Sistemas Multi-core
en el Grupo de Arquitectura de Computadores de la Universidad de A Coruña, que
ya proporcina algunos algoritmos escalables. No obstante, no está todavı́a disponible
públicamente. Finalmente, nos hemos decantado por MPJ Express en base a las siguientes razones: es una biblioteca disponible públicamente, tiene cientos de usuarios,
y su biblioteca de operaciones colectivas no está optimizada.
Para la integración, se ha modificado la clase Intracomm del paquete mpi (en MPJ
Express, sustituye “mpj” por “mpi” en los nombres de paquetes y clases, pero esto apenas afecta a la implementación de las colectivas) incluyendo los métodos desarrollados
en la biblioteca de operaciones colectivas, además de la documentación de acuerdo a las
convenciones vigentes y todo lo necesario para la selección automática de algoritmos1 .
4.1.1.
Correcciones sobre MPJ Express
A medida que se fue avanzando en la integración y la inclusión de las distintas
colectivas en la clase Intracomm de MPJ Express, se fueron detectando pequeños bugs
que han sido subsanados para que no afectasen al funcionamiento de las operaciones.
La mayorı́a de estos errores se corresponden con problemas en los buffers temporales
y tratamiento de desplazamientos iniciales. Esto aparece, por ejemplo, en las operaciones de reducción predefinidas, donde para que el resultado se vaya acumulando, es
necesario inicializar el buffer interno de la operación con una llamada a createInitialBuffer(). En esta función se utiliza un solo offset o desplazamiento inicial. Esto obliga a
suponer que los datos que se pasan para crear el buffer inicial tienen el mismo desplazamiento que el vector de datos con el que se van a realizar las demás operaciones. En las
colectivas de reducción, esto no se puede suponer. En la llamada a Reduce, por ejemplo,
podrı́amos tener un offset diferente en el envı́o y en la recepción. Aunque esta situación
puede no ser muy habitual, sı́ que se trata de una violación de la especificación de la
primitiva, ya que si el offset del envı́o es diferente al de la recepción, podrı́a haber incoherencias al ir actualizando el valor almacenado. El proceso raı́z inicializará el buffer
con sus datos para enviar y su offset de envı́o, pero a continuación utilizará el vector de
recepción con su offset para actualizarlo. En este caso, una solución sencilla serı́a copiar
1
El código desarrollado asciende a 5.210 SLOC (Source Lines Of Code) [36]
4.1. MPJ Express como Marco para la Integración
65
al inicio el array de envı́o en otro con diferente offset, lo que obligarı́a a copiar dos veces
el array (uno en un array temporal y otro al inicializar el buffer de la operación). En
otros casos, en que las operaciones de reducción se vayan actualizando de forma parcial
utilizando nodos intermedios, la solución se complica e implicarı́a numerosas copias de
arrays.
La solución por la que se ha optado consiste en crear una función nueva dentro
de las operaciones predefinidas, la cual ha de recibir dos offsets, uno para el vector de
entrada y otro para el vector que va a almacenar los resultados intermedios. El primero
coincidirı́a con el offset del vector a enviar y el segundo con el del vector en que se
recibe. En el estándar MPJ no se define la interfaz de una operación de reducción
predefinida, con lo cual, en cada biblioteca, habrı́a que comprobar el funcionamiento
de estas operaciones.
En este punto, lo mejor serı́a poder realizar esta modificación en una superclase
sin tener que tratar cada operación predefinida por separado. No obstante, esto no es
posible en MPJ Express, lo cual obliga a implementar esta función auxiliar en cada
clase. Es importante destacar que también se cambiaron las llamadas en las funciones
por defecto incluidas en MPJ Express para asegurar su correcto funcionamiento.
Un error similar afecta al Reduce y al Scan. Por lo que se puede deducir de los
comentarios que aparecen en el código fuente de la clase Intracomm, este mismo error
ha sido subsanado en la primitiva Allreduce gracias a la colaboración de la comunidad.
El problema radica en la creación de buffers temporales. Estos se creaban copiando
el buffer de envı́o y luego se aplicaban las operaciones de reducción con el offset del
buffer de recepción. De nuevo, estos offset no tienen por qué ser iguales. La solución
implementada para solventar este problema consiste en crear buffers temporales sin
offset inicial y cambiar el tratamiento de los desplazamientos en el resto del método.
Para el Scan se creó una función que implementa una versión similar a la que aparece
en MPJ Express pero corregida (es decir, tratando buffers y offsets de forma correcta).
De esta manera, será posible evaluar si las modificaciones para corregirlo introducen
algún problema de eficiencia.
Finalmente, en las implementaciones de colectivas de MPJ Express, cuando se pro-
66
4. Integración y Optimización en Sistemas Multi-core
ducen condiciones de fallo, se notifica mediante un mensaje por pantalla, pero no se
lanza ninguna excepción ni se detiene la ejecución. Con el objeto de tener una implementación más robusta, se han modificado estas notificaciones sustituyéndolas por
el lanzamiento de una excepción de tipo MPIException que recibe como parámetro la
cadena de caracteres que, antes de la corrección, se mostraba por pantalla.
4.2.
Integración y Selección Automática de Algoritmos
Tras implementar la biblioteca de operaciones colectivas, pasamos a integrarla en
una biblioteca de paso de mensajes para Java (MPJ) con el objeto de que pueda ser
utilizada y probada. A pesar de que es portable por estar construida sobre primitivas
punto a punto estándar, es necesario realizar una serie de pasos adicionales para llevar
a cabo la integración. Tal y como vimos en la sección de diseño, utilizaremos la clase
Intracomm, en la que introduciremos los algoritmos implementados (ver Figura 4.1).
Serı́a posible crear una subclase con la implementación de la biblioteca desarrollada,
pero uno de los objetivos es facilitar su incorporación a cualquier biblioteca MPJ, en
particular en MPJ Express, proyecto con el cual se planea la próxima inclusión de esta
biblioteca de operaciones colectivas en su distribución oficial, ası́ que no se deberı́a
alterar su estructura. Esta integración con MPJ Express se ha realizado con éxito en
el marco de este proyecto.
mpj
MPJ
Group
Datatype
Comm
Intracomm
Cartcomm
Status
Intercomm
Request
Op
Prequest
Graphcomm
Figura 4.1: Clases principales en MPJ
UserFunction
4.2. Integración y Selección Automática de Algoritmos
67
Otro de los objetivos del proyecto era el de proporcionar varios algoritmos por
primitiva y habilitar un mecanismo de selección. Esta selección del algoritmo se pretende
que sea automática y para ello se utilizará un fichero de configuración que indique cuál
escoger en función de dos parámetros: número de procesadores y tamaño de mensaje. No
obstante, para las colectivas Barrier y Alltoallv, se realizará sólo en función del número
de procesadores. En la primera, el tamaño de mensaje es irrelevante y, en la segunda,
no tiene por qué ser el mismo para todos y podrı́a causar conflictos de selección entre
distintos procesos. En las demás, estos dos parámetros son fáciles de medir en tiempo
de ejecución y sirven para caracterizar, en cierto modo, la arquitectura utilizada.
Es posible también que haya casos en que se prefiera seleccionar el algoritmo directamente en el código fuente sin utilizar la selección automática. Para ello, como se
explica más adelante, se incluyen variables estáticas que permiten activar y desactivar
la selección automática y especificar el algoritmo a utilizar en cada colectiva.
Para cargar la configuración de selección, se utilizará un inicializador estático en
la clase Intracomm que cree una tabla hash con los valores del número de procesos y
tamaño de mensajes, junto con el algoritmo a seleccionar. Esto se consigue en Java
fácilmente si utilizamos un fichero .properties para la configuración. Para evitar errores
en tiempo de ejecución, crearemos un método de acceso que permita obtener cualquiera
de estos valores del fichero y que, en caso de que algún campo no esté configurado, nos
proporcione un valor por defecto sin provocar una excepción. Ası́, será posible configurar
sólo las opciones deseadas.
Todas las opciones configurables en tiempo de ejecución se presentan en forma de variables estáticas. Para referirnos a cada algoritmo, se definen unas constantes en la clase
Intracomm cuyo nombre tendrá el formato ALGORITMO COLECTIVA, por ejemplo
REDUCE MST. Para indicar si la selección es automática o no, se incluirá una variable estática booleana SELECT AUTO. También se define otra variable estática para
cada colectiva de nombre optionCollective, configurable por el usuario y que señalará el
algoritmo a utilizar en cada operación (por ejemplo, optionBcast) en caso de elegir el
algoritmo desde el código y como opción por defecto en la selección automática. Por
último, ya que la selección de algoritmo se hace en función del número de nodos y del
68
4. Integración y Optimización en Sistemas Multi-core
tamaño del mensaje, para utilizar la función de acceso a las propiedades, se definen
dos variables estáticas para los valores por defecto del número de procesos y tamaño
de mensaje, respectivamente: PROC DEFAULT y SIZE DEFAULT . Estas variables
serán utilizadas cuando no se den valores especı́ficos en el fichero de configuración.
En caso de estar activada la selección automática de algoritmos (mediante SELECT AUTO), todos los procesos llaman a una función de configuración automática al
inicio de su ejecución (selectAlgorithm). Esta función utilizará el número de procesos y
el tamaño de mensaje para seleccionar la opción adecuada del fichero de configuración
(o coger la opción por defecto en el caso de encontrarse en una situación no configurada). El número de procesos implicados en la comunicación es sencillo de obtener con
la función de MPJ Size() desde cualquiera de los procesos. El tamaño de mensaje, en
las colectivas sin sufijo v, es directamente el del mensaje que se va a enviar o recibir
en cada procesador. Para operaciones en que el tamaño de mensaje sea variable, por
ejemplo Gatherv, se utiliza una función auxiliar que en función del vector de tamaños
calcule un valor medio, el cual será utilizado en la selección del algoritmo.
En cada colectiva, el método que implementa la firma de la operación estándar, es
el que comprueba primero si la selección ha de ser automática. En ese caso, invoca a
la función de selección y obtiene el algoritmo a utilizar. Si el algoritmo seleccionado
de este modo no cumple los prerrequisitos para ejecutarse con la configuración actual
(por ejemplo, si se ha seleccionado un algoritmo BDE y el número de procesadores
no es potencia de dos), se sustituye por la opción por defecto. En caso de que no
esté activada, se utilizará la opción definida por defecto para la primitiva colectiva que
se esté invocando (optionCollective).
A continuación, se muestran las firmas y documentación de los tres métodos más
significativos en la selección automática. En primer lugar, el que elige el algoritmo
adecuado para la operación colectiva indicada en función del tamaño de mensaje y del
número de procesos (selectAlgorithm). El siguiente método es el que lee los valores del
fichero de configuración (getProp). Recibe un parámetro que le indica qué propiedad leer
y un valor por defecto por si falla la lectura, por ejemplo, por no estar configurada esa
opción. Finalmente, se muestra la firma del método que calcula el tamaño de mensaje
4.2. Integración y Selección Automática de Algoritmos
69
medio, utilizado en primitivas colectivas en que los mensajes pueden ser de diferente
tamaño (mean). En la implementación proporcionada, la media se obtiene como media
aritmética de los tamaños de mensaje de cada procesador. Serı́a posible variar esta
implementación sin afectar al resto de métodos implicados.
protected int selectAlgorithm( int
algTag, int
proc, int
msize )
Usage
• It selects the collective algorithm. It uses SELECT AUTO to determine if
the selection should be manual (optionCollective) or automatic.
algTag
collective identifier
proc
number of processes
msize
message size
returns:
algorithm identifier
protected int getProp( java.lang.String
key, int
def )
Usage
• It accesses to properties in configuration file. If an exception occurs, it returns
the default value.
key
key in .properties file
def
property’s default value
returns:
property’s value
protected int mean( int [] vector, int
total )
Usage
• Auxiliary function to obtain the message size in a collective operation
vector
size vector
total
number of sizes
returns:
size value
70
4. Integración y Optimización en Sistemas Multi-core
En el Cuadro 4.2 aparece un ejemplo de configuración. npes representa el número
de procesos y tam el tamaño de mensaje. Cuando no es aplicable la diferencia de región
por tamaño de mensaje, se muestra un guión.
Broadcast
Gather
Gatherv
Scatter
Scatterv
Allgather
Allgatherv
Alltoall
Alltoallv
Reduce
Allreduce
Reduce-Scatter
Scan
Barrier
npes < 8
tam< 8KB
MST
nbFT
nbFT
nbFT
nbFT
nbFTGather
+ MSTBcast
nbFTGather
+ MSTBcast
nbFT
nbFT
bFT
nbFTReduce
+MSTBcast
nbBDE
Secuencial
Árbol binario
npes < 8
tam>= 8KB
MST
nbFT
nbFT
nbFT
nbFT
nbFTGather
+ MSTBcast
nbFTGather
+ MSTBcast
nbFT
bFT
nbFTReduce
+MSTBcast
nbBKT
Secuencial
-
npes >= 8
tam< 8KB
MST
MST
MST
MST
MST
MSTGather
+MSTBcast
MSTGather
+MSTBcast
nbFT
nbFT
MST
MSTReduce
+MSTBcast
nbBDE
Secuencial
Árbol binario
npes >= 8
tam>= 8KB
MST
MST
MST
MST
MST
MSTGather
+ MSTBcast
MSTGather
+ MSTBcast
nbFT
MST
MSTReduce
+MSTBcast
nbBKT
Secuencial
-
Cuadro 4.1: Ejemplo de Configuración de Algoritmos para las Operaciones Colectivas
4.3.
Código de Ejemplo
Hecha la integración, para poder ejecutar un código MPJ para MPJ Express usando
la nueva biblioteca, sólo habrı́a que realizar un fichero de configuración (por ejemplo,
con los algoritmos elegidos en el Cuadro 4.2), compilar nuestro código con MPJ Express
y ejecutarlo. Un ejemplo podrı́a ser el siguiente:
import mpi.*;
public class BroadcastEjemplo {
public static void main(String[] args) throws MPIException {
int my_pe, npes;
MPI.Init(args);
my_pe = MPI.COMM_WORLD.Rank();
npes = MPI.COMM_WORLD.Size();
4.3. Código de Ejemplo
71
MPI.COMM_WORLD.Barrier();
if(my_pe == 0){
System.out.println("BEGIN");
}
String [] arrStr = new String [1];
arrStr[0]= new String("Hello");
MPI.COMM_WORLD.Bcast(arrStr, 0, 1, MPI.OBJECT, 0);
System.out.println("Process["+my_pe+"] Message received:"+arrStr[0]);
MPI.COMM_WORLD.Barrier();
if(my_pe == 0){
System.out.println("THE END");
}
MPI.Finalize();
}
}
Este código se corresponderı́a con la clase “BroadcastEjemplo.java”. En ella, se
importan los paquetes necesarios de MPJ Express, se inicializan los procesos MPJ
(MPI.Init(args)), se realiza la operativa necesaria (en este caso, el Broadcast de una
cadena de caracteres) y se finalizan los procesos MPJ (MPI.Finalize()). Las operaciones que llevan a cabo los procesos, además de obtener el número de procesadores total
(MPI.Size()) y su rango (MPI.Rank()), son sincronizaciones (Barrier) y un Broadcast. Como se puede ver, estas operaciones se enmarcan en un Comunicador global
(MPI.COMM WORLD) que incluye todos los procesos.
Ejecutado con cuatro procesos, el resultado serı́a el siguiente:
BEGIN
Process[0]
Process[1]
Process[2]
Process[3]
THE END
Message
Message
Message
Message
received:Hello
received:Hello
received:Hello
received:Hello
72
4. Integración y Optimización en Sistemas Multi-core
4.4.
Incremento de la Escalabilidad en Operaciones Colectivas
Una de las principales ventajas de la biblioteca desarrollada es la posibilidad de
seleccionar automáticamente los algoritmos que, en función de las caracterı́sticas de
cada caso, proporcionen un mejor rendimiento. Lo más habitual es que cada uno de
los algoritmos presente ventajas e inconvenientes dependiendo de la configuración experimental. Ası́, aunque los MST reducen las comunicaciones entre nodos distintos,
requieren cierto trabajo de inicialización que puede no compensar si el mensaje es pequeño o el número de procesos es reducido y todos forman parte del mismo nodo. Por
ello, disponer de diversas implementaciones por colectiva será beneficioso a la hora de
paralelizar la ejecución de aplicaciones con MPJ mediante la variación de los parámetros
de configuración.
En esta biblioteca, para la elección del algoritmo, se utilizan el tamaño de mensaje
y el número de procesos. Los lı́mites en cada caso configuran cuatro regiones: mensajes
pequeños y pocos procesos, mensajes grandes y pocos procesos, mensajes pequeños y
muchos procesos, mensajes grandes y muchos procesos (exceptuando Alltoallv y Barrier en los que, como se ha comentado anteriormente, no tiene sentido separar por
tamaño de mensaje). La parametrización de la configuración para obtener rendimientos escalables de las operaciones colectivas, requiere un trabajo previo de benchmarking
en la máquina a utilizar. Ası́, se obtendrı́an los parámetros lı́mite en cada colectiva y
las implementaciones adecuadas que maximizan el rendimiento para cada cuadrante
de parámetros de configuración. Esto es ası́ porque dependiendo de la arquitectura
utilizada (el número de cores por núcleo, las interconexiones entre procesadores, etc)
los umbrales pueden variar e incluso los algoritmos por cuadrante pueden ser diferentes (sobre todo, en implementaciones con caracterı́sticas similares, como un BT o un
BDE). Esta función de inicialización se incluirı́a en el inicializador estático de la clase
Intracomm y deberı́a ejecutarse sólo cuando no existiese ya un fichero de configuración.
Una de las motivaciones principales de este proyecto era el proporcionar una biblioteca de operaciones colectivas que aprovechase las arquitecturas clúster multi-core. El
algoritmo que mejor lo hace es el MST. Esto afecta directamente al Broadcast, Gather,
4.4. Incremento de la Escalabilidad en Operaciones Colectivas
73
Reduce, Scatter, primitivas a las que se aplica el algoritmo MST, y todas las que se hayan
implementado como combinación de las mismas (Allgather, Allreduce, Reduce-Scatter),
ası́ como sus variantes terminadas en “v”. Asumiendo que el “mapeo” o asignación de
procesos MPJ a cores, en un clúster multi-core, se realizará asignando rangos próximos
a procesos que estén dentro del mismo procesador, se reducen las comunicaciones entre
nodos de diferentes procesadores. Este algoritmo se basa en la subdivisión del espacio de
procesos maximizando las comunicaciones internas entre subconjuntos y minimizando
aquellas que tienen lugar entre conjuntos diferentes. Por lo tanto, si los procesos de un
mismo conjunto están en el mismo nodo, se produce el efecto indicado anteriormente:
se reducen las comunicaciones entre procesos de diferentes cores.
Otra mejora, que afecta en realidad al uso de MPJ Express, es la reducción de copias
de buffers innecesarios en las operaciones de reducción. Tal y como estaban planteadas
las operaciones de reducción, se asumı́a que tanto buffer de envı́o como de recepción
tenı́an las mismas caracterı́sticas. Esto obligarı́a a hacer copias en vectores temporales
para evitar problemas en caso de diferencias entre ellos, como se vio en la sección
anterior. En el caso de una implementación en árbol plano, esto sólo conlleva una copia
al inicio. Dependiendo de la operación y de quién reciba el resultado, podrı́a ser en
todos o sólo en uno de los nodos. No obstante, en un algoritmo algo más complejo, con
múltiples envı́os y recepciones en varias etapas, esto supondrı́a hacer copias temporales
en cada paso, lo cual afectarı́a de forma importante al rendimiento.
Capı́tulo 5
Evaluación del Rendimiento
5.1.
Configuración Experimental
La evaluación de la biblioteca desarrollada se ha llevado a cabo sobre un clúster
multi-core con 8 nodos, cada uno de ellos con 4 GB de RAM y 2 procesadores Intel
Xeon 5060 dual-core (4 cores por nodo), interconectados mediante InfiniBand (20 Gbps) y Gigabit Ethernet (1 Gbps). Su sistema operativo es CentOS 5.2 con kernel 2.6.18.
La JVM utilizada es la de Sun Microsystems, versión 1.6.0 07. Para esta evaluación se
ha utilizado la biblioteca de paso de mensajes en Java más representativa y extendida, MPJ Express versión 0.27. La biblioteca MPI nativa utilizada ha sido Intel MPI
3.2.0.011 configurada para utilizar InfiniBand. El compilador utilizado es el de Intel
versión 11.0.074. El micro-benchmarking de la biblioteca de operaciones colectivas se
ha realizado utilizando los 32 cores del clúster, a razón de 4 procesos MPJ/MPI por
nodo.
5.2.
Análisis del Rendimiento de la Biblioteca de Colectivas Implementada
Aquı́ se presentan los resultados de rendimiento para las primitivas Broadcast (Figura 5.1), Scatter (Figura 5.2), Allgather (Figura 5.3), Alltoall (Figura 5.4), Reduce
(Figura 5.5) y Allreduce (Figura 5.6) para la biblioteca desarrollada, integrada dentro de MPJ Express (resultados etiquetados con “Nueva”), de forma comparativa con
las primitivas originales de MPJ Express (resultados etiquetados con “Original”). Esta
evaluación ha sido realizada utilizando tanto la red InfiniBand (utilizando el soporte
75
76
5. Evaluación del Rendimiento
proporcionado por la emulación TCP/IP sobre InfiniBand, IPoIB), como la red Gigabit Ethernet. Los datos transferidos son arrays de bytes, evitando de este modo la
serialización, proceso de transformación de los objetos en arrays de bytes, ya que puede penalizar de forma importante el rendimiento en cualquiera los casos. La métrica
utilizada en esta evaluación experimental es el ancho de banda agregado, pues tiene en
cuenta la cantidad total de información transferida por cada primitiva, que es el tamaño
del mensaje multiplicado por el número de procesos, excepto para Allgather y Alltoall,
en donde se multiplica el tamaño de mensaje por el cuadrado del número de procesos.
Los resultados experimentales han sido obtenidos con la suite de micro-benchmarks de
primitivas colectivas MPJ desarrollada en el Grupo de Arquitectura de Computadores
de la UDC [2], debido a la inexistencia de benchmarks adecuados para esta evaluación
del rendimiento.
Los algoritmos utilizados por MPJ Express son los que aparecen en el Cuadro 3
(ver Capı́tulo 3). Para la nueva biblioteca, se han seleccionado los siguientes algoritmos:
MST para el Broadcast, nbFT para el Scatter, Gather (nbFT) + Bcast (MST) para el
Allgather, bFT para el Alltoall, MST para el Reduce, y Reduce (MST) + Bcast (MST)
para el Allreduce. Para la elección, se han tenido en cuenta resultados experimentales
previos. Parte de estos resultados previos se pueden consultar en el Apéndice 2.
Como se puede apreciar en las gráficas, los resultados obtenidos verifican que la
nueva biblioteca de primitivas colectivas aumenta significativamente el rendimiendo
con respecto a las operaciones colectivas originales de MPJ Express. Este incremento
es más acusado en operaciones que usan el algoritmo MST, ya que este permite minimizar el coste de comunicaciones en clusters multi-core. Las operaciones que usan
este algoritmo son el Broadcast, Allgather (debido a que está implementado como un
nbFTGather+MSTBcast), el Reduce, y el Allreduce (al estar implementado como un
MSTReduce+MSTBcast). Esta mejora del rendimiento puede alcanzar los dos órdenes
de magnitud, como se puede apreciar para Allgather con mensajes entre 4 y 64 KB. En
el caso del Scatter y el Alltoall el rendimiento es similar al de la versión original, bien
por usar el mismo algoritmo (como en el caso del Scatter), bien por utilizar algoritmos
poco escalables (Alltoall usa el bFT). En este último caso, se podrı́a haber utilizado
5.2. Análisis del Rendimiento de la Biblioteca de Colectivas Implementada
77
otro algoritmo, de los proporcionados en la nueva biblioteca, más escalable similar al
usado en MPJ Express, con primitivas punto a punto no bloqueantes. No obstante, se
utiliza un bFT a modo de comparación.
Hay casos como el Scatter en que se podrı́a haber utilizado un algoritmo MST. Sin
embargo, en este algoritmo es necesario realizar envı́os a mayores entre nodos intermedios y aunque es beneficioso en caso de tener un comunicador formado por un gran
número de procesos, puede no ser lo más efectivo si el número de nodos es pequeño. En
este caso, tras unas pruebas preliminares (ver Anexo 2), se ha visto que para el Scatter,
Ancho de Banda Agregado [MB/s]
con pocos nodos, es más eficiente usar nbFT.
Broadcast
1600
Nueva (InfiniBand)
Original (InfiniBand)
Nueva (Gb. Eth.)
Original (Gb. Eth.)
1400
1200
1000
800
600
400
200
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura 5.1: Resultados experimentales de Broadcast con 32 cores
Ancho de Banda Agregado [MB/s]
78
5. Evaluación del Rendimiento
Scatter
450
Nueva (InfiniBand)
Original (InfiniBand)
Nueva (Gb. Eth.)
Original (Gb. Eth.)
400
350
300
250
200
150
100
50
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura 5.2: Resultados experimentales de Scatter con 32 cores
Finalmente, es posible apreciar el impacto en el rendimiento del cambio del protocolo de comunicación punto a punto, cuyo umbral está establecido en 512KB para la
biblioteca propuesta y en 128KB por defecto en MPJ Express. Este cambio de protocolo afecta a los envı́os, que necesitan que el receptor indique dónde quiere recibir el
mensaje cuando éste es de un tamaño considerable, pues en caso contrario, no serı́a
posible almacenarlo de forma temporal. Cuando el mensaje es pequeño, se puede utilizar un eager send, es decir, un envı́o no acordado que asume que el receptor tiene
suficiente espacio para almacenar el mensaje y no necesita su autorización explı́cita.
En MPJ Express se utiliza un valor por defecto reducido (128KB), que se ha decidido
incrementar a 512KB, haciendo que sólo se penalicen mensajes de mayor tamaño por
necesitar confirmación explı́cita del receptor (protocolo rendezvous).
Ancho de Banda Agregado [MB/s]
5.2. Análisis del Rendimiento de la Biblioteca de Colectivas Implementada
Allgather
1600
Nueva (InfiniBand)
Original (InfiniBand)
Nueva (Gb. Eth.)
Original (Gb. Eth.)
1400
1200
1000
800
600
400
200
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Ancho de Banda Agregado [MB/s]
Figura 5.3: Resultados experimentales de Allgather con 32 cores
Alltoall
1000
Nueva (InfiniBand)
Original (InfiniBand)
Nueva (Gb. Eth.)
Original (Gb. Eth.)
900
800
700
600
500
400
300
200
100
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura 5.4: Resultados experimentales de Alltoall con 32 cores
79
Ancho de Banda Agregado [MB/s]
80
5. Evaluación del Rendimiento
Reduce
800
Nueva (InfiniBand)
Original (InfiniBand)
Nueva (Gb. Eth.)
Original (Gb. Eth.)
700
600
500
400
300
200
100
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Ancho de Banda Agregado [MB/s]
Figura 5.5: Resultados experimentales de Reduce con 32 cores
Allreduce
600
Nueva (InfiniBand)
Original (InfiniBand)
Nueva (Gb. Eth.)
Original (Gb. Eth.)
500
400
300
200
100
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura 5.6: Resultados experimentales de Allreduce con 32 cores
5.3. Análisis Comparativo del Rendimiento de Colectivas MPJ vs. MPI
5.3.
81
Análisis Comparativo del Rendimiento de Colectivas
MPJ vs. MPI
Otro resultado interesante es el de la comparación entre MPJ y MPI. En las Figuras
que van desde la 5.7, hasta la 5.12, se pueden ver los resultados obtenidos con MPJ
Express, la biblioteca desarrollada y los obtenidos con MPI, sobre InfiniBand. En ellas
se aprecia que MPI presenta unos rendimientos muy superiores a los obtenidos por
colectivas MPJ. Aunque en MPI se observa un ligero descenso del rendimiento a partir
de ciertos tamaños (por el uso de buffers y diferentes protocolos de envı́o), de forma muy
acusada en algunas como Scatter e inapreciable en otras como Allgather, este descenso
no es suficiente para que MPJ lo alcance. La diferencia de rendimiento entre MPJ y
MPI es porcentualmente mucho mayor para mensajes cortos que para mensajes largos,
debido a que MPJ presenta latencias mucho mayores que las de MPI con mensajes
cortos por utilizar una emulación IP sobre InfiniBand, mientras que MPI utiliza un
protocolo de más bajo nivel sobre InfiniBand, IBV (InfiniBand Verbs). Sin embargo,
para mensajes largos, MPJ puede llegar a alcanzar anchos de banda comparables a los
de MPI (aunque todavı́a inferiores). Estas diferencias son especialmente notables para
Alltoall y Reduce
Ancho de Banda Agregado [MB/s]
82
5. Evaluación del Rendimiento
Broadcast
5000
MPI (InfiniBand)
Nueva (InfiniBand)
Original (InfiniBand)
4500
4000
3500
3000
2500
2000
1500
1000
500
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Ancho de Banda Agregado [MB/s]
Figura 5.7: Comparación MPI y MPJ para Broadcast con 32 cores
Scatter
1200
MPI (InfiniBand)
Nueva (InfiniBand)
Original (InfiniBand)
1000
800
600
400
200
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura 5.8: Comparación MPI y MPJ para Scatter con 32 cores
Ancho de Banda Agregado [MB/s]
5.3. Análisis Comparativo del Rendimiento de Colectivas MPJ vs. MPI
83
Allgather
7000
MPI (InfiniBand)
Nueva (InfiniBand)
Original (InfiniBand)
6000
5000
4000
3000
2000
1000
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Ancho de Banda Agregado [MB/s]
Figura 5.9: Comparación MPI y MPJ para Allgather con 32 cores
Alltoall
3000
MPI (InfiniBand)
Nueva (InfiniBand)
Original (InfiniBand)
2500
2000
1500
1000
500
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura 5.10: Comparación MPI y MPJ para Alltoall con 32 cores
Ancho de Banda Agregado [MB/s]
84
5. Evaluación del Rendimiento
Reduce
6000
MPI (InfiniBand)
Nueva (InfiniBand)
Original (InfiniBand)
5000
4000
3000
2000
1000
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Ancho de Banda Agregado [MB/s]
Figura 5.11: Comparación MPI y MPJ para Reduce con 32 cores
Allreduce
4000
MPI (InfiniBand)
Nueva (InfiniBand)
Original (InfiniBand)
3500
3000
2500
2000
1500
1000
500
0
1KB
2KB
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura 5.12: Comparación MPI y MPJ para Allreduce con 32 cores
5.4. Resultados con una Aplicación Java HPC (jGadget)
5.4.
85
Resultados con una Aplicación Java HPC (jGadget)
Gadget-2 [37] es una aplicación de simulación bastante extendida en Cosmologı́a
ya que permite estudiar una gran variedad de problemas, desde la colisión y fusión de
galaxias, hasta la formación de estructuras cosmológicas a gran escala. Esta aplicación
se utiliza constantemente para investigación en Cosmologı́a, siendo una buena medida
de popularidad el haber sido citado en más de 700 artı́culos de investigación hasta el
momento, destacando su uso en la “Millennium Simulation” [38], la mayor simulación Nbody (con más de 1010 partı́culas) del Universo desde sus orı́genes hasta hoy, ejecutada
sobre 512 procesadores y que requirió 350000 horas de CPU y 1TB de memoria. Gadget2 está escrito en C y paralelizado con MPI. Además, hace uso de la biblioteca cientı́fica
del GNU (GNU Scientific Library, GSL) y la biblioteca FFTW, con soporte opcional
para Hierarchical Data Format (HDF5).
La estrategia de paralelización es una descomposición del dominio que se realiza
de forma irregular y adaptada dinámicamente, con abundantes comunicaciones entre
procesos. Este código ha sido portado a Java (jGadget) [39] y paralelizado usando
MPJ Express para explorar las capacidades de Java en códigos de simulación real. Esta
versión en Java no tiene la posibilidad de usar funciones GSL ni bibliotecas FFTW,
pero es portable y facilita enormemente el desarrollo de aplicaciones.
En la Figura 5.13 se proporcionan los resultados obtenidos con la aplicación jGadget usando las colectivas originales de MPJ Express y las de la biblioteca desarrollada.
Las pruebas han sido realizadas con la misma configuración que las de los apartados
anteriores. Se puede ver que la nueva biblioteca de colectivas proporciona una mayor
escalabilidad, puesto que, aunque con pocos procesadores los resultados son similares
para ambas bibliotecas, cuando el número de procesos aumenta, disminuye el rendimiendo de las primitivas de MPJ Express, mientras que, para la biblioteca desarrollada, se
incrementa. Además, la diferencia de rendimiento es considerable. Este problema de
escalabilidad se hace patente a partir de 16 procesadores sobre Gigabit Ethernet y a
partir 32 para InfiniBand.
86
5. Evaluación del Rendimiento
Rendimiento de jGadget
3500
Original (Gig. Eth.)
Nueva (Gig. Eth.)
Original (InfiniBand)
Nueva (InfiniBand)
Tiempo (segundos)
3000
2500
2000
1500
1000
500
0
1
2
4
8
Número de Procesos
16
32
Figura 5.13: Resultados obtenidos con la aplicación jGadget
Capı́tulo 6
Principales Aportaciones y
Conclusiones
A pesar de las reticencias iniciales debido a su menor rendimiento, el uso de Java en
Computación de Altas Prestaciones está siendo objeto de un creciente interés gracias a
que, además de ventajas como seguridad, robustez, orientación a objetos, expresividad
y sencillez, entre otras muchas, incluye en el núcleo del lenguaje un completo soporte
multithread y comunicaciones de red. Además, la compilación a código nativo en tiempo
de ejecución mediante el compilador Just-In-Time hace que el rendimiento de Java hoy
dı́a sea competitivo y pueda ser utilizado en entornos en los que el rendimiento es
crı́tico. Por otro lado, el paradigma de programación con paso de mensajes es la opción
preferida a la hora de paralelizar aplicaciones en las arquitecturas más populares hoy en
dı́a, los clúster multi-core. Todo ello, lleva a que haya un gran interés en el desarrollo
de proyectos de bibliotecas de paso de mensajes sobre Java (MPJ). Dentro de las
bibliotecas de paso de mensajes, las operaciones colectivas juegan un importante papel
por representar patrones de comunicación muy habituales y, por tanto, es crucial su
rendimiento y escalabilidad.
El objetivo de este proyecto es presentar una biblioteca de primitivas colectivas de
paso de mensajes en Java que permita obtener rendimientos escalables sobre diferentes
configuraciones y que sea portable. Tras una análisis de las soluciones existentes y del
estándar MPJ, se desarrolló una biblioteca que permite seleccionar, de forma automática, el algoritmo más conveniente para una determinada configuración, entre el conjunto
de los implementados para cada operación colectiva. La biblioteca ha sido integrada
87
88
6. Principales Aportaciones y Conclusiones
en MPJ Express y se ha evaluado su rendimiento comparándolo con las primitivas
colectivas originales de MPJ Express y con el de una implementación MPI.
6.1.
Principales Aportaciones
Las principales aportaciones de este trabajo son las siguientes:
Se trata de la primera implementación libre y portable de una biblioteca de primitivas colectivas de paso de mensajes en Java. Además se trata de la primera
biblioteca de operaciones colectivas que incluye varios algoritmos por cada operación.
Diseño y desarrollo de un sistema de selección automática de algoritmos en tiempo
de ejecución con el objetivo de maximizar el rendimiento para cada configuración.
La biblioteca desarrollada contiene la documentación de las funciones colectivas
del estándar MPJ más completa hasta la fecha.
La biblioteca desarrollada es totalmente portable, al estar basada en funciones
del estándar MPJ. Se ha demostrado integrando esta biblioteca de forma sencilla
en la implementación MPJ Express. Como ya se ha mencionado anteriormente,
la inclusión de la biblioteca desarrollada en la distribución oficial se realizará en
breve.
Evaluación del rendimiento de primitivas y de los distintos algoritmos de forma
comparativa con respecto a MPJ Express y a MPI.
Obtención de una publicación nacional derivada del trabajo del proyecto: S. Ramos, G.L. Taboada,J. Touriño y R. Doallo, “Biblioteca de Primitivas Colectivas
en Paso de Mensajes para Java en Sistemas Multi-core”, a presentar en las XX
Jornadas de Paralelismo [40].
Finalmente, de forma colateral, se han corregido ciertos bugs en el manejo de
buffers en las operaciones de reducción de MPJ Express y en alguna primitiva
colectiva como el Reduce.
6.2. Conclusiones
6.2.
89
Conclusiones
En este proyecto se presenta una biblioteca de primitivas colectivas de paso de mensajes en Java que proporciona la implementación de varios algoritmos por operación
colectiva, permitiendo obtener rendimientos escalables sobre diferentes configuraciones
de procesos. La posibilidad de seleccionar dichos algoritmos en tiempo de ejecución de
forma automática y configurable facilita el incremento del rendimiento de las aplicaciones Java en computación de altas prestaciones, ya que se incluyen algoritmos que
adaptan los patrones de comunicación a distintas arquitecturas, los clúster multi-core
entre ellos, con el objeto de maximizar su eficiencia.
Un punto importante es que la implementación se ha llevado a cabo sobre primitivas
punto a punto del estándar MPJ, lo que hace que la biblioteca sea portable y posibilita
su integración en distintas implementaciones MPJ. De hecho, en este proyecto, se ha
integrado en MPJ Express de forma inmediata. Además, la evaluación experimental ha
mostrado significativas mejoras en el rendimiento en comparación con las operaciones
originales.
Finalmente, este proyecto plantea nuevas lı́neas de investigación, como la profundización en la configuración automática para proporcionar ficheros de configuración
adaptados a un sistema concreto. Otro posible camino es la combinación de algoritmos
en dos niveles, pensando en arquitecturas jerárquicas, ya que un algoritmo puede maximizar el rendimiento en una comunicación intranodo, mientras que otro puede ser el
algoritmo ideal para comunicaciones internodo. De este modo es posible incrementar el
rendimiento y la escalabilidad de Java en sistemas multi-core.
Apéndice A
Planificación
A continuación, se detalla la planificación del trabajo realizado desde comienzos de
julio de 2008 hasta julio de 2009.
Se ha planificado un trabajo de ocho horas diarias los meses de verano (julio, agosto
y septiembre) y de unas dos horas y media durante los meses que abarcan el curso 20082009, siempre contando cinco dı́as laborables por semana. Este cambio de horario es
debido a que, durante ese periodo, la realización del proyecto ha tenido que combinarse
con las tareas y clases de quinto curso.
Las actividades planificadas (ver figura A.1 se dividen en documentación, análisis
y diseño, implementación de colectivas, pruebas unitarias, integración, pruebas de integración, benchmarking, redacción de la memoria y preparación de la presentación.
Análisis y diseño abarca tanto el análisis y diseño de algoritmos para cada primitiva
como el análisis y diseño de la integración. Estas actividades de análisis de algoritmos,
implementación y pruebas unitarias, se han ido solapando de manera que, para cada
colectiva, se realiza un ciclo completo que abarca: análisis y diseño, implementación
y pruebas. Hecho esto, es cuando se lleva a cabo la integración, las pruebas de integración y el benchmarking. El diagrama de Gantt de la figura A.2 muestra todas las
actividades, aunque el detalle de las relativas a cada colectiva aparece en la figura A.3.
Con esta planificación, el número de horas de trabajo asciende a 1.068 horas, repartidas en 273 dı́as, comenzando el 1 de julio de 2008 y terminando el 16 de julio de
2009.
A lo largo de la realización del proyecto aparecen varios hitos que representan con91
92
A. Planificación
troles de evaluación del desarrollo del proyecto por parte de los directores. No implican
horas de trabajo del realizador del proyecto, con lo cual, no se incluyen como actividades propiamente dichas, sino como puntos de control. Uno de los directores, o ambos,
evalúan el trabajo hecho hasta ese punto y comunican su parecer. Esto podrı́a ocasionar
una replanificación de ciertas fases en caso de que haya que corregir de forma importante el producto de alguna de las actividades ya realizadas. Estimando un trabajo de
4 horas por control para el director que evalúe el proyecto y teniendo en cuenta que el
segundo director sólo participa en el control de la planificación inicial, el de las colectivas y el de la memoria, y que el primer director está presente en todas, tenemos que
sumar 24 horas de trabajo del primer director y 12 del segundo. Considerando además
un trabajo previo de preparación de cada revisión, se estima un total de 36 horas para
el segundo director y 72 horas para el primero.
Si suponemos un coste aproximado en función del valor de mercado, el coste estimado del trabajo del proyecto serı́a el que aparece en la tabla A.
Realizador del
proyecto
Primer director
Segundo director
Horas de trabajo
1.068 h
Coste por hora
30 e/h
Coste total.
32.040 e
72 h
36 h
45 e/h
60 e/h
TOTAL
3.240 e
2.160 e
37.340 e
Cuadro A.1: Coste estimado de realización del proyecto.
Por otro lado, si contabilizamos el esfuerzo en lı́neas de código desarrolladas, se
puede cuantificar como 5.210 SLOC (Source Lines Of Code) [36]. En un estudio del
coste de los proyectos de desarrollo software en computación de altas prestaciones [41] se
indica que el coste estimado por lı́nea de código es de 75$ por SLOC. Esto resultarı́a en
un coste total de 390.750$, con lo que queda patente la elevada productividad alcanzada
en el desarrollo del presente proyecto.
Finalmente, es necesario remarcar que MPJ Express ha tenido más de 2000 descargas desde la web de su proyecto y cuenta con varios cientos de desarrolladores activos,
por lo que hay un amplio número de potenciales usuarios de la biblioteca desarrollada.
93
Figura A.1: Actividades
94
A. Planificación
Figura A.2: Diagrama de Gantt
95
Figura A.3: Detalle de planificación de realización de colectivas
Apéndice B
Resultados Experimentales
Adicionales
A continuación se proporcionan las gráficas de resultados experimentales obtenidos
en el supercomputador Finis Terrae (se presenta su descripción a continuación) para
varias colectivas comparando algunos de los algoritmos implementados. Para cada función colectiva, se muestran dos gráficas, una con tamaños de mensaje pequeños, en los
que se compara el tiempo de ejecución o latencia, y otra con mensajes mayores, en las
que aparece el ancho de banda agregado. La implementación con el nombre “Original”,
indica que el algoritmo usado es el proporcionado por MPJ Express (se puede consultar
en el Cuadro 3, en el Capı́tulo 3).
Esta evaluación adicional del rendimiento de la biblioteca desarrollada se ha realizado en un nodo del supercomputador Finis Terrae [42] ubicado en el Centro de
Supercomputación de Galicia (CESGA). Dicho nodo es un HP Integrity rx7640 con 16
cores Intel IA64 Itanium2 Montvale a 1,6 GHz y 128 GB de memoria.
Los resultados contenidos en este Anexo presentan una evaluación preliminar del
rendimiento de la biblioteca de operaciones colectivas desarrollada. Ası́, pueden servir
para seleccionar el algoritmo más adecuado para una configuración en particular, en este
caso, un nodo con 16 cores. No obstante, al no ser tan intenso el benchmarking como
en el caso del realizado para el capı́tulo de resultados experimentales (en número de
iteraciones del programa de prueba), los resultados presentan una mayor variabilidad
e inestabilidad. Sin embargo, el objetivo de la evaluación preliminar no es conocer
el rendimiento exacto de esta biblioteca, sino cuál es el algoritmo que maximiza el
97
98
B. Resultados Experimentales Adicionales
rendimiento en una determinada configuración. Finalmente, se indica que el tamaño
del mensaje en esta evaluación preliminar se corresponde con el tamaño global del
array procesado, y no el de cada mensaje individual enviado, con el fin de reducir el
número de pruebas a realizar.
99
Broadcast (latencia mensajes cortos)
600
Original
bFT
nbFT
MST
550
Tiempo (µs)
500
450
400
350
300
250
200
150
16B
32B
64B
128B
256B
512B
1KB
2KB
Ancho de Banda Agregado [MB/s]
Tamaño del Mensaje
Broadcast (ancho de banda mensajes largos)
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
4KB
Original
bFT
nbFT
MST
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.1: Comparación de algoritmos para Broadcast con 16 cores
4MB
100
B. Resultados Experimentales Adicionales
Scatter (latencia mensajes cortos)
540
Original
MST
520
Tiempo (µs)
500
480
460
440
420
400
380
360
16B
32B
64B
128B
256B
512B
1KB
2KB
Ancho de Banda Agregado [MB/s]
Tamaño del Mensaje
Scatter (ancho de banda mensajes largos)
800
Original
MST
700
600
500
400
300
200
100
0
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.2: Comparación de algoritmos para Scatter con 16 cores
4MB
101
Tiempo (µs)
Gather (latencia mensajes cortos)
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
16B
Original
bFT
nbFT
MST
32B
64B
128B
256B
512B
1KB
2KB
Ancho de Banda Agregado [MB/s]
Tamaño del Mensaje
Gather (ancho de banda mensajes largos)
500
Original
bFT
nbFT
MST
450
400
350
300
250
200
150
100
50
0
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.3: Comparación de algoritmos para Gather con 16 cores
4MB
102
B. Resultados Experimentales Adicionales
Allgather (latencia mensajes cortos)
4000
Original
bBDE
nbBKT
MSTGather+MSTBcast
bBKT
nbBDE
3600
Tiempo (µs)
3200
2800
2400
2000
1600
1200
800
16B
32B
64B
128B
256B
512B
1KB
2KB
Ancho de Banda Agregado [MB/s]
Tamaño del Mensaje
Allgather (ancho de banda mensajes largos)
2400
2200
2000
1800
1600
1400
1200
1000
800
600
400
200
0
4KB
Original
bBDE
nbBKT
MSTGather+MSTBcast
bBKT
nbBDE
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.4: Comparación de algoritmos para Allgather con 16 cores
4MB
103
Alltoall (latencia mensajes cortos)
2800
Original
bFT
nb1FT
nb2FT
2700
Tiempo (µs)
2600
2500
2400
2300
2200
2100
2000
16B
32B
64B
128B
256B
512B
1KB
2KB
Ancho de Banda Agregado [MB/s]
Tamaño del Mensaje
Alltoall (ancho de banda mensajes largos)
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
4KB
Original
bFT
nb1FT
nb2FT
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.5: Comparación de algoritmos para Alltoall con 16 cores
4MB
104
B. Resultados Experimentales Adicionales
Reduce (latencia mensajes cortos)
950
Original
bFT
MST
Tiempo (µs)
900
850
800
750
700
650
600
16B
32B
64B
128B
256B
512B
1KB
2KB
Ancho de Banda Agregado [MB/s]
Tamaño del Mensaje
Reduce (ancho de banda mensajes largos)
350
Original
bFT
MST
300
250
200
150
100
50
0
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.6: Comparación de algoritmos para Reduce con 16 cores
4MB
105
Reduce−Scatter (latencia mensajes cortos)
4000
Original
nbBKT
bBKT
nbBDE
bBDE
Tiempo (µs)
3500
3000
2500
2000
1500
1000
500
16B
32B
64B
128B
256B
512B
1KB
2KB
Ancho de Banda Agregado [MB/s]
Tamaño del Mensaje
Reduce−Scatter (ancho de banda mensajes largos)
120
Original
nbBKT
bBKT
nbBDE
bBDE
100
80
60
40
20
0
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.7: Comparación de algoritmos para Reduce-Scatter con 16 cores
4MB
106
B. Resultados Experimentales Adicionales
Allreduce (latencia mensajes cortos)
18000
Original
MSTReduce+MSTBcast
nbBDE
bBDE
16000
12000
10000
8000
6000
4000
2000
0
16B
32B
64B
128B
256B
512B
1KB
2KB
Tamaño del Mensaje
Ancho de Banda Agregado [MB/s]
Tiempo (µs)
14000
Allreduce (ancho de banda mensajes largos)
700
Original
MSTReduce+MSTBcast
nbBDE
bBDE
600
500
400
300
200
100
0
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.8: Comparación de algoritmos para Allreduce con 16 cores
4MB
107
12000
11000
10000
9000
8000
7000
6000
5000
4000
3000
2000
1000
0
16B
Original
nbFT (Corregido)
Secuencial
32B
64B
128B
256B
512B
1KB
2KB
Tamaño del Mensaje
Ancho de Banda Agregado [MB/s]
Tiempo (µs)
Scan (latencia mensajes cortos)
Scan (ancho de banda mensajes largos)
160
Original
nbFT (Corregido)
Secuencial
140
120
100
80
60
40
20
0
4KB
8KB
16KB
32KB
64KB 128KB 256KB 512KB
1MB
2MB
Tamaño del Mensaje
Figura B.9: Comparación de algoritmos para Scan con 16 cores
4MB
Bibliografı́a
[1] “MPI: A Message-Passing Interface Standard. Message Passing Interface Forum.” Disponible en http://www.mpi-forum.org/docs/mpi-11-html/
mpi-report.html.
[2] Taboada, G. L., Touriño, J., and Doallo, R., “Performance Analysis of Java
Message-Passing Libraries on Fast Ethernet, Myrinet and SCI Clusters,” Proc.
5th IEEE Intl. Conf. on Cluster Computing (CLUSTER’03), pp. 118–126, 2003.
[3] Barchet-Estefanel, L. A. and Mounie, G., “Fast Tuning of Intra-cluster Collective Communications,” in Proc. 11th European PVM/MPI Users’ Group Meeting
(EuroPVM/MPI’04), LNCS vol. 3241, (Budapest, Hungary), pp. 28 – 35, 2004.
[4] Chan, E., Heimlich, M., Purkayastha, A., and van de Geijn, R. A., “Collective
Communication: Theory, Practice, and Experience,” Concurrency and Computation: Practice and Experience, vol. 19, no. 13, pp. 1749–1783, 2007.
[5] Thakur, R., Rabenseifner, R., and Gropp, W., “Optimization of Collective Communication Operations in MPICH,” Intl. Journal of High Performance Computing
Applications, vol. 19, no. 1, pp. 49–66, 2005.
[6] Vadhiyar, S. S., Fagg, G. E., and Dongarra, J. J., “Towards an Accurate Model
for Collective Communications,” Intl. Journal of High Performance Computing
Applications, vol. 18, no. 1, pp. 159–167, 2004.
[7] “mpiJava Home Page.” Disponible en http://www.hpjava.org/mpiJava.html.
Última actualización, Mayo 2007.
109
110
BIBLIOGRAFÍA
[8] Carpenter, B., Getov, V., Judd, G., Skjellum, A., and Fox, G., “MPI for Java. Position Document and Draft API Specification.” Disponible en http://www.npac.
syr.edu/projects/pcrc/reports/MPIposition/position.ps, 1998.
[9] Carpenter, B., Getov, V., Judd, G., Skjellum, A., and Fox, G., “MPJ: MPI-like
Message Passing for Java,” no. 11, pp. 1019–1038, 2000.
[10] Java Grande Forum. http://www.javagrande.org.
[11] Lim, S. B., “Platforms for HPJava: Runtime Support for Scalable Programming
in Java.” Disponible en http://www.hpjava.org/theses/slim/dissertation/
dissertation/dissertation.html, 2007.
[12] “Mpiwiki:Community Portal.” Disponible en http://www.hpjava.org/mpiwiki/
index.php/Mpiwiki:Community_Portal. Última actualización, Abril 2007.
[13] Taboada, G. L., Touriño, J., and Doallo, R., “Configuración y Evaluación de Bibliotecas de Paso de Mensajes Java en Clústers,” in XIV Jornadas de Paralelismo,
2003.
[14] Baker, M., Carpenter, B., Fox, G., Ko, S., and Lim, S., “mpiJava: an ObjectOriented Java Interface to MPI,” in Proc. 1st Intl. Workshop on Java for Parallel
and Distributed Computing (IWJPDC’99), LNCS vol. 1586, (San Juan, Puerto
Rico), pp. 748–762, 1999.
[15] “JPVM. The Java Parallel Virtual Machine.” http://www.cs.virginia.edu/
~ajf2j/jpvm.html. Última actualización, Febrero 1999.
[16] “P2P-MPI.” http://grid.u-strasbg.fr/p2pmpi/. Última actualización, 2005.
[17] Genaud, S. and Rattanapoka, C., “P2P-MPI: A Peer-to-Peer Framework for Robust Execution of Message Passing Parallel Programs,” Journal of Grid Computing, vol. 5, no. 1, pp. 27–42, 2007.
[18] “JHPC:
List
of
Research
Topics.”
http://research.ac.upc.edu/CAP/
SeminariCAP/SEM9899/jordig/main.html. Última actualización, 1998.
BIBLIOGRAFÍA
111
[19] Dincer, K., “jmpi and a Performance Instrumentation Analysis and Visualization
Tool for jmpi,” in In First UK Workshop on Java for High Performance Network
Computing, Europar’98, 1998.
[20] Kaminsky, A., “Parallel Java Library.” http://www.cs.rit.edu/~ark/pj.shtml.
Última actualización, Julio 2008.
[21] A.Kaminsky, “Parallel Java: A Unified API for Shared Memory and Cluster Parallel Programming in 100 % Java,” in Proc. 9th Intl. Workshop on Java and Components for Parallelism, Distribution and Concurrency (IWJacPDC’07), (Long
Beach, CA, USA), p. 196a (8 pages), 2007.
[22] Zhang, B., “Jcluster. A Java Parallel Environment.” http://vip.6to23.com/
jcluster/. Última actualización, 2004.
[23] Zhang, B.-Y., Yang, G.-W., and Zheng, W.-M., “Jcluster: an Efficient Java Parallel
Environment on a Large-scale Heterogeneous Cluster,” Concurrency and Computation: Practice and Experience, vol. 18, no. 12, pp. 1541–1557, 2006.
[24] Pugh, B. and Spacco, J., “MPJava: High-Performance Message Passing in Java
using Java.nio,” 2003.
[25] Al-Jaroodi, J., Mohamed, N., Jiang, H., and Swanson, D., “JOPI: A Java ObjectPassing Interface,” in Concurrency and Computation: Practice and Experience,
2005.
[26] Al-Jaroodi, J. and Mohamed, N., “JOPI: Java Object-Passing Interface. User’s
Guide,” 2004.
[27] “Website del Proyecto MPJ Express.” Disponible en http://mpj-express.org/.
[28] Shafi, A., Carpenter, B., and Baker, M., “Nested Parallelism for Multi-core HPC
Systems using Java,” Journal of Parallel and Distributed Computing, (In press).
[29] Bornemann, M., van Nieuwpoort, R. V., and Kielmann, T., “MPJ/Ibis: A Flexible
and Efficient Message Passing Platform for Java,” Recent Advances in Parallel Virtual Machine and Message Passing Interface, vol. 3666/2005, pp. 217–224, 2005.
112
BIBLIOGRAFÍA
[30] Taboada, G. L., Touriño, J., and Doallo, R., “F-MPJ: Scalable Java Messagepassing Communications on Parallel Systems,” Journal of Supercomputing, 2009
(In press).
[31] Taboada, G. L., Touriño, J., and Doallo, R., “Java Fast Sockets: Enabling Highspeed Java Communications on High Performance Clusters,” Computer Communications, vol. 31, no. 17, pp. 4049–4059, 2008.
[32] “JavaNow: A Linda Based Parallel Framework.” http://code.google.com/p/
javanow/. Última actualización, 2008.
[33] Bhatti, S. A., Dickens, P. M., and Thiruvathukal, G. K., “JavaNow: A Framework
for Parallel Computing on Networks of Workstations.”
[34] Gray, P. A. and Sunderam, V. S., “IceT: Distributed Computing and Java,” Concurrency: Practice and Experience, vol. 9, no. 11, pp. 1161–1167, 1997.
[35] “JavaParty - Java’s Companion for Distributed Computing.” http://svn.
ipd.uni-karlsruhe.de/trac/javaparty/wiki/JavaParty?redirectedfrom=
WikiStart.
[36] Wheeler, D. A., “SLOCCount.” http://www.dwheeler.com/sloccount/.
[37] Springel, V., “The Cosmological Simulation Code GADGET-2,” Monthly Notices
of the Royal Astronomical Society, vol. 364, no. 4, pp. 1105–1134, 2005.
[38] Springel, V., White, S., and Jenkins, A. e. a., “Simulations of the Formation,
Evolution and Clustering of Galaxies and Quasars,” Nature, vol. 435, no. 7042,
pp. 629–636, 2005.
[39] Baker, M., Carpenter, B., and Shafi, A., “MPJ Express Meets Gadget: Towards
a Java Code for Cosmological Simulations,” in 13th European PVM/MPI Users’
Group Meeting (EuroPVM/MPI’06), LNCS 4192, Springer-Verlag, (Bonn, Germany), pp. 358–365, 2006.
BIBLIOGRAFÍA
113
[40] Ramos, S., Taboada, G. L., , Touriño, J., and Doallo, R., “Biblioteca de Primitivas
Colectivas en Paso de Mensajes para Java en Sistemas Multi-core,” in Actas de las
XX Jornadas de Paralelismo (JP’09), (A Coruña, Spain), p. (In press), 2009.
[41] Reifer, D. J., “Let the Numbers Do the Talking,” March 2002.
[42] Centro de Supercomputación de Galicia (CESGA), “Descrición do Supercomputador Finis Terrae.” http://www.cesga.es/content/view/917/115/ [Última actualización, 2009].