paralelización de un algoritmo para la detección de clusters de
TRANSCRIPT
UNIVERSIDAD DE CHILE FACULTAD DE CIENCIAS FÍSICAS Y MATEMÁTICAS DEPARTAMENTO DE CIENCIAS DE LA COMPUTACIÓN
PARALELIZACIÓN DE UN ALGORITMO PARA LA DETECCIÓN DE CÚMULOS DE GALAXIAS
MEMORIA PARA OPTAR AL TÍTULO DE INGENIERO CIVIL EN COMPUTACIÓN
MARCEL ANDRE AUGSBURGER BECERRA
PROFESOR GUIA:
NANCY HITSCHFELD KAHLER
MIEMBROS DE LA COMISION: LUIS CAMPUSANO BROWN
JORGE PÉREZ ROJAS
SANTIAGO DE CHILE ENERO 2012
2
Tabla de contenido
Resumen ......................................................................................................................................................... 4
1. Introducción ........................................................................................................................................... 5
1.1. La nueva astronomía ...................................................................................................................... 5
1.2. La computación paralela ................................................................................................................ 6
1.3. Motivación ..................................................................................................................................... 7
1.3.1. Importancia del análisis de cúmulos de galaxias ................................................................... 7
1.3.2. Importancia del análisis paralelo ........................................................................................... 7
1.3.3. Otras aplicaciones del software ............................................................................................. 8
1.4. Objetivos ........................................................................................................................................ 8
1.5. Contenido de la memoria ............................................................................................................... 9
2. Antecedentes .......................................................................................................................................10
2.1. Tipos de paralelismo ....................................................................................................................10
2.2. Herramientas ................................................................................................................................12
2.3. Algoritmo secuencial de detección de cúmulos de galaxias ........................................................14
3. Análisis ..................................................................................................................................................17
Arquitectura de ................................................................................................................................17
3.1. Hadoop .........................................................................................................................................17
3.1.1. Distribución de datos ...........................................................................................................18
3.1.2. Procesamiento de datos .......................................................................................................19
3.1.3. Escalabilidad .........................................................................................................................21
3.1.4. HDFS .....................................................................................................................................21
3.1.5. MapReduce ..........................................................................................................................24
3.2. Arquitectura de Vocludet .............................................................................................................26
3.3. Versión disponible de Vocludet ...................................................................................................29
3.4. Alternativas de implementación ..................................................................................................32
3
3.4.1. MapReduce de Hadoop ........................................................................................................32
3.4.2. Pig .........................................................................................................................................32
3.4.3. Streaming .............................................................................................................................33
3.5. Alternativa escogida .....................................................................................................................34
4. Diseño e Implementación ....................................................................................................................35
4.1. Diseño de la solución ....................................................................................................................35
4.1.1. División de los datos .............................................................................................................36
4.1.2. Procesamiento de los datos .................................................................................................39
4.1.3. Re ensamblaje de los resultados ..........................................................................................40
4.1.4. Diseño final ...........................................................................................................................41
4.2. Código Implementado ..................................................................................................................43
5. Evaluación ............................................................................................................................................52
5.1. Datos de prueba ...........................................................................................................................52
5.2. Pruebas .........................................................................................................................................54
5.2.1. Particiones ............................................................................................................................54
5.2.2. Ensamblaje ...........................................................................................................................54
5.2.3. Verificación de datos y procedimientos ...............................................................................57
5.3. Desempeño ..................................................................................................................................60
5.3.1. Uso de recursos ....................................................................................................................60
5.3.2. Desempeño comparativo .....................................................................................................61
6. Conclusiones.........................................................................................................................................65
7. Anexos ..................................................................................................................................................66
Tabla 3: Distribución de cúmulos por tamaño .........................................................................................66
Mapper Function 2 celdas 10% de overlap ..............................................................................................67
Re Assembly Function ..............................................................................................................................70
8. Bibliografía ...........................................................................................................................................72
4
Resumen
Dados los avances en la tecnología, la astronomía es capaz de recolectar del orden de terabytes de datos
por noche. El análisis manual de ésta es prácticamente imposible, por lo que es constante la necesidad
de nuevos y mejores algoritmos para realizar análisis automático. Por otro lado, la computación paralela
provee herramientas para realizar análisis de datos masivos, las que incrementan la capacidad total de
procesamiento y disminuyen el tiempo requerido.
Existe un software para la búsqueda de cúmulos de galaxias, el cual funciona de forma secuencial. Hacer
que este software funcione en forma paralela sería de gran utilidad, dada la cantidad de datos que
existen y existirán para analizar. El objetivo de esta memoria es diseñar e implementar una solución
computacional que permita efectuar la detección de cúmulos de galaxias en forma paralela.
La paralelización del algoritmo se hizo sobre el framework Hadoop, utilizando la herramienta Streaming
con el lenguaje Python para el desarrollo del software. Se construyó una aplicación que divide los datos
de entrada de forma inteligente, ejecuta el algoritmo de detección de cúmulos de galaxias en varias
máquinas de forma paralela, y re ensambla los resultados parciales obtenidos. Se estudiaron estrategias
para el particionamiento de los datos, utilizando celdas con distintas geometrías. También se estudiaron
e implementaron estrategias para el re ensamblado de los resultados. En base a conocimientos
astronómicos y experimentación se determinó la utilidad, dado el contexto, de cada estrategia, y los
valores límites para sus parámetros.
Los resultados son los siguientes: (1) un software paralelo para la detección de cúmulos de galaxias; (2) al
correr el programa paralelo con dos particiones, el tiempo de ejecución se reduce a la mitad; (3) el
software secuencial de detección de cúmulos se observa altamente dependiente de las particiones
utilizadas, encontrándose para una partición de 2 celdas sólo un 11% de los cúmulos que se detectaban
en la versión secuencial.
Como trabajo futuro se propone: (1) modificar el software secuencial de búsqueda de cúmulos de
galaxias para que detecte cúmulos sin miembros repetidos; (2) ejecutar este software en un clúster de
computadores o con cloud computing, para medir las mejoras en tiempo; (3) la ejecución de este
software con sets de datos más grandes, para medir la escalabilidad de éste; (4) crear una partición ad-
hoc al set de datos.
5
1. Introducción
Desde la antigüedad, diferentes culturas y personas han creado catálogos de estrellas. Al principio se
hacían observaciones a ojo desnudo, hasta que en 1610 Galileo Galilei apuntó su telescopio al cielo. Al
irse mejorando las capacidades de los telescopios, la cantidad de información astronómica y por ende los
catálogos estelares fueron creciendo en gran escala. Hoy en día las herramientas de observación
permiten escanear el cielo y guardar los datos de manera automática, generando terabytes de
información sólo de pequeñas áreas del cielo.
Al haber tanta información, el análisis manual de ésta es prácticamente imposible, por lo que es
constante la necesidad de nuevos y mejores algoritmos computacionales para realizar análisis
automático. A pesar de ser mucho más eficiente, la gran cantidad de datos hace que el análisis
computacional requiera de mucho tiempo de procesamiento. La computación paralela puede disminuir
los costos en tiempo, ya que permite aumentar los recursos asignados a cada tarea.
La computación paralela divide un problema en varios sub problemas, los que son resueltos de manera
concurrente. De tal manera se puede resolver cada sub problema con distintos recursos, pudiendo
distribuir la carga en equipos que pueden estar separados físicamente. Sin embargo, los programas
paralelos agregan dificultad en los procesos de división del problema, en la concurrencia, y en el re
ensamblado de la solución completa a partir de las soluciones parciales.
Existe un software desarrollado por Daniel Pizarro et al, el cual aplica un algoritmo basado en el Voronoi
Tessellation Technique (1) para la detección de cúmulos de galaxias. Este software lleva por nombre
Vocludet. Uno de los componentes del Vocludet es el Voronoi Tessellation Maximum Likelihood
Estimator, en adelante VTMLE, el cual lleva a cabo el proceso de búsqueda de cúmulos (2). Este software
funciona de manera secuencial.
1.1. La nueva astronomía
Gracias a los avances en la tecnología de los telescopios, algoritmos de detección, y hardware
computacional, la recolección de datos astronómicos es actualmente del orden de terabytes por noche
(4). Este incremento en la obtención de datos ha ocurrido en muchas áreas de la ciencia, cambiando la
forma en que se hace la investigación y transformándola en análisis de datos sobre bases de datos con
mucha información. En el área de la astronomía se habla del Observatorio Virtual, donde se puede
acceder a la información de distintos centros de datos de manera relativamente fácil y transparente. Se
6
están aportando datos y relacionándolos entre sí a tasas cada vez mayores, lo cual ha tornado la
obtención de datos por este medio en algo tan importante como la obtención de datos a través de
telescopios.
En los últimos años se han puesto a disposición resultados de numerosos estudios con distintos tipos de
información astronómica. En particular existen datos e interés en la estructura a gran escala del
universo, donde han jugado un rol importante las observaciones de cúmulos de galaxias, al servir para
contrastarlas con las predicciones de diferentes modelos teóricos.
Los cúmulos de galaxias, mencionados previamente, son sistemas que contienen desde 50 a 1000
galaxias unidas por atracción gravitacional mutua; las galaxias son sistemas de estrellas, las cuales
poseen típicamente cientos de miles de millones de estrellas. Estos cúmulos han sido detectados
históricamente en forma visual, y recientemente se han aplicado algoritmos para la detección
automática de éstos.
El sondeo 2dF Galaxy Redshift Survey (2dFGRS), llevado a cabo por el Observatorio Anglo-Australiano
entre 1997 y 2002, generó variados tipos de información respecto a galaxias, incluyendo su distribución
al determinar su posición en tres dimensiones (5). La estimación de distancias es posible gracias a la
información del corrimiento al rojo, que es el aumento de la longitud de onda equivalente al Efecto
Doppler en las ondas físicas.
1.2. La computación paralela
La computación paralela es la utilización simultánea de múltiples recursos de computación para resolver
problemas computacionales.
Tradicionalmente, el software ha sido escrito para ser ejecutado en forma secuencial. Esto significa que
para resolver un problema, el algoritmo que se implementa debe ser escrito como una secuencia de
instrucciones. Como restricción se tiene que estas instrucciones deben ser ejecutadas una a la vez, y
solamente en el orden especificado por el algoritmo. Cada una de estas instrucciones es ejecutada por
una CPU (Central Processing Unit). El tiempo que demora la resolución del problema depende de la
eficiencia del algoritmo y de la rapidez con la que la CPU realiza los cálculos, haciendo que este último
factor haya sido y siga siendo sujeto de estudio.
7
Por décadas, la velocidad de procesamiento de los computadores ha ido incrementando según la Ley de
Moore, esto es, duplicándose cada aproximadamente dos años (6). Este incremento se ha debido en
gran medida al aumento de la frecuencia de reloj de los procesadores, sin embargo se está llegando a un
límite conocido como power wall, debido al consumo exponencial de energía con el aumento en la
frecuencia de operaciones, y el consiguiente aumento de temperatura. Existen también cuellos de
botella como la rapidez de acceso a la memoria, por lo que el desarrollo de procesadores más rápidos
no necesariamente va a significar un incremento en la eficiencia final de los computadores.
En la búsqueda de cómo continuar incrementando la capacidad de procesamiento, y tomando en cuenta
otros factores limitantes, se ha considerado el paralelismo como buena solución. La computación
paralela se ha usado históricamente, mostrando buenos resultados en distintas áreas, como en
computación de alto rendimiento, servidores, aceleración gráfica, y muchos sistemas embebidos (7). Así
podemos encontrar actualmente diseños de procesadores con múltiples núcleos, los cuales permiten
realizar varios cálculos por ciclo, para computadores multipropósito.
1.3. Motivación
Existe un software para la búsqueda de cúmulos de galaxias, el cual funciona de forma secuencial. Hacer
que este software funcione en forma paralela es de gran utilidad, dada la cantidad de datos que existen y
existirán a futuro para analizar.
1.3.1. Importancia del análisis de cúmulos de galaxias
Los cúmulos de galaxias son de gran interés para la astronomía. En las últimas décadas han tenido un
importante rol en el estudio cosmológico: sirven para reconocer las estructuras en gran escala del
universo, su densidad numérica se puede contrastar con predicciones de distintos modelos, y además
sirven para monitorear la formación y evolución de las galaxias (9).
1.3.2. Importancia del análisis paralelo
Como se mencionó, hoy se cuenta con un observatorio virtual con grandes cantidades de datos. Futuros
sondeos prometen aumentar el volumen de datos en órdenes de magnitud, produciendo una llamada
“avalancha de datos astronómicos”.
8
La ciencia en general ha logrado producir más datos de los que se pueden analizar, y dada esta
problemática, es de gran importancia primero lograr el análisis automático de los datos, e
inmediatamente lograr paralelizarlo para aminorar los tiempos de procesamiento. Lo relevante de esta
tarea para la astronomía, la aplicabilidad en otras ramas de la ciencia, y la conocida dificultad de
paralelizar procesos, hacen de éste un tema interesante como memoria.
1.3.3. Otras aplicaciones del software
El análisis de cúmulos es un problema recurrente en varias áreas, entre éstas el aprendizaje automático,
reconocimiento de patrones, análisis de imágenes, recuperación de la información y bioinformática.
Dado que el software desarrollado se creó de manera flexible, éste puede ser usado no sólo en el análisis
de datos astronómicos, sino que también en cualquiera de las áreas recién mencionadas. Si bien la
aplicación secuencial ya lo permitía, el desarrollo de este análisis en paralelo abre las puertas al
procesamiento de órdenes de magnitud mayor de datos, pudiéndose atacar problemas que de otra
manera hubiese sido imposible.
1.4. Objetivos
El objetivo general es diseñar e implementar una solución computacional que permita detectar cúmulos
de galaxias en forma paralela, utilizando un framework que permita distribuir la carga de procesamiento.
Los objetivos específicos son:
Construir una aplicación utilizando el framework Hadoop para llevar a cabo el análisis de datos
en forma paralela. Lo anterior considera el diseño e implementación de un programa que realice
una buena partición de los datos, resuelva el problema sobre cada partición, y calcule la solución
completa re ensamblando los resultados parciales.
Evaluar si se efectúa una mejora a la solución existente, lo que implica comparar la aplicación
secuencial y la aplicación paralela en término de tiempos y consumo de recursos.
Determinar las diferencias y similitudes entre los resultados obtenidos por el programa
secuencial y el paralelo .
9
1.5. Contenido de la memoria
En la sección 2 se presentan las tecnologías disponibles y el algoritmo que se quiere paralelizar. En la
sección 3 se analizan las herramientas con las que se cuenta para el desarrollo del software, se comparan
las diferentes alternativas y su estado, y se decide cuáles se van a utilizar y de qué forma. En la sección 4
se presentan las decisiones que se consideraron para el diseño del software, se propone un diseño final,
y se muestra y explica el código implementado. En la sección 5 se presentan los experimentos realizados
para probar el software y determinar su eficacia y eficiencia. Se incluye una descripción de los datos de
prueba, las pruebas mismas, y el desempeño del software. Finalmente, en la sección 6 se presentan las
conclusiones a las que se llegó con este trabajo, según los objetivos que se habían planteado, y se
proponen acciones para continuarlo a futuro.
10
2. Antecedentes
En este capítulo se presentan los antecedentes del problema, tales como las tecnologías disponibles y el
algoritmo que se quiere paralelizar.
2.1. Tipos de paralelismo
La computación paralela se puede llevar a cabo en varios niveles, desde la cantidad de bits que se
pueden procesar por ciclo, la cantidad de instrucciones que se pueden procesar en forma paralela, la
paralelización en procesamiento de datos, hasta la paralelización de tareas. De todos modos, sólo se
pueden realizar ciertos cálculos en paralelo: algunos deben ser secuenciales pues dependen de
resultados obtenidos por cálculos previos. Se puede hacer una clasificación de los computadores que
trabajan en forma paralela según el nivel al cual el hardware provee el paralelismo. Para el paralelismo a
nivel de núcleos de procesamiento, esta clasificación es a grandes rasgos análoga a la distancia entre los
nodos básicos de computación, y no son mutuamente excluyentes.
Computación Multinúcleo: Un procesador multinúcleo es aquel que posee múltiples unidades de
ejecución (núcleos) en el mismo chip. Estos procesadores tienen la capacidad de ejecutar
múltiples instrucciones por ciclo, provenientes de múltiples flujos de instrucciones.
Multiprocesamiento Simétrico: Un multiprocesador simétrico es un computador que posee
múltiples procesadores idénticos, los cuales comparten el acceso a la memoria a través de un
único bus de datos. Esto permite asignar más de un procesador a una o varias tareas. Dado el
diseño del acceso a datos, los multiprocesadores simétricos suelen no tener más de 32
procesadores.
Computación Distribuida: La computación distribuida es un modelo de computación donde se
usan múltiples computadores para la resolución de un problema. Estos se coordinan entre sí a
través de un sistema distribuido, definido como: una colección de computadores separados
físicamente y conectados entre sí por una red de comunicaciones distribuida, donde cada
máquina posee sus componentes de hardware y software que el usuario percibe como un solo
sistema. Se define como nodo a una unidad individual, típicamente una máquina, dentro de un
sistema distribuido.
El framework utilizado en el presente trabajo se construyó para que pudiese ser utilizado por cualquiera
de las tres clases de computación paralela, abstrayéndose de este nivel y funcionando indiferente a la
11
clase de paralelismo y cantidad de núcleos utilizados. Sin embargo, presta su verdadera contribución al
ser aplicada con computación distribuida, al crecer la capacidad de procesamiento de datos en paralelo
en órdenes de magnitud. Para la cantidad de datos que genera la ciencia, y la astronomía en particular,
se hace necesario que el procesamiento de información se haga con computación distribuida.
La computación a gran escala presenta un gran número de dificultades. La mayoría de éstas derivan de la
coordinación entre múltiples máquinas para la solución de un problema, y las consiguientes fallas que se
pueden presentar, tanto de comunicación, como de los nodos en sí. En un ambiente distribuido, las fallas
parciales son esperadas e incluso comunes: las redes pueden experimentar variados tipos de problemas
derivados de fallas físicas o de congestión, los cuales impiden que los datos lleguen a destino en el
momento que debían; los nodos pueden dejar de funcionar debido a problemas de hardware, software,
o de capacidad; los datos pueden estar corrompidos por causas en la generación, transmisión o por
ataques maliciosos; puede haber diferencias en el protocolo de transmisión de diferentes versiones del
software; los relojes pueden desincronizarse; pueden ocurrir problemas de sincronización en el acceso a
datos; etc. El resto del sistema distribuido debiese ser capaz de recuperarse en forma independiente del
elemento fallido, para cada uno de los casos posibles de falla (8).
Además de los problemas asociados con fallas en la red o en los nodos, también existe el problema de
que los nodos poseen hardware con capacidad finita. Los elementos más importantes para estos efectos
son el tiempo de procesador, la memoria, el espacio en disco y el ancho de banda de la red. Cada nodo
tiene por lo general algunos gigabytes de memoria, lo que implica que si el set de datos es de algunos
terabytes, se necesitarían del orden de miles de nodos para guardar la información en RAM. Los discos
duros proveen un espacio de almacenamiento mucho mayor, que pueden ser del orden de terabytes. Sin
embargo, los sets de datos que se generan al procesar la información pueden llegar a ocupar mucho más
espacio que el input original, lo que podría llevar a sobrepasar la capacidad de almacenamiento de un
nodo y recurrir a la de otro. El ancho de banda es un recurso escaso incluso en una red interna, y mucho
más si los nodos están separados por medianas o grandes distancias. Es fácil que se saturen las redes al
haber grandes cantidades de datos siendo transferidos, y que por ello se pierdan datos o no lleguen en el
momento requerido.
Un sistema distribuido de gran escala debe poder manejar los recursos previamente mencionados en
forma eficiente, buscando un equilibrio entre el funcionamiento del sistema en forma íntegra y el
procesamiento de datos.
12
El desafío más grande en el diseño de sistemas distribuidos es la sincronización entre los nodos. Si éstos
tienen la capacidad de comunicarse entre sí de manera explícita, se enfrentan riesgos como producir
más llamados a procedimientos remotos de los que soporta el sistema, bloqueos mutuos sobre los datos
y otras condiciones de carrera. Además está la complejidad de diseñar un sistema que sea capaz de
recuperarse ante el fallo de un nodo, con poca pérdida de recursos y redistribuyendo la carga de manera
eficiente en el resto del sistema.
2.2. Herramientas
La programación funcional utiliza frecuentemente las funciones map y reduce, las que sirvieron como
base para que Google creara el framework MapReduce (10). Este modelo de programación tiene la
ventaja de proveer automáticamente paralelización y distribución, además de ser tolerante a fallas. Éste
fue utilizado por el framework Apache Hadoop, el cual permite crear programas paralelos.
Map-reduce trabaja con pares de key/values (llave/valores): la función map recibe uno de estos pares, lo
procesa y genera un set de pares intermedios; la función reduce procesa los pares intermedios para
generar uno o varios valores finales. Esto se ve ilustrado en el siguiente ejemplo, donde se cuentan las
palabras de un texto:
int resultado=0
La función map recibe un texto, valorEntrada, y emite un par por cada palabra, donde el key es la
palabra y el value es 1. En este programa en particular no se utiliza el primer parámetro recibido,
llaveEntrada. La función reduce recibe entonces un key, llaveIntermedia, y una lista con el valor 1
repetido tantas veces como apariciones del key haya habido en el texto original, valoresIntermedios, y
emite la suma de los valores de la lista.
13
Tanto la función map como reduce se pueden ejecutar en forma paralela. Los datos de entrada se
pueden distribuir en tantos map como se estime conveniente, usualmente la misma cantidad que el
número de procesadores disponibles. Luego se crean tantas funciones reduce como keys hayan, las
cuales son procesadas en forma paralela por los procesadores que se asignen.
El software se instaló y configuró en varios modos para evaluar la ejecución. También se investigaron
herramientas disponibles y se escribieron programas básicos para evaluar la complejidad de desarrollar
con este framework. También se buscó información para determinar si el framework es capaz de
manejar la gran cantidad de datos que se requiere en la búsqueda de cúmulos de galaxias. Según los
creadores, éste es capaz de manejar del orden de petabytes, lo cual es más que suficiente considerando
que la cantidad de datos astronómicos que se quieren procesar es de alrededor de 6 terabytes. Además,
ya se ha utilizado en el procesamiento de imágenes astronómicas en universidades como la Washington
University (11).
Respecto a dónde y cómo se debería ejecutar el software desarrollado para procesar la información: el
framework está pensado para ser usado con cualquier cantidad de recursos, pero en particular funciona
bien con cloud computing. El modelo de programación map-reduce tiene la ventaja de que la
escalabilidad es directa, por lo que no es necesario esfuerzo alguno para ejecutarlo en una cantidad
mayor de máquinas. Al utilizar cloud computing se pueden arrendar un número variable de recursos, por
lo que se puede reducir el tiempo de procesamiento de forma proporcional a la cantidad de
procesadores que se arrienden. En caso de aumentar la cantidad de datos a procesar, se pueden
arrendar más recursos, pudiendo mantenerse el tiempo de procesamiento relativamente independiente
de la cantidad de datos de entrada. Además existen proyectos de grandes compañías como The New
York Times y famosas universidades como Stanford, que se han llevado a cabo con Apache Hadoop sobre
la plataforma Amazon Elastic Compute Cloud (12).
Haciendo una breve comparación con otros sistemas de computación paralela sobre grandes volúmenes
de datos, existen otras alternativas como Condor, pero ésta no distribuye los datos automáticamente
como Apache Hadoop. Esto significa que el control de datos se debe realizar de forma manual, y que la
colaboración entre nodos se debe programar con sistemas de comunicación como MPI, lo cual conlleva
un gran esfuerzo y aumenta la probabilidad de errores. Esto no es necesario en Apache Hadoop, pues
son tareas automatizadas que constituyen parte del framework, y de las cuales el programador se puede
abstraer. Si bien la eficiencia puede ser menor que al computarlo con otros sistemas, la ganancia en
14
facilidad para el programador, la escalabilidad directa, y los bajos costos de cloud computing hacen de
este framework una buena opción para el proyecto.
2.3. Algoritmo secuencial de detección de cúmulos de galaxias
Daniel Pizarro desarrolló un software llamado Vocludet para la detección de cúmulos de galaxias (2). Éste
consta de dos partes, el algoritmo VTMLE y el DePropris, siendo el primero utilizado para la detección de
cúmulos de galaxias.
El algoritmo VTMLE recibe como entrada un set de puntos en un espacio de tres dimensiones, donde
cada punto representa las coordenadas de una galaxia que hay en el espacio que se quiere analizar. Con
esta información se calcula el diagrama de Voronoi que se construye a partir de los puntos, el cual
entrega una estimación de la densidad local de galaxias para cada punto del set. Se itera sobre los datos
de entrada para encontrar posibles cúmulos de galaxias, lo que consta de las siguientes fases:
Detección de semilla: Para esto se busca el área de mayor densidad de galaxias, lo que equivale a
buscar la celda de menor volumen en el diagrama. Se debe verificar que la semilla encontrada no
sea parte de un cúmulo encontrado en una iteración previa.
Detección de miembros del cúmulo (Algoritmo 1): Partiendo con la semilla como cúmulo base, se
trata de aumentar el valor resultante de una función de probabilidad, el Estimador de Máxima
Verosimilitud (EMV), agregando los puntos vecinos al cúmulo. Este proceso se lleva a cabo hasta
que una de las siguientes dos condiciones deja de cumplirse:
o No se puede incrementar el EMV del conjunto al agregar alguno de sus vecinos. En este
caso se encontró un cúmulo y se guardan los datos.
o El elemento agregado forma parte de otro cúmulo. En este caso se descarta la semilla
que originó este cúmulo.
El algoritmo recibe el diagrama de Voronoi previamente calculado con la herramienta Qhull. Este
software es capaz de generar el diagramas de Voronoi en varias dimensiones. En particular sirve para
generar el diagrama de Voronoi en tres dimensiones.
A continuación, se presenta el pseudocódigo del algoritmo para el caso de la búsqueda de un cúmulo:
15
Algoritmo 1: Algoritmo VTMLE
Este pseudocódigo debe enmarcarse dentro del algoritmo general, que busca todos los cúmulos de
galaxias en una zona del espacio. En éste se deben hacer la búsqueda de la semilla y el conteo de las
celdas usadas y cúmulos de galaxias encontrados, e invocar al algoritmo de búsqueda individual para
encontrar posibles cúmulos de galaxias a partir de cada semilla.
Se presenta el pseudocódigo del algoritmo general a continuación:
16
Algoritmo 2: Algoritmo de detección de cúmulos
17
3. Análisis
En este capítulo se analizan las herramientas con las que se cuenta para el desarrollo del software, se
comparan las diferentes alternativas y su estado, y se decide cuáles se van a utilizar de qué forma.
3.1. Arquitectura de Hadoop
Hadoop es un framework que permite procesar datos usando el modelo de programación map-reduce.
Está diseñado para procesar grandes volúmenes de información de manera eficiente a través de
computación distribuida, conectando computadores y coordinándolos para trabajar juntos en paralelo.
Permite trabajar con diferentes cantidades de nodos, permitiendo escalabilidad desde un servidor hasta
miles de máquinas, cada una con capacidad de computación y almacenamiento locales. El sistema está
diseñado para detectar y manejar fallas en el nivel de aplicación. Esto permite despreocuparse de contar
con hardware de alta disponibilidad, ya que el sistema es capaz de soportar y manejar fallas en
cualquiera de los nodos del cluster.
Si bien existen otros sistemas que realizan procesamiento de grandes volúmenes de datos en sistemas
distribuidos, Hadoop tiene la ventaja de proveer un modelo de programación simple, el cual permite
escribir y testear sistemas distribuidos de forma rápida. Además provee un sistema eficiente de
distribución automática de datos y trabajo en el conjunto de nodos, y también dentro de cada nodo con
sus respectivos núcleos.
El framework está constituido por las siguientes componentes, que en su totalidad permiten la creación
y ejecución de programas distribuidos y en forma paralela:
Hadoop Common: Un set de herramientas que sirven de base para otros componentes y
proyectos de Hadoop. Esto incluye el sistema de archivos básico, manejo de llamadas a
procedimientos remotos, y bibliotecas de serialización.
Hadoop Distributed File System (HDFS): Un sistema de archivos distribuido, que provee un rápido
acceso y alto flujo de datos. Es el sistema primario de almacenamiento de datos usado por las
aplicaciones de Hadoop, el cual replica los bloques de datos y los distribuye en nodos del cluster.
Es esta distribución y redundancia la que permite el acceso rápido y la tolerancia a fallos en los
nodos del cluster.
18
Hadoop MapReduce: Un framework de software para el procesamiento distribuido de grandes
cantidades de datos en clusters de computadores. Éste provee un modelo de programación que
permite crear aplicaciones que procesan datos en forma paralela.
Existen también proyectos que extienden las capacidades de Hadoop, agregando funcionalidades o
usándolo como base para realizar tareas de más alto nivel. Entre éstos destacan lenguajes de alto nivel
que facilitan el acceso y procesamiento de datos.
3.1.1. Distribución de datos
En un cluster de Hadoop, los datos se distribuyen a todos los nodos a medida que éstos se van cargando
en el sistema, como muestra la Figura 1. El HDFS se encarga de particionar los datos y distribuirlos en
forma redundante, de tal manera que cada conjunto de datos se encuentre replicado en más de un
nodo. Esto evita pérdida o inaccesibilidad a cualquier conjunto de datos, en caso de presentarse fallas en
algún nodo o en las redes. Un sistema de monitoreo activo se encarga de replicar los datos en respuesta
a fallas en el sistema. A pesar de su distribución en varios nodos, los conjuntos de datos forman parte del
mismo espacio de nombres, por lo que son fácil y completamente accesibles.
Al cargar la información al sistema, cada archivo se divide en líneas, o en algún otro formato específico
para la lógica del programa en particular. A esto se le llama orientación a registros, donde cada registro
es una línea o su símil. Cada proceso que esté corriendo en cada nodo procesa un subconjunto de estos
registros, siendo el framework quien organiza los datos que se procesan, según la proximidad que éstos
tengan a los datos. De esta forma, la mayoría de los datos son leídos directamente desde el disco duro
local, aliviando la carga en las redes y evitando transferencias innecesarias. La estrategia utilizada para la
organización tareas consiste en mover la capacidad de computación hacia los datos, en vez de mover los
datos. Esto se traduce en una alta localización de los datos, que a su vez resulta en un alto desempeño.
19
Figura 1: Distribución de datos en Hadoop
3.1.2. Procesamiento de datos
Hadoop limita la cantidad de comunicación que puede ocurrir entre los procesos, ya que cada registro es
procesado por una tarea aislada del resto. Aunque esto suena como una gran limitación en un principio,
hace que el framework sea mucho más confiable. Hadoop no está diseñado para ejecutar cualquier
programa y distribuirlo en un cluster, sino que los programas deben estar escritos en acuerdo a un
modelo de programación en particular, llamado MapReduce.
En MapReduce, los registros son procesados de forma aislada por tareas llamadas mappers, las que
suelen utilizarse para clasificar datos. En caso de ser necesario, un proceso llamado shuffle toma los
resultados obtenidos por todos los mappers, y los agrupa según estas clasificaciones. Finalmente los
datos son procesados por tareas llamadas reducers, donde cada una recibe datos pertenecientes a una o
más clasificaciones, y las procesa. La Figura 2 muestra el proceso recién descrito.
20
Figura 2: Procesos de map y reduce en Hadoop
Los nodos de un cluster de Hadoop se comunican entre sí, pero a diferencia de otros sistemas
distribuidos, donde los desarrolladores de la aplicación ordenan explícitamente los flujos de datos de
nodo a nodo a través de buffers MPI, la comunicación en Hadoop se lleva a cabo de forma implícita. Los
subconjuntos de datos pueden ser etiquetados, lo que es utilizado por Hadoop para saber cómo enviar la
información a un nodo destinatario común. Hadoop maneja internamente todas las transferencias de
datos y los asuntos referentes a la topología del cluster.
Al restringir la comunicación entre los nodos, Hadoop hace que el sistema distribuido sea mucho más
confiable. Las fallas individuales en los nodos pueden solucionarse reiniciando las tareas en otras
máquinas. Dado que las tareas a nivel de usuario no se comunican explícitamente entre sí, no es
necesario un intercambio de mensajes en los programas de usuario, ni tampoco definir puntos de
retorno para retomar la ejecución en caso de fallas. Ante la falla en un nodo, el resto de los nodos
continúan operando como si nada hubiese pasado, dejando las complicaciones del reinicio parcial del
programa a la capa subyacente de Hadoop.
21
3.1.3. Escalabilidad
Una de los grandes beneficios de Hadoop en comparación a otros sistemas distribuidos, es su curva
plana de escalabilidad. El rendimiento de Hadoop puede no ser espectacular si es que se ejecuta con
pocos datos bajo un pequeño número de nodos, ya que el sobrecosto asociado a iniciar programas de
Hadoop es relativamente alto. Otros paradigmas de programación paralelos o distribuidos como MPI
(Message Passing Interface) pueden presentar un mejor desempeño al ejecutarse con menos de diez
máquinas. Aunque el esfuerzo de coordinación entre un pequeño número de máquinas puede ser mejor
llevado por tales tipos de sistemas, el costo que se paga en rendimiento y esfuerzo por parte de los
ingenieros al añadir más hardware como resultado del incremento en el volumen de datos, crece de
forma no lineal.
Un programa escrito en otros frameworks distribuidos, pueden requerir grandes cantidades de
refactorización al escalar de diez a cien o a mil máquinas. Esto puede requerir que el programa se
reescriba varias veces; elementos fundamentales de su diseño pueden también poner un tope a la escala
de crecimiento de la aplicación.
Hadoop, en contraste, está específicamente diseñado para tener una curva de escalabilidad muy plana.
Luego de que un programa en Hadoop está escrito y funcionando en diez nodos, muy poco o nulo
trabajo es requerido para correr el mismo programa sobre una cantidad mucho mayor de hardware. Se
requiere muy poco trabajo extra sobre una aplicación para poder crecer en órdenes de magnitud. La
plataforma de Hadoop subyacente maneja los recursos de datos y hardware, y provee un crecimiento en
el rendimiento dependiente, proporcional al número de máquinas disponibles.
3.1.4. HDFS
El sistema de archivos distribuidos de Hadoop, HDFS, es un sistema de archivos diseñado para guardar
grandes cantidades de datos (terabytes e incluso petabytes), y proveer acceso de alto flujo de datos a
esta información. Los archivos son almacenados de manera redundante a través de múltiples máquinas,
para asegurar su resistencia a fallas y su alta disponibilidad a aplicaciones altamente paralelas, tal como
se muestra en la Figura 3.
22
Figura 3: HDFS de Hadoop
Un sistema de archivos distribuido está diseñado para almacenar grandes cantidades de datos y proveer
acceso a estos datos a la mayor cantidad de clientes distribuidos a través de la red. Existen varios
sistemas de archivos distribuidos que resuelven este problema de diferentes maneras, como el NFS
(Network File System) de Linux.
HDFS está diseñado para ser robusto frente a un gran número de problemas a los que otros sistemas de
archivos distribuidos, como NFS son vulnerables. En particular:
El HDFS está diseñado para almacenar una gran cantidad de información (terabytes o petabytes).
Esto requiere distribuir los datos en un gran número de máquinas. También es capaz de manejar
tamaños de archivo mucho mayores a los que puede manejar NFS.
El HDFS almacena los datos de manera confiable. Si falla alguna máquina del cluster, los datos
deberían seguir estando disponibles.
El HDFS provee acceso rápido y escalable a esta información. Debiese ser posible proveer
servicios a un número mayor de clientes sólo agregando más máquinas al cluster.
El HDFS se integra bien con el Hadoop MapReduce, permitiendo que los datos sean leídos y
computados localmente cuando sea posible.
23
El HDFS, sin embargo, no es de propósito general, por lo que hay decisiones de diseño que lo hacen
óptimo para trabajar con Hadoop, pero no para otros fines. En particular:
Se asume que el diseño de las aplicaciones que usan HDFS realiza lecturas a los archivos de
forma secuencial y larga. El HDFS está optimizado para proveer lectura con altos flujos de datos.
Si bien esto es bueno para la mayoría de los casos de uso, tiene un sobrecosto al intentar
acceder a posiciones arbitrarias de un archivo, ya que se realiza una búsqueda secuencial que
toma un tiempo no determinado.
Dados los grandes tamaños de los archivos, y la naturaleza secuencial de las lecturas, el sistema
no provee un mecanismo para almacenamiento en caché de forma local. El sobrecosto del
almacenamiento en caché es suficientemente grande como para preferir que los datos
simplemente sean releídos desde la fuente HDFS.
Se asume que máquinas individuales van a fallar a una tasa relativamente frecuente, tanto de
manera intermitente como permanente. El clúster debe ser capaz de soportar la falla completa
de varias máquinas, posiblemente todas ocurriendo al mismo tiempo (por ejemplo si falla un
rack por completo). Aunque el rendimiento puede degradarse proporcionalmente al número de
máquinas que se hayan perdido, el sistema completo no debería ponerse demasiado lento, ni se
debería perder información. Las estrategias de replicación de datos combaten este problema.
El diseño del HDFS está basado en el diseño de GFS, el sistema de archivos de Google. Su diseño fue
descrito en un paper publicado por Google (14).
El HDFS es un sistema de archivos con estructura de bloques: cada archivo es dividido en bloques de un
tamaño determinado. Estos bloques son almacenados en el clúster en una o más máquinas con
capacidad de almacenamiento de datos. A estas máquinas se les llama DataNodes. Un archivo puede
estar compuesto por varios bloques, y éstos no están almacenados necesariamente en la misma
máquina. Así el acceso a un archivo puede requerir la cooperación de múltiples máquinas, pero es capaz
de manejar tamaños de archivo mucho más grandes; cada archivo puede requerir más espacio de lo que
es capaz de almacenar un solo disco duro.
Si varias máquinas deben estar involucradas para proveer acceso a un archivo, entonces un archivo
puede no estar disponible si se pierde acceso a alguna de estas máquinas. El HDFS resuelve este
problema replicando cada bloque en varias máquinas, 3 por defecto.
24
La mayoría de los sistemas de archivos con estructura de bloques usan un tamaño de bloque del orden
de 4 a 8 KB. En HDFS, el tamaño de bloque por defecto es de 64 MB, órdenes de magnitud más grande.
Esto permite que el HDFS disminuya la cantidad espacio requerido para almacenar metadatos por cada
archivo (las listas de bloques por archivo son menores a medida que el tamaño de los bloques
incrementa). La consecuencia de esta decisión es que el HDFS espera manejar archivos grandes, y espera
que ellos sean leídos de manera secuencial. Los metadatos son manejados por una única máquina,
llamada NameNode.
3.1.5. MapReduce
Hadoop implementa un paradigma computacional llamado map-reduce, el cual divide la aplicación en
fragmentos de trabajo. Cada uno de los fragmentos puede ser ejecutado o re ejecutado en cualquier
nodo del cluster. Un trabajo MapReduce divide el set de datos de entrada en trozos independientes, los
cuales son procesados por las tareas map en forma paralela. El framework clasifica el output de los
maps, los cuales son entonces entregados como input de las tareas “reduce”, que también son
ejecutadas de forma paralela. Típicamente, ambos el input y el output de cada trabajo son almacenados
en el sistema de archivos HDFS. El framework se hace cargo de la organización temporal, monitoreo y re
ejecución, en caso de fallo, de las tareas. Por lo general, los nodos de computación y de almacenamiento
son los mismos, es decir, el framework MapReduce y el HDFS son ejecutados en el mismos set de nodos.
El framework MapReduce consiste de un único JobTracker maestro, encargado de manejar el trabajo en
su totalidad, y un TaskTracker esclavo por nodo del cluster, encargados del manejo de las tareas
específicas asignadas a cada nodo del cluster. El maestro es responsable de la distribución espacial y
temporal de las tareas que componen un trabajo, asignando y luego monitoreando el estado de las
tareas en los esclavos. También se encarga de la re ejecución de las tareas fallidas.
Para ejecutar una tarea, se debe especificar la ubicación del input/output y proveer las funciones map y
reduce. Para que estas funciones sean válidas y puedan ejecutarse, deben implementar interfaces
apropiadas y/o clases abstractas. La ubicación del input/output, las funciones map y reduce, y otros
parámetros de los trabajos comprenden la configuración del trabajo. El JobClient de Hadoop entrega el
trabajo (jar, ejecutable u otros) y la configuaración al JobTracker. Éste asume la responsabilidad de
distribuir ambos a los esclavos, organizándolos temporalmente y monitoreándolos, además de
constantemente proveer la información de estado y diagnóstico al JobClient.
25
El framework MapReduce opera exclusivamente con pares key/values, es decir, el framework ve el input
del trabajo como un set de pares key/values y produce sets de pares key/values como output del
trabajo, posiblemente de tipos distintos. Las clases key y values deben ser serializables por el framework,
y por ende necesitan implementar la interfaz Writable.
26
3.2. Arquitectura de Vocludet
Vocludet (Voronoic based Cluster Detector) es un software desarrollado para el análisis de datos
astronómicos, en particular para la detección de cúmulos de galaxias. Éste fue desarrollado por Daniel
Pizarro para su tesis de magister en ciencias, mención computación (2). Está escrito principalmente en
Java, sin embargo hace llamados a programas en C y utiliza shell scripting para uno de sus módulos.
El input para la aplicación son datos provenientes de diferentes sets de datos, no necesariamente en el
mismo formato, y no necesariamente completos. Se necesita que el proceso sea igual sin importar qué
tipo de datos sean ingresados al sistema. El output de la aplicación debe ser uniforme, sin importar el
tipo de datos ingresados, y puede ser necesario adaptar datos para ser leídos por muchas aplicaciones
distintas.
Figura 4: Modelo pipe-and-filter de Vocludet
El problema de los distintos formatos de input es solucionado por Pizarro et al con la implementación de
un modelo pipe-and-filter presentado en la Figura 4. Existen tres funciones principales, a partir de las
cuales se crean tres grupos de filtros. El primer grupo de filtros, llamado Formatted Data Readers (FDR)
27
está compuesto por módulos que leen información de diversas fuentes y los escriben en un formato
común. El segundo grupo de filtros, llamados Internal Data Processing (IDP) tiene módulos que procesan
la información escrita en el formato común, escribiendo también sus resultados en formato común. El
tercer grupo de filtros, llamados Formatted Data Writers (FDW) leen datos escritos en el formato común
y escriben la información en diferentes formatos, según lo especificado por el usuario final.
Una gran ventaja es que diferentes niveles de complejidad están encapsulados en diferentes grupos de
filtros. Es claro que la complejidad relacionada con la heterogeneidad de los formatos de entrada está
encapsulada en el primer grupo de filtros (FDR), donde cada módulo es básicamente un parser. La
complejidad relacionada con el procesamiento de datos está encapsulada en el segundo grupo de filtros
(IDP), los que siempre funcionan con tipos de dato homogéneo para sus inputs, mejorando la eficiencia.
Finalmente, el tercer grupo de filtros (FDW) encapsulan la complejidad relacionada a los formatos de
output, entregando facilidad en la personalización y creación de nuevos formatos de output. El plan de
ejecución de estos módulos se encuentra en otro módulo: el módulo de Script, el cual encapsula la
especificación del plan de ejecución en la aplicación.
Esta arquitectura es muy flexible, ya que no está restringida a una secuencia input-procesamiento-
output, sino que puede tener múltiples ejecuciones de procesos externos que incorporan nueva
información al proceso.
La secuencia de ejecución es la especificación del orden temporal en que los módulos son ejecutados en
la aplicación.
28
Figura 5: Ejecución simple de Vocludet
La ejecución más simple de Vocludet es presentada en el diagrama de secuencia de la Figura 5. La
secuencia es la siguiente:
El módulo Script es invocado, y va a ser quien esté a cargo de invocar a todos los otros módulos.
Un módulo Reader es invocado: Lee datos desde una Formatted Data Source (fuente de datos
inicial en algún formato muy probablemente distinto al formato común interno), y escribe la
información al componente Interna Data (datos internos escritos en el formato común). Esto
corresponde a la primera capa.
Un módulo de Data Processing (procesamiento de datos) es ejecutado, el cual lee los datos y
escribe los resultados en el componente Internal Data. Esto corresponde a la segunda capa.
Un módulo Writer (escritor) es invocado, el cual lee información desde el componente Internal
Data y la escribe como Formatted Data Output (datos en algún formato especificado por el
usuario final). Esto corresponde a la tercera capa.
29
Los componentes básicos de la arquitectura son:
Script: Un Script es un shell script que define la sucesión de líneas de comando de los módulos que
deben ejecutarse para realizar un proceso de detección de clusters.
Módulos: Un Módulo es cualquier programa ejecutable mediante la línea de comandos sin intervención
del usuario (no interactiva).
Según esta definición, el “proceso de detección de cúmulos” es especificado en el Script, y define una
ejecución secuencial de uno o más Módulos.
3.3. Versión disponible de Vocludet
Se realizaron varios experimentos para ver el estado del software con que se trabajó. En primer lugar se
determinó que la versión disponible de Vocludet encuentra cúmulos con galaxias repetidas en más de un
cúmulo. Esto es contrario a lo que se describe en el algoritmo en (2), y en el algoritmo descrito en la
sección 2.3.
Cabe hacer notar que el algoritmo presentado en la sección 2.3 no está completo, pues a pesar de
mencionar el conjunto de semillas descartadas, conjunto B, no las incluye allí. El algoritmo corregido es
el siguiente:
30
Algoritmo 3: Algoritmo VTMLE corregido
31
Algoritmo 4: Algoritmo de detección de cúmulos corregido
32
3.4. Alternativas de implementación
En esta sección se analizan las distintas herramientas disponibles para la implementación del software.
Para la mayoría de las decisiones de diseño, se escribió código para comparar y determinar cuál era el
mejor camino a tomar. Se desarrollaron programas con inputs y procesamiento similar al que se debía
implementar, utilizando MapReduce de Hadoop, Pig y Streaming. También se intentó escribir un reducer
que fuese una versión modificada del Vocludet, adaptando el código existente y agregándole secciones
para que siguiese el modelo de programación. Se implementó también una versión donde el mapper
entregaba los rangos que se debían procesar, en vez de los datos mismos.
3.4.1. MapReduce de Hadoop
MapReduce es un modelo de programación diseñado para procesar grandes volúmenes de datos en
forma paralela, dividiendo el trabajo en un set de tareas independientes. Los programas se escriben de
una manera particular, influenciada por el estilo de programación funcional, en el cual se procesan listas
de datos.
Conceptualmente, un programa escrito en MapReduce transforma listas de elementos entregados como
input, en listas de datos de salida. Esto se lleva a cabo dos veces, usando dos procesos de modificación
de datos: map y reduce.
El modelo de programación se explica en las secciones 2.2 y 3.1. Éste usa Java como lenguaje de
programación, existiendo restricciones en las clases que se deben extender y las interfaces que se deben
implementar. Este es nivel más bajo de programación para Hadoop, al existir lenguajes de programación
más simples que usan éste como base, tales como Pig y Streaming.
3.4.2. Pig
Apache Pig es una plataforma para analizar sets de datos de gran tamaño. Consiste de un lenguaje de
alto nivel para escribir programas de análisis de datos, junto con una infraestructura para evaluar estos
programas. Estos programas poseen una estructura que permite paralelización, lo que les da la
capacidad para manejar sets de datos muy grandes.
La capa de infraestructura de Pig consiste de un compilador que produce secuencias de programas
MapReduce, para los cuales ya existe una implementación paralela general. La capa de lenguaje consiste
33
en un lenguaje llamado Pig Latin, el cual posee propiedades positivas interesantes: la programación es
relativamente fácil, al tener una sintaxis parecida a consultas en SQL; simplifica los análisis de datos
simples, al escribirse éstos en pocas líneas y obtener una ejecución en paralelo de forma automática; las
tareas complejas, compuestas por múltiples transformaciones de datos interrelacionados, son
codificadas explícitamente como secuencias de flujo de datos, lo que facilita su lectura; la forma en que
se codifican las tareas permite que el sistema pueda optimizar su ejecución automáticamente,
permitiendo al usuario enfocarse en semántica en vez de eficiencia; y los usuarios pueden crear sus
propias funciones para hacer procesamiento de datos para sus propios propósitos.
3.4.3. Streaming
Hadoop Streaming es una herramienta que viene con la distribución de Hadoop. Ésta permite crear y
ejecutar tareas MapReduce usando cualquier ejecutable o script como mapper o reducer. Esto entrega
una gran flexibilidad, al poder utilizar diferentes lenguajes de programación con herramientas y
bibliotecas específicas para crear los scripts o ejecutables. Streaming inserta estos programas en la línea
de procesos MapReduce, envolviéndolos con mappers y reducers que los alimentan por el stdin y
recuperan sus resultados por el stdout.
Cuando se define el ejecutable que va a ser utilizado en los mappers, cada una de las tareas map lanza el
ejecutable como un proceso separado al momento que se inicializa. Mientras se ejecuta, convierte sus
inputs en líneas y alimenta con estas lineas al stdin del proceso. Mientras tanto, el mapper recolecta los
outputs provenientes del stdout del proceso y los convierte en un par key/values, lo que se transforma
en el output del mapper. Para esto, se usa la convención de que cada línea escrita por el proceso en el
stdout debe corresponder a un key seguido por sus values. Por defecto, el prefijo de una línea hasta el
primer tab se considera el key, y el resto de la línea va a corresponder a los values. Para los reducers
ocurre un proceso similar al de los mappers, pero recibiendo como un par key/values como entrada.
Esta es una alternativa que puede ser muy útil para trabajos en que se quiera reutilizar código, ya que
permite el desarrollo en cualquier lenguaje. Además hace fácil la invocación a código externo al proveer
la capa “envolvente” descrita más arriba.
34
3.5. Alternativa escogida
La alternativa que mostraba la mayor flexibilidad y potencialmente mejores resultados en rendimiento
era MapReduce, pero ésta era también la herramienta de más bajo nivel. Esto significaba escribir más
código el cual probablemente sería más difícil de entender, además de realizar trabajos que otras
utilidades ejecutaban de manera automática.
Pig parecía ser una buena opción, dado el manejo de archivos y la facilidad para particionarlos. Se
intentó hacer un programa en Pig Latin que hiciera el particionamiento de los datos, sin embargo la
ejecución de programas externos no era algo propio de la plataforma, por lo que se debía migrar todo el
Vocludet desde su código en Java a funciones personalizadas de Pig. Si bien esto era posible, se evaluó
negativamente por temas de retro compatibilidad y por la necesidad de conocer Pig, un lenguaje poco
conocido, para desarrollos futuros.
Se decidió utilizar Streaming con Python. Streaming permite futuros desarrollos con cualquier lenguaje
de scripting o que genere ejecutables, ya que se abstrae a un simple procesamiento de datos que se leen
por el stdin y se escriben al stdout. Además su diseño no sólo permite, sino que funciona con llamadas a
código externo. Python provee un fácil manejo de strings, además de permitir ejecutar llamadas a código
externo de una manera relativamente sencilla, en particular a bash, con el que están escritos los scripts
del Vocludet. También permite trabajar con listas y tablas de hash de manera muy simple, facilitando el
trabajo en la partición y re ensamblaje de datos. Para este trabajo el input ya estaba organizado en líneas
y columnas separadas por tabs, por lo que no era necesario un pre procesamiento de la información
antes de ser usada para Streaming. Todo lo anterior se traduce en un código más simple, corto y legible,
facilitando su modificación y la comprensión de terceros para desarrollos futuros
35
4. Diseño e Implementación
En este capítulo se presentan las decisiones que se consideraron para el diseño del software, se propone
un diseño final, y se muestra y explica el código implementado.
4.1. Diseño de la solución
Dado que se decidió trabajar con Hadoop, en particular con streaming, el diseño de la solución
contempla el uso de mappers y reducers para procesar los datos en forma paralela.
Al funcionar con tres capas, de lectura, procesamiento y escritura de datos, el software Vocludet permite
hacer múltiples tipos de procesamiento de datos. Dado que el fin de este proyecto no es la ejecución
misma de los procesos, sino la implementación de una solución que permita efectuar estos procesos en
forma paralela, para el diseño y testeo se consideró sólo la ejecución del VTMLE. A pesar de no llevarse a
cabo el resto de los procesos, los resultados se pueden extrapolar, de esta forma evitando los altos
costos en tiempo y recursos asociados. De todos modos se pueden llevar a cabo otros procesos
simplemente agregándolos a los scripts de ejecución.
Para ejecutar el VTMLE se requiere sólo la información de posición de las galaxias, obtenidas desde el
sondeo 2dFGRS. Sin embargo, Vocludet utiliza información adicional, correspondiente a los datos
fotométricos de las galaxias. Esta información no sirve de input para el VTMLE, pero se incluyó pues es
utilizada por el software secuencial para ejecutar otros procesos. Si bien este trabajo no contempla
realizar aquellos procesos, por lo que la información fotométrica no es de interés, de todos modos se
entregarán ambos sets de datos como entrada. Esto permite trabajar con el programa Vocludet sin
modificarlo, lo que facilita la re ejecución de trabajos anteriores y el desarrollo de futuros trabajos donde
sí se quieran hacer comparaciones, con la ventaja del paralelismo otorgado por el presente trabajo. La
versión sobre la que se trabajó recibe entonces dos archivos de entrada, correspondientes a los datos de
posición y a datos fotométricos de las galaxias.
Los archivos constan de 8 y 12 columnas de datos separados por tabs. Ambos archivos describen puntos
en tres dimensiones, con dos coordenadas angulares y una de profundidad (ra, dec y z), además de otros
datos descriptivos.
Se contemplaron varios diseños para llevar a cabo el procesamiento de los datos, descritos a
continuación. Lo que se mantuvo constante para todos los diseños es que el mapper debía establecer
36
una división de los datos, y el reducer debía hacer el procesamiento equivalente al VTMLE en forma
paralela.
4.1.1. División de los datos
La primera decisión de diseño fue la división de los datos en un set de celdas, sin que se perdiesen datos
en el proceso. Al hacer una división disjunta, se tenía el problema de que un cúmulo fuese separado en
dos o más de estas divisiones. Para ello habían dos soluciones posibles: encargarse del problema
después, viendo cuáles eran los cúmulos que habían quedado como caso de borde y analizándolos en
una segunda pasada, o crear las particiones de tal manera que no hubiesen estos problemas. Se optó por
la segunda opción, dado que la primera implicaría la creación de tres nuevas etapas: un análisis de los
cúmulos para encontrar los que se encontraran posiblemente divididos, una ejecución del detector de
cúmulos sobre las áreas en conflicto, y una posterior reconstrucción de los datos. Además, si no se hacía
bien, se podrían generar nuevos conflictos en la segunda pasada, lo que requeriría de nuevos
mecanismos de re ensamblaje.
Las particiones se podían crear de tal manera que no dividiesen los cúmulos, pero esto implicaba un gran
costo en el análisis previo de los datos. La solución por la que se optó fue crear divisiones no disjuntas,
donde los bordes de cada elemento de la partición se solaparan en una cierta distancia. Esto implica un
sobrecosto al procesar datos de ciertas áreas del cielo más de una vez, pero se evita el procesamiento
asociado al análisis de cúmulos que se encuentren divididos en varias celdas. Algunos cúmulos se pueden
detectar en más de una celda, por lo que en una etapa posterior se deben identificar y eliminar los que
se encuentren repetidos.
Una tarea esencial es encontrar los parámetros para lograr una buena partición. Estos incluyen: la
cantidad mínima de galaxias que debe tener una celda, de tal manera que la cantidad de cúmulos que se
puedan detectar por completo sea mayor a los cúmulos que se encuentren en los bordes; el tamaño
mínimo del solapamiento entre las celdas (en adelante overlap), para que todo cúmulo de galaxias se
pueda encontrar íntegramente en al menos una división; la geometría de las celdas, para evitar que un
cúmulo sea más grande que una celda en alguna de sus dimensiones, maximizar la cantidad de cúmulos
íntegros y a su vez minimizar los cúmulos en los bordes; la forma en que se dividen los datos, dado que
se usan coordenadas esféricas para la descripción de posición, lo que conlleva un aumento en el
volumen de las particiones (a coordenadas angulares constantes) al alejarse la coordenada radial del
centro en intervalos constantes; etc.
37
Los parámetros escogidos deben encontrar un equilibrio entre maximizar la paralelización, y minimizar la
cantidad de procesamiento duplicado debido a cúmulos que se encuentran en zonas de overlap. Lo
primero tiende a aumentar el número de celdas y por ende reducir su tamaño, mientras que lo segundo
tiende a lo contrario.
Se habló con astrónomos que trabajaron directa o indirectamente con el trabajo en que se basa el
presente, de los que se obtuvo la siguiente información:
Para determinar si se está en presencia de un cúmulo de galaxias, se debe primero obtener una
densidad local promedio de galaxias. Para esto se toma un punto y un radio, calculándose la
densidad dentro de la esfera que se construye a partir de éstos. El radio mínimo que se podría
considerar es de 15 Mega parsecs (Mpc).
Por construcción, un cúmulo de galaxias debe tener menos de 1,5 Mpc de radio. De lo contrario
se tendrían cúmulos de tamaños muy distintos, al poder seguirse agregando muchas galaxias a
un cúmulo según otros criterios de agregación.
Con el primer dato se puede establecer que en toda celda debe poder caber una esfera inscrita de 15
Mpc de radio, para que en el caso general se logre una densidad local promedio adecuada. Con el
segundo dato se puede establecer que independiente del tamaño y la geometría de las celdas, el tamaño
del overlap debe ser mayor a 3 Mpc. De esta manera se garantiza que todo cúmulo va a poder ser
encontrado íntegramente en al menos una celda.
Respecto a la geometría, es intuitivo pensar que celdas con formas y volúmenes similares van a tender a
equiparar el tiempo de procesamiento de los datos. Por lo demás, la geometría de las celdas va a
depender necesariamente de la geometría del espacio que se esté analizando. Al graficar los puntos en
tres dimensiones se ve que la forma del espacio analizado es la de un cono aplanado, con
aproximadamente 1,3 radianes de ascensión recta y 0,15 radianes de declinación. No se trata
exactamente de un cono, sino más bien una pirámide con base redondeada similar a un pedazo de pizza.
En adelante se referirá a ésta como slice.
Para particiones con pocas celdas, y por ende para una cantidad limitada de paralelismo, se pueden
hacer divisiones en la ascensión recta del slice, y si se desestima el volumen resultante en virtud del
paralelismo, se pueden hacer divisiones según el radio. El resultado de la primera estrategia es una
división del slice original en varios slices alargados sobrepuestos, presentados en la Figura 6. El de la
38
segunda estrategia es similar, agregándose cortes redondeados definidos por radios fijos y la
superposición de éstos, presentados en la Figura 7.
Figura 6: Partición angular Figura 7: Partición angular y radial
Si bien la primera estrategia genera celdas similares en forma y volumen, su forma alargada genera
amplias zonas de overlap cuando se divide en muchas celdas. Se buscó otra forma de particionar los
datos que permitiese obtener celdas con volúmenes similares, pero además con una geometría más
equilátera. Las celdas con forma de cubo minimizan las áreas de overlap, disminuyendo los datos que se
procesan múltiples veces. Si bien la forma del espacio y el sistema de coordenadas utilizado hacen sub
óptima la división del slice en cubos, se encontró una partición que mientras más divisiones tiene, más se
parece a un cubo. Ésta consiste en definirse una arista mínima del cubo ideal, y en base a ésta hacer
divisiones lo más equiláteras posibles, con lados mayores a la arista. Primero se divide el slice en
cascarones múltiplos del mismo radio, donde el radio es el menor valor mayor o igual a la arista, que
divida el radio total en partes iguales. Luego se particiona cada uno de los cascarones en partes iguales,
tanto en el ángulo de ascensión recta como en el de declinación. Para esto se toma el arco menor del
cascarón y se divide en partes iguales, al igual que con el radio, en el menor valor que sea mayor a la
arista mínima. Las celdas resultantes dividen al slice en trozos de volumen similar, y tienen una
geometría que disminuye la proporción total de overlap. Además, mientras más pequeña es la arista
mínima, más divisiones se hacen y las celdas se parecen más a un cubo. Esta estrategia está graficada en
la Figura 8.
39
Figura 8: Partición en celdas similares a cubos
4.1.2. Procesamiento de los datos
La siguiente decisión de diseño fue determinar la forma en que el reducer llevaría a cabo el proceso
VTMLE. La primera opción, que se escogió en un principio al ser la más natural, consistía en crear un
reducer que fuese una versión del Vocludet modificado, manteniendo las funcionalidades y adaptando
los procesos para hacerlos compatibles con el framework Hadoop. Se abría una gama de opciones para la
implementación de este código, incluyendo el lenguaje y la capa de abstracción de Hadoop a la que iba a
pertenecer. Independiente de esta decisión, crear un reducer con estas características implicaba no sólo
cambiar el código del Vocludet, sino dejarlo inutilizable fuera del contexto del framework. También
significaba que para la utilización de algunas funcionalidades existentes y de desarrollos futuros se debía
contar con conocimientos específicos de Hadoop, yendo en contra de la idea original del Vocludet que
proponía independencia del lenguaje en la creación de filtros para el procesamiento de datos. Uno de los
aspectos básicos que se debía cambiar en la implementación de esta solución, era la forma en que se
entregaran los datos. Se debía cambiar el sistema de lectura de archivos por uno donde se leyesen líneas
desde el stdin.
Esta problemática dio origen a una segunda opción para el diseño del reducer, que contemplaba no
modificar el código del Vocludet, sino crear un reducer que se encargara de proporcionarle los datos en
el formato original. Se eligió la segunda opción ya que facilitaba la reutilización de utilidades ya creadas,
40
y permitía el desarrollo de otras nuevas con las mismas herramientas y metodologías utilizadas en el
proyecto original. Dada esta opción, se debía decidir de qué forma se entregarían los datos. El mapper
debía determinar la partición, luego de lo cual los datos se podían entregar uno por uno al reducer
correspondiente, o simplemente entregar el rango de datos a cada reducer.
La solución que consistía en la entrega de los rangos de datos era posible ya que uno de los parámetros
con los que trabaja el Vocludet es el rango sobre el que se quiere trabajar, dentro de un conjunto de
datos. La implementación de esta modalidad contemplaba la creación de múltiples archivos de scripting,
con los parámetros configurados para que cada uno se hiciera cargo de una celda del cielo derivado de la
partición, para luego ejecutar estos scripts en forma paralela. Si bien este modo era directo y su
implementación relativamente simple, no se usaba el framework Hadoop de manera estándar, en
particular el modelo de entrada/salida del “reduce” y el sistema de archivos. Tampoco se tenía la certeza
de que la selección dentro del conjunto de datos se hiciese en forma eficiente.
Se optó entonces por otro mecanismo que realiza tareas redundantes de lectura y escritura de archivos,
pero utiliza Hadoop de una manera convencional. Ésto permite obtener las optimizaciones implícitas de
Hadoop, optar a posibles mejoras en versiones futuras del framework, y facilitar desarrollos futuros de la
aplicación. Para esto se utilizó el modo estándar de Streaming, en que luego de determinar los criterios
de partición, el mapper le entrega a cada reducer el set de datos que le corresponde. El reducer luego se
encarga de crear los archivos de entrada, crear los scripts correspondientes, y ejecutar el Vocludet.
4.1.3. Re ensamblaje de los resultados
El proceso VTMLE crea dos archivos resultantes: uno con la extensión “.ID”, el cual en cada línea tiene el
identificador único de un cúmulo de galaxias; el otro con la extensión “.GID”, el cual por cada línea tiene
datos separados por tabs, donde cada dato corresponde al identificador de una galaxia, y cada línea
corresponde a un cúmulo de galaxias. El archivo más relevante, y por ende el que interesa re ensamblar,
es el que contiene las galaxias integrantes de cada cúmulo; el otro simplemente sirve como referencia
para otros procesos que se pueden llevar a cabo. Al dividir los datos y ejecutar el VTMLE sobre cada
partición, se crean tantos archivos de salida como conjuntos.
Uno de los problemas es que los identificadores de las galaxias son únicos para un set de datos, pero van
a estar repetidos y usados para identificar a más de una galaxia si se trabaja con más de un set. Esto se
debe a que la identificación es una simple enumeración de las galaxias contenidas en el set de datos.
Para trabajar con varios archivos se necesita tener el mismo identificador para las galaxias
41
independientemente del archivo en que se encuentren, pudiendo usarse la posición absoluta o un
derivado de ésta. Una primera solución consistía en modificar el Vocludet para que generara
identificadores absolutos independientemente del archivo. Ésta se descartó por razones ya descritas en
otras secciones de este documento, donde se explica por qué se evitar modificar el Vocludet. Otra
opción era generar un nuevo archivo, reemplazando los identificadores de galaxias por su posición
absoluta en el espacio. Uno de los archivos intermedios que genera el Vocludet contiene, entre otras
cosas, el identificador de cada galaxia y su posición absoluta, por lo que se utilizó para la generación del
nuevo archivo.
Ya trabajando con un sistema de identificación única, el siguiente paso fue eliminar los cúmulos
repetidos. Primero está la pregunta de cómo se define un cúmulo repetido, si es cuando los centroides
se encuentran a una corta distancia, cuando se utiliza la misma semilla para su generación, o si
simplemente tienen algún miembro en común. Dada la forma en que funciona el algoritmo de Daniel
Pizarro, descartando la construcción de cúmulos que contengan una o más galaxias pertenecientes a un
cúmulo ya aceptado, se optó por definir como repetidos aquellos que tenían cualquier miembro en
común. La siguiente decisión fue determinar cuál de los cúmulos se guarda y cuáles se desechan,
pudiendo haber hasta 8 cúmulos con un mismo miembro, siendo 8 el número máximo de celdas que
pueden estar sobrepuestas en un mismo sector al dividir el espacio en 3 dimensiones. Suponiendo una
partición de datos con un overlap suficientemente grande, como las descritas en este documento, se
puede asumir que cualquier cúmulo de galaxias va a estar completo en alguno de los archivos. Por ende,
se puede eliminar el que tenga menor cantidad de galaxias, sin tener que tomar en consideración otros
elementos astronómicos como la semilla de menor volumen, la densidad promedio del cúmulo, u otros.
4.1.4. Diseño final
Los datos de entrada, tanto de posición como fotométricos, son ingresados al sistema de
almacenamiento de Hadoop. Luego el sistema de streaming lee ambos archivos, entregando a cada
mapper un set de datos, una línea a la vez. Cada mapper recibe las líneas por el stdin y determina a qué
celda del cielo corresponde cada una. Luego agrega la información de la celda a la línea con datos y la
escribe como salida en el stdout. De esta forma, los sets de datos que corresponden a la misma celda se
agrupan y se redirigen a un mismo reducer.
El reducer recibe líneas por el stdin, las que provienen de ambos archivos de entrada originales. Cada
reducer recibe las líneas correspondientes a una o más celdas del cielo. Por ende, el reducer debe
42
clasificar las líneas según archivo del que provengan, y según la celda a la que correspondan. Luego crea
archivos según éstas clasificaciones, los que escribe en el sistema de archivos local. Para cada celda de
las que recibe datos, crea un script que ejecuta el Vocludet, entregándole como parámetros los dos
archivos de entrada correspondientes. Luego ejecuta cada uno de los scripts creados, los que escriben
sus resultados en el sistema de archivos local. Con los archivos resultantes se construye uno nuevo, en
que cada línea corresponde a un cúmulo de galaxias, con cada elemento de la línea siendo la posición
absoluta de una galaxia perteneciente a tal cúmulo. Por último lee los archivos de resultado, y escribe
cada línea resultante en el stdout, para así completar el proceso de Streaming. Cada uno de los reducers
se ejecuta en paralelo, por lo que la rapidez de ejecución depende del número de celdas, reducers, y
procesadores disponibles. Además del aumento en eficiencia evidente al ejecutarse los procesos en
paralelo, Hadoop tiene optimizaciones internas que agilizan estos procesos.
Para el re ensamblado de los resultados, se utilizan los archivos que describen los cúmulos de galaxias
con la posición absoluta de las galaxias que los componen. Se ejecuta un script de python que toma dos
de estos archivos, busca las galaxias que se repitan en dos cúmulos, y de estos dos cúmulos elimina el
que tenga menor cantidad de galaxias. Se puede asumir que una galaxia va a encontrarse en un solo
cúmulo por archivo, dado el proceso de construcción de los cúmulos, por lo que la intersección de
elementos sólo va a darse entre dos cúmulos de galaxias. Este proceso de unión de archivos se lleva a
cabo recursivamente, hasta finalmente unirse en un archivo que contiene todos los cúmulos de galaxias.
43
4.2. Código Implementado
Ya que Hadoop y sus herramientas asociadas no son de uso masivo, la documentación que se puede
encontrar es escasa. Además, al estar en desarrollo, la información que se encuentra puede no ser útil
para la versión del software que se esté ocupando. Dado lo anterior y que quien escribe no contaba con
conocimiento en el tema, durante todo el desarrollo del presente trabajo y en especial al principio, se
llevaron a cabo numerosos tutoriales y experimentos para entender el funcionamiento y
comportamiento de cada una de las herramientas que se investigó.
La última herramienta que se investigó, y con la que se decidió finalmente construir el presente trabajo,
fue Streaming con Python. Se hicieron numerosos experimentos para entender el comportamiento que
presentaba: determinar el método de separación de parámetros; entender el comportamiento de
Python en el manejo de strings, input y output, y ejecución de bash; entender la secuencia de procesos
que ocurren para que funcione Hadoop; determinar la ubicación del directorio de trabajo, donde se
guardan los archivos resultantes de procesos intermedios, y si esto se hacía en el sistema de archivos del
sistema operativo o en el HDFS; averiguar si se trabajaba sobre los archivos originales de map y reduce
en una misma máquina, o si se crea una copia por cada mapper y reducer; determinar cómo se lleva a
cabo la asignación de los grupos de keys en los reducers; etc.
El código final para el programa corresponde a un mapper encargado de crear la partición de los datos,
un reducer encargado del procesamiento de éstos, y un script de unión de los archivos resultantes.
Además se utilizan el software secuencial y los scripts que se crean en el reducer para ejecutarlo.
A continuación se presenta el código del mapper correspondiente a la partición más interesante, la que
resulta en celdas similares a cubos, como graficado en la Figura 8:
44
#!/usr/bin/env python
from __future__ import division
import sys
#------------------------------------ set parameters ----------------------------------
#minimum values of the data, in theta, phy, redshift
min_val = {'x':2.57441, 'y':-0.10975, 'z':0.002}
#maximum values of the data
max_val = {'x':3.88319, 'y':0.04363, 'z':0.2995}
#minimum edge of the "cube", in Mpc
min_cube_edge = 30
#minimum overlap of the cube, in Mpc
min_cube_overlap = 3
#dimensions of the space (cone), in Mpc
cone_dimensions = {'x':1000, 'y':100, 'z':1000}
#------------------------------------ calculate variables ----------------------------------
#separate z in n parts, where (maxz-minz)/n > min_cube_edge
split_number_z = int(cone_dimensions['z'] / min_cube_edge)
#dimension of the z edge of a cube, in Mpc
cube_edge_z = cone_dimensions['z']/split_number_z
#overlap value for the z axis
overlap_val_z = ( (min_cube_overlap * (max_val['z'] - min_val['z'])) / cone_dimensions['z'])
#for each z fringe, (separate in semi cubes) find n so that (thetamax-thetamin)*z = arc, arc/n >
min_cube_edge
split_number_x = [0]
overlap_val_x = [0]
for i in range(1, split_number_z) :
#arc = radius * angle(radians) , in Mpc
arc = (i * cube_edge_z) * (max_val['x'] - min_val['x'])
#number of divisions for that fringe
divisions = int(arc / min_cube_edge)
#overlap in degrees for that fringe, arc -> delta_angles : cube_overlap -> angle_overlap
angle_overlap = min_cube_overlap * (max_val['x'] - min_val['x']) / arc
#add values to corresponding arrays
overlap_val_x.append(angle_overlap)
split_number_x.append(divisions)
#for each z fringe, (separate in semi cubes) find n so that (phy_max-phy_min)*z = arc, arc/n >
min_cube_edge
split_number_y = [0]
overlap_val_y = [0]
for i in range(1, split_number_z) :
#arc = radius * angle(radians) , in Mpc
arc = (i * cube_edge_z) * (max_val['y'] - min_val['y'])
#number of divisions for that fringe
45
divisions = int(arc / min_cube_edge)
#overlap in degrees for that fringe, arc -> delta_angles : cube_overlap -> angle_overlap
angle_overlap = min_cube_overlap * (max_val['y'] - min_val['y']) / arc
#add values to corresponding arrays
overlap_val_y.append(angle_overlap)
split_number_y.append(divisions)
split_number = {'x':split_number_x, 'y':split_number_y, 'z':split_number_z}
overlap_val = {'x':overlap_val_x, 'y':overlap_val_y, 'z':[overlap_val_z] * (split_number['z'] -1)}
#columns with the x, y and z values fron the 2dFNGgals file
col_2d = [0, 1, 2]
#columns with the x, y and z values fron the best.observations file
col_ob = [4, 5, 6]
z_ponderator = [split_number['z'] / (max_val['z'] - min_val['z'])] * (split_number['z'] -1)
x_ponderator = map(lambda x : x / (max_val['x'] - min_val['x']), split_number['x'])
y_ponderator = map(lambda x : x / (max_val['y'] - min_val['y']), split_number['y'])
ponderator = {'x':x_ponderator, 'y':y_ponderator, 'z':z_ponderator}
#function for getting the quadrants to which a value corresponds
def get_quadrant(val, coord, fringe):
list = []
#val moved to simulate overlap
plus_val = val + overlap_val[coord][fringe]
minus_val = val - overlap_val[coord][fringe]
#quadrant to where val belongs
quad = int((val - min_val[coord]) * ponderator[coord][fringe])
#quadrant to where val belongs, considering top overlap
quad_plus = int((plus_val - min_val[coord]) * ponderator[coord][fringe])
#quadrant to where val belongs, considering bottom overlap
quad_minus = int((minus_val - min_val[coord]) * ponderator[coord][fringe])
#correct the max vals problem (max vals get their own cell)
if val == max_val[coord]:
quad -=1
list.append(quad)
if plus_val < max_val[coord] :
list.append(quad_plus)
if minus_val > min_val[coord] :
list.append(quad_minus)
return set(list)
#------------------------------------ process data ----------------------------------
46
# input comes from STDIN (standard input)
for line in sys.stdin:
# remove leading and trailing whitespace
line = line.strip()
# parse the data
coords = line.split()
#if the input is from the 2dFNGgals file
if len(coords) == 8:
coord_x = float(coords[col_2d[0]])
coord_y = float(coords[col_2d[1]])
coord_z = float(coords[col_2d[2]])
coord = {'x':coord_x, 'y':coord_y, 'z':coord_z}
#if the input is from the best.observations file
elif len(coords) == 12:
coord_x = float(coords[col_ob[0]])
coord_y = float(coords[col_ob[1]])
coord_z = float(coords[col_ob[2]])
coord = {'x':coord_x, 'y':coord_y, 'z':coord_z}
else:
continue
#if the values are out of the min and max range
if coord['x'] > max_val['x'] or coord['x'] < min_val['x'] or coord['y'] > max_val['y'] or coord['y'] <
min_val['y'] or coord['z'] > max_val['z'] or coord['z'] < min_val['z']:
continue
#determine corresponding quadrants
quadrants = []
quadrants_z = get_quadrant(coord['z'], 'z', 0)
for fringe in quadrants_z :
quadrants_x = get_quadrant(coord['x'], 'x', fringe)
quadrants_y = get_quadrant(coord['y'], 'y', fringe)
#pair both lists, and add the corresponding fringe
quadrants.extend([[x, y, fringe] for x in quadrants_x for y in quadrants_y])
#print the lines with their corresponding quadrants to the stdout
for quadrant in quadrants :
quadrant_string = `quadrant[0]` + ',' + `quadrant[1]` + ',' + `quadrant[2]`
print '%s\t%s' % (quadrant_string, line)
47
El mapper recibe líneas, una por una, en el stdin, correspondientes a cada línea de los dos archivos de
entrada. Estas líneas pueden tener 8 o 12 datos separados por tabs, dependiendo de cuál de los dos
archivos provienen. De cada línea, se seleccionan los datos correspondientes a las coordenadas de la
galaxia que la línea representa. Las posiciones en las que se encuentran los datos de las coordenadas son
diferentes según el archivo del que provengan, por lo que se usa la cantidad de parámetros que la línea
tenga para determinarlas. Usando los datos de posición, y otros parámetros que determinan el tamaño y
forma de las celdas, se calcula la o las celdas a los que pertenece la galaxia. Finalmente, el mapper
imprime al stdout las celdas a las que pertenece la galaxia, seguido por la línea misma. El primer dato
impreso pasa a ser el key del output, y la línea, es decir el conjunto de valores separados por tabs, pasa a
ser el conjunto de values del output. El key, correspondiente al identificador de la celda, es una
coordenada en tres dimensiones: tres números separados por comas.
Se presenta a continuación el código del reducer:
48
#!/usr/bin/env python
#os.system only works in linux
from operator import itemgetter
import os
import sys
import shlex, subprocess
#------------------------ create the input files ---------------------
#working directory
wd = "/home/maugsbur/Dropbox/hadoop/cludet_004/"
#a dict of dicts, keeping record of the existent keys
#each key is a dict with two arrays corresponding to each file
keys = {}
# input comes from STDIN
for line in sys.stdin:
# remove leading and trailing whitespace
line = line.strip()
# parse the input we got from mapper.py
coords = line.split()
#eliminate first element (key), and save it
key = coords.pop(0)
#remake line without first element
line_wo_key = "\t".join(coords)
line_wo_key += "\n"
#if it's the first time the key appears, open corresponding 2dFNGgals and best.observations
if keys.get(key) == None :
keys[key] = {}
keys[key]['file_2d'] = open(wd + 'work/2dFNGgals_' + key, 'w')
keys[key]['file_ob'] = open(wd + 'work/best.observations_' + key, 'w')
#if the line comes from a the 2dFNGgals file
if len(coords) == 8:
#write line into local 2dFNG file
keys[key]['file_2d'].write(line_wo_key)
else:
#write line into local best.observation file
keys[key]['file_ob'].write(line_wo_key)
#close files
for key in keys :
keys[key]['file_2d'].close()
49
keys[key]['file_ob'].close()
#------------------------ process data with Vocludet ---------------------
#write script
for key in keys :
script = open(wd + 'script_' + key, 'w')
script.write('PREV_DIR=$PWD\n')
script.write('cd $WORK_DIR\n')
script.write('$JREPATH/bin/java -Xms256M -Xmx256M -classpath
"$OTHER_JAR/vecmath.jar$PATHSEP$CLUDET_JAR" cl.uchile.dcc.astro.cludet.parser.TPIGG_parser Z001-
NGP_' + key + ' 16 9 3 -10 0.007 9.000 2dFNGgals_' + key + ' best.observations_' + key + '\n')
#script.write('$JREPATH/bin/java -Xms256M -Xmx256M -classpath
"$OTHER_JAR/vecmath.jar$PATHSEP$CLUDET_JAR" cl.uchile.dcc.astro.cludet.parser.TPIGG_parser Z001-
NGP 16 9 3 -10 0.007 9.000 2dFSGPgals.dat best.observations.idz.photometric\n')
script.write('$JREPATH/bin/java -Xms256M -Xmx256M -classpath
"$OTHER_JAR/vecmath.jar$PATHSEP$CLUDET_JAR"
cl.uchile.dcc.astro.cludet.detector.VTMLE_ClusterDetector Z001-NGP_' + key + '\n')
script.close()
#execute script
#create log file, with stdout info
logout = open(wd + 'work/logout' + key, 'w')
#create log file, with stderr info
logerr = open(wd + 'work/logerr' + key, 'w')
#write command line command
bash = wd + 'run ' + wd + 'script_' + key
args = shlex.split(bash)
#execute command on bash
process = subprocess.Popen(args, shell=False, stdout=logout, stderr=logerr)
a = process.communicate()
#-------------------------- create file for reassembling ---------------------------
#elements hash, id with its corresponding line in file
cells = {}
#file with cell ids and positions
volumeslog = open(wd + 'work/Z001-NGP_' + key + '.VOLUMES_log.log', 'r')
#eliminate first line, with table column names
volumeslog.readline()
for line in volumeslog :
# remove leading and trailing whitespace
line = line.strip()
50
# parse the line
split = line.split()
#obtain the cell id
id = split[0]
#obtain the position
pos = split[1]
#insert in the hash table
cells[id] = pos
#file with clusters, one per line, multiple cell ids
clustersgid = open(wd + 'work/Z001-NGP_' + key + '.VTMLE.clustersGID', 'r')
#outfile with the gid file rewritten with positions
newgid = open(wd + 'work/Z001-NGP_' + key + '.VTMLE.clustersGID.pos', 'w')
for line in clustersgid :
# remove leading and trailing whitespace
line = line.strip()
# parse the input
split = line.split()
#rewrite the clustersgid with positions
for id in split :
#obtain the position of the cell
pos = cells[id]
#write the new file
newgid.write(pos+'\t')
newgid.write('\n')
#---------------------------- write output -------------------------------
#read files
file_2d = open(wd + 'work/Z001-NGP_' + key + '.VTMLE.clustersGID.pos', 'r')
#write them on the stdout
for line in file_2d:
print key + ' ' + line
51
El reducer recibe líneas, una por una, en el stdin, las cuales contienen primero el identificador de la celda
a la que corresponde la galaxia representada por la línea, y luego los datos de posición y fotométricos de
ésta. La totalidad de las líneas que recibe cada reducer corresponden a una o más celdas de la partición
del espacio hecha por el mapper. El número de celdas que reciba cada reducer va a depender de la
proporción entre éstos, siendo idealmente uno a uno, pero pudiendo haber más de una celda por cada
reducer. Primero se hace una separación de la línea, entre el key y los values, que corresponden al
primer dato y el resto de los datos respectivamente. En caso de que el valor del key, identificador de la
celda al que pertenece la galaxia, no haya aparecido en líneas anteriores, se crean los dos archivos
asociados a éste. Luego se escribe una nueva línea con los values en el archivo que corresponda, el cual
se define según la cantidad de values que contenga: 8 y 12 en cada caso. Luego de leídas y procesadas
todas las líneas del stdin, se procede a guardar los archivos escritos. Posteriormente se escribe un script
con las instrucciones para llamar a los procesos del Vocludet necesarios para que se ejecute el VTMLE:
primero un parser que transforme los datos entregados en el formato común de la aplicación, luego el
programa que ejecuta el VTMLE, y finalmente el programa que transforma los datos intermedios al
formato de output final. Se escribe un script por celda que recibe el reducer, difiriendo un script del otro
en el nombre de los archivos de entrada que se entregan como parámetros a los programas del
Vocludet. Cada uno de los scripts creados se ejecuta desde el reducer a través de otro script denominado
run, el cual recibe el script como parámetro y lo ejecuta, además de llevar a cabo tareas como configurar
el ambiente. La ejecución de los scripts crea archivos con resultados, los cuales son leídos e impresos
línea por línea en el stdout, para así completar el proceso de Streaming. Finalmente se ejecuta, una vez
terminado el proceso de Streaming, el script de re ensamblaje de resultados, sobre los archivos con los
cúmulos y posiciones absolutas de las galaxias. Toda la escritura y ejecución se llevan a cabo en una
carpeta definida como variable global al principio del programa, donde se encuentra la estructura
completa del Vocludet.
Por razones que se mencionaron en otras partes de este documento, se construyó el software sin
modificar el Vocludet. Lo único que se tuvo que cambiar fue una variable con un path absoluto, la que se
modificó para trabajar con la estructura de archivos local.
En la sección de anexos se encuentra otro ejemplo de la función mapper y el re ensamblador.
52
5. Evaluación
En este capítulo se presentan los experimentos realizados para probar el software y determinar su
eficacia y eficiencia. Se incluye una descripción de los datos de prueba, las pruebas mismas, y el
desempeño del software.
5.1. Datos de prueba
En el presente trabajo se utilizaron datos astronómicos que describían la distribución de las galaxias,
además de algunas otras propiedades de éstas. En particular se usaron los del estudio 2dF Galaxy
Redshift Survey (2dFGRS), ya que mostraba la posición de las galaxias en tres dimensiones, siendo la
profundidad determinada por el corrimiento al rojo de las ondas de luz.
Figura 9: Mapa del cielo completo mostrando el sondeo APM, en el que se basó el 2dFGRS
Este es un estudio que se llevó a cabo usando las instalaciones 2dF, construidas por el Observatorio
Anglo-Australiano. Su meta fue obtener espectros de alta calidad y datos del corrimiento al rojo de
250.000 galaxias, pertenecientes a las regiones de ambos el norte y sur galáctico (15), como presentado
en la Figura 9.
53
Figura 10: Mapa de la distribución de galaxias obtenidas por el 2dFGRS
La Figura 10 muestra un mapa de la distribución de galaxias obtenido por este estudio.
En el presente trabajo se utilizaron datos del 100k Release, un subconjunto de los datos del estudio. Éste
contiene los datos de 467.214 objetos, de los cuales no todos eran únicos ni correspondían a galaxias. Se
trabajó con una selección de estos objetos que correspondían a galaxias, separados en dos archivos
correspondientes a observaciones del norte y sur galáctico con 78.508 y 112.513 galaxias
respectivamente. Además se trabajó con un archivo que presentaba los datos fotométricos de 245.591
objetos. Se utilizaron estos datos pues son los mismos con los que trabajó Daniel Pizarro en su tesis de
magister.
Si bien durante la mayoría del trabajo se usó el set de datos completo, para la etapa de pruebas
intensivas y comparaciones sólo se utilizaron los datos del sondeo del norte galáctico. Esto se hizo para
facilitar el análisis de datos reales y para disminuir los recursos requeridos para ejecutar las pruebas.
54
5.2. Pruebas
5.2.1. Particiones
Se definieron 3 tipos de particiones, siendo cada una de éstas útil según la cantidad de máquinas
disponibles para ejecutar reducers. Se hicieron pruebas con todas ellas: la partición angular de la Figura 6
y la partición angular y radial de la Figura 7 se probaron para divisiones de hasta 8 celdas, la partición con
celdas similares a cubos de la Figura 8, se probó para aristas de 30, 60 y 120 Mpc.
Para los primeros tipos de partición, y con la cantidad de celdas descrita, no se tuvo problemas. Para la
partición con celdas similares a cubos, se había definido como volumen mínimo un cubo de lado 30 Mpc.
Usando el tamaño mínimo se iban a poder correr la mayor cantidad de procesos en paralelo, al haber
más celdas para procesar. Con este parámetro, el programa que particionaba los datos dividía el slice en
32 cascarones, cada uno subdividido según el ancho y largo del cascarón, formándose un total de 1641
divisiones. En circunstancias ideales, de densidad constante de datos, cada celda debiese haber tenido
alrededor de 50 elementos. Sin embargo, la mayoría de los elementos se concentraba en unas pocas
celdas, y la gran mayoría de las celdas tenía menos de 20 elementos e incluso carecían de éstos. Además,
las celdas con mayor cantidad de datos no se encuentran distribuidos con un patrón claro. Dado que los
cúmulos interesantes para la astronomía tienen al menos 30 elementos, no tiene sentido usar esta
partición de datos.
La experiencia anterior indica que para particiones con muchas celdas sobre todo el espacio, no es
óptimo trabajar con celdas de volumen constante, siendo probablemente mucho mejor una partición ad-
hoc a la distribución de datos. En particular este sondeo muestra una densidad de galaxias mucho menor
para distancias de z grandes, por lo que una buena distribución ad-hoc podría ser obviando las galaxias
que tengan un z mayor a 0,15, y luego aplicando la partición usada en el experimento anterior.
5.2.2. Ensamblaje
Cuando se trabajó con el re ensamblador, se realizaron experimentos para verificar que se eliminaban
los cúmulos repetidos en varias particiones. Fue entonces que se comprobó que el VTMLE repite
elementos en los cúmulos que encuentra. Esto se debe al funcionamiento del software presentado en la
sección 3.3. Se siguió experimentando, y se descubrió que al modificarse los límites de las particiones, se
obtiene un set muy distinto de cúmulos de galaxias. Los cúmulos resultantes son altamente
55
dependientes de las condiciones iniciales del problema, encontrándose cúmulos distintos dependiendo
de qué cúmulo sea el primero en encontrarse. Dado que el software es determinístico, no hay
discordancias en ejecuciones sobre el mismo set de datos, pero si se modifican de tal manera que
cambien las semillas o el orden en que éstas se encuentran, se van a obtener resultados distintos. En
particular si se divide el set de datos, se van a encontrar cúmulos en otro orden, lo que conlleva a
resultados distintos. Otro factor que influye en el re ensamblaje, es que la mayoría de los cúmulos que
encuentra el algoritmo son pequeños, muchos con 2 o 3 elementos.
Para determinar si el funcionamiento del VTMLE se debía a modificaciones introducidas al paralelizarlo,
lo primero que se hizo fue verificar que la ejecución con Hadoop no cambiase el resultado original. Para
esto se ejecutó el Vocludet solo, los scripts de python, y Hadoop con una partición de una celda. Los tres
métodos dieron los mismos archivos resultantes, encontrando 4170 cúmulos de galaxias.
Para que el proceso de re ensamblado sea válido, se debe verificar que el número de cúmulos que
encuentra sea similar al que se obtiene con el algoritmo secuencial. Se analizaron casos con 2 y 4
divisiones, con y sin overlap, obteniendo los siguientes resultados:
Sin
División
2 Divisiones
sin Overlap
2 Divisiones
10% Overlap
4 Divisiones
sin Overlap
4 Divisiones
10% Overlap
N° de cúmulos 4170 4323 4678 4316 4860
N° de cúmulos post
re ensamblaje
4606 4530
La cantidad de cúmulos encontrados en las particiones sin overlap superan a las del archivo original. Esto
se puede explicar pues las divisiones separan cúmulos de galaxias mayores en cúmulos más pequeños,
sin embargo no se puede mostrar una tendencia, ya que varía de forma irregular con la cantidad de
celdas de una partición. Dado que se encontraron más cúmulos al dividirse los datos sin overlap, era
esperable que agregando un overlap se encontraran más aún. Se esperaba que la reducción de datos por
parte del re ensamblador fuese mayor, sin embargo esto puede deberse a las anomalías previamente
descritas.
Otra condición para la validez del proceso de re ensamblado, es que éste conserve los cúmulos
obtenidos en cada celda y no corrompa los resultados parciales obtenidos. Analizando los datos
56
obtenidos con una partición de 2 celdas y un 10 por ciento de overlap, se obtuvo que las coincidencias
entre los cúmulos de galaxias detectados en los sets de datos parciales y el re ensamblado son:
Celda 1 Celda 2
Cúmulos en el set de datos re
ensamblado
2120
97,83%
2491
99,20%
Se puede notar que a pesar de haber un overlap de un 10%, se elimina menos de un 2% de los cúmulos
por repetición. También se ve que una de las dos celdas mantiene una mayor proporción de sus cúmulos.
Esto es debido a que el criterio de eliminación es por cantidad de galaxias, y al haber muchas de dos o
tres galaxias, existen empates donde se favorece a la celda cuyos datos fueron leídos primero.
Uno de los objetivos de este trabajo es comparar los resultados obtenidos entre el método secuencial y
el método paralelo luego del reensamblaje. Al analizar los datos obtenidos con una partición de 2 celdas
y un 10 por ciento de overlap, se obtuvo que las coincidencias en los cúmulos de galaxias detectados
entre los sets de datos son:
Celda 1 Celda 2 Re ensamblado
Coincidencia de cúmulos
con la partición original
238
10,98%
272
10,83%
502
10,89%
Al analizar los datos obtenidos con una partición de 4 celdas y un 10 por ciento de overlap, se obtiene
algo parecido. Las coincidencias en los cúmulos de galaxias detectados entre los sets de datos son:
Celda 1 Celda 2 Celda 3 Celda 4 Re
ensamblado
Coincidencia de cúmulos
con la partición original
166
7,39%
43
8,6%
184
10,21%
27
8,65%
375
8,99%
Esto indica que claramente hay poca coincidencia entre los cúmulos. Observando los archivos
resultantes, se puede notar que a veces se repiten varios miembros de un cúmulo, pero difieren en
algunos, o en la cantidad total de miembros. Esto muestra que los resultados del algoritmo de búsqueda
de cúmulos son altamente dependientes de las condiciones iniciales con las que trabaja. Sin embargo, al
57
analizar la coincidencia entre los primeros miembros de los cúmulos, correspondientes a las semillas, se
encuentra una coincidencia mucho mayor, como se muestra a continuación:
Partición de 2 Celdas Partición de 4 Celdas
Coincidencia de semillas
con la partición original
1224
29%
1150
28%
5.2.3. Verificación de datos y procedimientos
Dados los resultados de las pruebas, se realizaron tres experimentos para determinar si los resultados se
debían o no a un comportamiento errático del software desarrollado. El primero fue verificar que no se
modificaran ni se perdieran datos en el proceso de partición. El segundo fue corroborar que el Vocludet
ejecutado desde Hadoop obtuviese los mismos resultados que el Vocludet ejecutado de forma
secuencial. El tercer experimento consistió en analizar los resultados obtenidos al ejecutar el software
secuencial y el paralelo sobre una partición simple, en términos de cúmulos y galaxias coincidentes.
El primer experimento fue verificar que los archivos generados por las particiones tuviesen los mismos
datos que los archivos originales, y que no se hubiesen perdido datos en el proceso del mapper. Para
esto se hizo una partición simple, en 2 celdas y sin overlap. Se verificó que las líneas de los archivos
generados por la partición coinciden exactamente con las líneas del primer archivo de entrada, no
habiendo repeticiones ni omisiones. Para las líneas del segundo archivo de entrada sí hubo omisiones,
borrándose 6.099 líneas, correspondiendo a un 6,8% de la totalidad de las líneas. Esto no es accidental,
ya que se decidió eliminar todas las líneas del segundo archivo que representasen puntos fuera del
espacio de muestreo del primer archivo. Estas líneas no serían utilizadas en los procesos posteriores,
pues la malla de puntos construida con el Voronoi Tessellation se genera sólo a partir del primer archivo.
Esto se corroboró ejecutando el VTMLE tanto con el segundo archivo de entrada original como con el
reducido, obteniendo exactamente los mismos resultados.
El segundo experimento fue para ver si el VTMLE ejecutado desde Hadoop se comporta de la misma
forma que el VTMLE ejecutado en forma secuencial. Para esto se ejecutó el VTMLE secuencial sobre la
totalidad de los datos, y se ejecutó Hadoop con una partición de una sola celda, obteniéndose resultados
idénticos. Para ver si el proceso de particionamiento afectaba o no el proceso general, se ejecutó el
VTMLE secuencial sobre el set de datos resultantes de un map. Se realizó con una partición de 2 celdas
58
sin overlap. Esta prueba arrojó como resultado archivos idénticos a los obtenidos con el software
paralelo.
Finalmente, se hizo un análisis de los resultados obtenidos por una partición de dos celdas sin overlap,
comparándose con la partición original de sólo uno. De ello se obtuvo la siguiente tabla de datos:
Tabla 1: Partición de dos celdas sin overlap
Celda 1 Celda 2 Ambas
Celdas
Partición
Original
N° de galaxias 45590 33156 78746 78746
N° de cúmulos 2487 1836 4323 4170
N° de cúmulos en la partición original 251 219 470
N° de galaxias en cúmulos 7306 5373 12679 11947
N° galaxias no repetidas en cúmulos 7207 5298 12505 11776
N° de galaxias en cúmulos de la
partición original
2339 2216 4555
El resultado de los experimentos muestra que el software paralelo es consistente con el software
secuencial, no modificando su comportamiento sino sólo paralelizándolo. Los resultados que obtienen
ambos software son altamente dependientes de las condiciones de borde que se le entrega al problema,
variando los cúmulos resultantes según el orden en que se vayan encontrando. Esto explica la baja
coincidencia entre las particiones de 1 y 2 celdas. Al igual que con los cúmulos de galaxias, al analizar las
galaxias que integran estos cúmulos también se encuentra una baja coincidencia entre las particiones.
Las galaxias “nuevas”, que no pertenecían a ningún cúmulo de la partición con una sola celda,
corresponden aproximadamente un 64% de las galaxias de la partición de dos celdas. Para validar este
experimento se verificó además que la partición de los datos correspondía a la totalidad de éstos y fuese
disjunta, al no haber coincidencias entre los datos de las celdas y al sumar entre ambas el mismo número
de líneas que el archivo original.
Una de las razones por la que las que tanto los cúmulos como las galaxias difieran al dividirse los datos,
es que la mayoría de los cúmulos poseen pocos elementos. Esto significa que a pesar que se encuentran
cúmulos, no hay grandes densidades de galaxias en algunas de las zonas donde los encuentran, por lo
59
que si se recorre de manera distinta, o se toma una densidad promedio distinta, se pueden encontrar
distintos cúmulos, en especial de los pequeños.
La tabla 2, adjuntada en el anexo, da cuenta de la distribución de la cantidad de cúmulos en función de
su tamaño. De aquella tabla se obtuvo el siguiente gráfico:
Figura 11: Cantidad de cúmulos de galaxias en función de su tamaño
En la Figura 11 se puede ver que la gran mayoría de los cúmulos tiene sólo 2 galaxias, y que la cantidad
de cúmulos disminuye abruptamente, siendo muy pocos los que tienen más de 6 galaxias. Se esperaba
encontrar resultados distintos al analizar los cúmulos que se repetían entre las particiones con una y dos
celdas, esperando una mayor coincidencia entre los cúmulos con más miembros, sin embargo los
resultados son similares. Estos se pueden observar en la tabla siguiente:
0
500
1000
1500
2000
2500
3000
2 4 6 8 10 12 14 16 18 21 24 26 28 40
Primer Cuadrante
Segundo Cuadrante
Original
60
Tabla 2: Cúmulos repetidos entre las particiones de una y dos celdas
Galaxias por cúmulo Cantidad de cúmulos
2 387
3 54
4 19
5 3
6 4
7 1
9 1
14 1
5.3. Desempeño
En esta sección se analiza el desempeño del software, tanto en uso de recursos como en el tiempo que
se demora, comparándolo con el software secuencial.
5.3.1. Uso de recursos
Las diferentes etapas de desarrollo del software se han probado con sólo una máquina, la que posee un
procesador de cuatro núcleos. Si bien se hicieron ejecuciones en paralelo, no sirve para establecer una
tendencia en el uso de recursos. Sólo se puede decir que dada la magnitud de las pruebas que se
hicieron, al duplicar los reducers, se duplican también nos núcleos utilizados, y el tiempo de ejecución se
reduce en aproximadamente la mitad.
61
Si bien se tuvo acceso a un clúster de máquinas, los nodos de éste compartían el disco duro. Por esta
razón no fue posible, ni tenía sentido, instalar el framework Hadoop, al no poderse trabajar con datos
distribuidos en nodos independientes.
5.3.2. Desempeño comparativo
Ejecutar un programa con Hadoop tiene un sobrecosto asociado a la coordinación y funcionamiento del
sistema distribuido. Esto hace que el desempeño y los costos en recursos computacionales varíen de
forma no lineal con el número de nodos destinados. Además existe un límite en que el tiempo de
procesamiento no va a disminuir al agregar más máquinas.
Experiencias de uso han mostrado que para grandes cantidades de datos Hadoop escala de forma lineal
al agregarse más nodos, siendo el sobrecosto de funcionamiento marginal, tal como se muestra en la
Figura 12. En esta se realizó un experimento simple, donde se computa el histograma de frecuencias
obtenidas por una expresión regular simple sobre un conjunto de datos de 350 GB. Sin embargo, si existe
un cuello de botella relacionado a restricciones en el procesamiento de datos, situación que ocurre al
paralelizar el VTMLE por las restricciones asociadas al tamaño de las celdas, Hadoop escala de forma
logarítimica como se muestra en la Figura 13. Este experimento muestra una iteración de un algoritmo
similar a k-means clustering. El análisis y ambos gráficos fueron obtenidos de un estudio que analiza la
escalabilidad de Hadoop en base a operaciones fáciles de paralelizar (16).
62
Figura 12: Información procesada en función del número de nodos
Figura 13: Información procesada en función del número de nodos, para una ejecución con restricciones
63
El análisis previo muestra que la comparación con un sistema secuencial sea compleja y pierda sentido al
enfocarse en sólo un aspecto a la vez. Claramente la ejecución en paralelo en general va a tomar menos
tiempo, pero va a gastar una mayor cantidad de recursos. Por otra parte, para una cantidad pequeña de
datos el sistema secuencial se va a comportar mejor.
También se pueden hacer comparaciones enfocadas en un área en particular, como por ejemplo: el
tiempo de ejecución, la escalabilidad, la cantidad máxima de datos que se pueden procesar, etc. No tiene
sentido comparar la escalabilidad y la cantidad máxima de datos que se pueden procesar, ya que sólo es
posible lograr buenos resultados con una aplicación distribuida, estando la lineal limitada en ambos
aspectos. Para comparar el tiempo de ejecución, se escribieron scripts para cada una de las pruebas que
se realizaron, donde se medía el tiempo de ejecución de los procesos en su totalidad. Se hicieron al
menos dos pruebas no consecutivas para cada uno de los procesos, no con el fin de obtener estadísticas,
sino simplemente verificar que estos resultados no fuesen accidentales, corroborando que el nuevo
tiempo obtenido estuviese dentro de un rango de cercanía.
Para ver el sobrecosto de la ejecución de scripts de python para el manejo de datos, se realizaron dos
pruebas:
Se ejecutó el script del Vocludet directamente, entregándose datos de entrada y recibiendo los
archivos de salida correspondientes. Esta prueba tomó un tiempo aproximado de 18 minutos.
Se ejecutó un script que entregaba los datos de entrada línea por línea al stdin del mapper, cuyo
resultado era entregado por el stdoud al stdin del reducer, el cual además de generar los
archivos de resultado también creaba uno utilizado para un posterior reensamblaje de
resultados. Es una ejecución similar a lo que haría Streaming, pero sin usar hadoop y con una
partición de sólo una celda. Esta prueba tomó alrededor de 19 minutos.
De los tiempos de ejecución se puede concluir que el sobrecosto del manejo de datos por parte de
python es bajo, si además se consideran las tareas de parear y crear archivos que no se realizaron en la
primera prueba.
Para ver el sobrecosto que significaba todo el aparataje asociado a hadoop (sistema de archivos,
redundancia de datos, sincronización y control, etc.), se hizo una tercera prueba:
Se ejecutó el programa en hadoop con una partición de sólo una celda, y por ende sin
paralelismo al estar trabajando con un solo núcleo. Esta prueba tomó aproximadamente 20
minutos en ejecutarse.
64
Del resultado de las pruebas anteriores se puede concluir que el sobrecosto en tiempo de hadoop es
menor, aunque no se sabe cuánto de este tiempo es constante y cuánto depende de la cantidad de datos
de entrada.
Luego se llevó a cabo una prueba para ver el incremento en eficiencia al ejecutarse hadoop con 2
núcleos:
Se creó una partición de los datos con dos celdas, y en la configuración se definió que se debían
utilizar 2 reducers. Esta prueba resultó en un tiempo total de 10 minutos.
Con este resultado se verificó que al duplicar la cantidad de procesadores, el tiempo tiende a reducirse a
la mitad. Además se comprobó que el tiempo de sobrecosto fijo pasa a ser secundario a pesar de que las
pruebas no sean tan intensivas en el uso de recursos. La división fue angular, por lo que la cantidad de
datos en cada celda fue similar y el tiempo pudo por ende reducirse a la mitad. De haber sido una
división radial, o alguna otra en la que la cantidad o dispersión de datos hubiese sido muy distinta, el
tiempo total estaría determinado por el procesamiento de datos en la celda de más carga.
Se hizo una cuarta prueba, esta vez usando hadoop con 4 reducers:
Se creó una partición de los datos en cuatro celdas, y se definió en la configuración que se
debían utilizar 4 reducers. El tiempo resultante fue de aproximadamente 10 minutos.
Si bien se esperaba que el tiempo disminuyese nuevamente a la mitad, se ve que a pesar de que se
incrementó la cantidad de reducers, la cantidad de procesadores utilizados siguió siendo dos. Esto se
debe a que la máquina usada tiene sólo cuatro núcleos, y al estar el sistema operativo ejecutando otros
procesos, no se pudieron asignar todos los recursos a hadoop.
Las pruebas anteriores no contemplaron el tiempo de re ensamblaje de los datos, por lo que hay que
agregarle este costo a una ejecución real. Sin embargo, cuando se llevó a cabo este proceso, la ejecución
duró del orden de unidades de segundos, por lo que no tiene mayor relevancia si el rango de error es de
minutos.
65
6. Conclusiones
A continuación se presentan las conclusiones agrupadas según los objetivos que se plantearon:
Se logró implementar un software que permite el análisis de datos astronómicos en forma
paralela. Para esto se utilizó el framework Hadoop en la partición de los datos, ejecución del
software detector de cúmulos de galaxias, y en la distribución de la carga de procesamiento.
Para el re ensamblaje de los resultados se utilizó un script escrito en Python. Con estas
herramientas se logró ejecutar el algoritmo VTMLE y reconstruir los resultados, pudiendo
además ejecutarse otros procesos astronómicos. Quedan propuestas herramientas para el re
ensamblado de otros procesos astronómicos que puedan ser útiles en un futuro. También queda
propuesto determinar el número óptimo de mappers y reducers, y la relación entre éstos y los
parámetros usados para la división de los datos.
Se diseñaron e implementaron 3 estrategias para el particionamiento de datos, dos de las cuales
sirven para paralelismo de menor escala, y una para paralelismo a gran escala. Se obtuvieron los
parámetros límites para esta última, y se determinó que dada la distribución de datos con la que
se trabajó, la cual no es homogénea, no es posible lograr un alto nivel de paralelismo. Queda
propuesta una partición ad-hoc para la distribución de datos existente, y el desarrollo de
estrategias basadas en esta última para la partición de otros sets de datos.
Se diseñaron 2 estrategias de re ensamblaje de datos, y se implementó una de estas. La
estrategia implementada trabaja correctamente sobre cúmulos sin repetición de elementos, sin
embargo no funciona de manera óptima con la implementación del algoritmo VTMLE utilizada,
pues ésta devuelve galaxias repetidas en varios cúmulos. Los cúmulos de galaxias resultantes
varían según la partición que se utilice, por lo que no se puede hacer una verdadera
comparación con el resultado del software secuencial.
Se verificó una mejora al software secuencial, al haberse logrado ejecuciones en paralelo que
dividieron el tiempo de procesamiento en 2. Es evidente que el tiempo se puede seguir
reduciendo al asignar más nodos de ejecución al problema. Queda propuesto ejecutar este
software en un clúster de computadores o con cloud computing, para medir las mejoras en
tiempo. También queda propuesta la ejecución de este software con sets de datos más grandes,
para medir la escalabilidad de éste.
Se proponen mejoras al software secuencial, implementando los cambios al algoritmo
propuestos en la sección 3.2.
66
7. Anexos
Tabla 3: Distribución de cúmulos por tamaño
Galaxias por
Cúmulo
Primera Celda Segunda Celda Original
2 1549 1137 2678
3 461 347 788
4 203 150 303
5 90 92 165
6 72 31 80
7 34 21 45
8 18 13 22
9 20 11 23
10 4 9 17
11 10 5 10
12 2 4 6
13 2 6 6
14 9 2 8
15 2 0 4
16 4 0 3
17 2 2 1
18 1 3 2
20 1 1 0
21 0 0 1
22 1 0 0
24 0 0 2
25 0 1 0
26 0 0 2
27 1 0 0
28 1 0 0
30 0 1 2
40 0 0 1
88
0 0 1
67
Mapper Function 2 celdas 10% de overlap
#!/usr/bin/env python
import sys
split_number = {'x':2, 'y':1, 'z':1}
min_val = {'x':2.57441, 'y':-0.10975, 'z':0.002}
max_val = {'x':3.88319, 'y':0.04363, 'z':0.2995}
max_dimensions = {'x':1000, 'y':100, 'z':1000}
overlap_perc = {'x':0.1, 'y':0.1, 'z':0.1}
x_overlap_val = ((max_val['x'] - min_val['x']) / split_number['x']) * overlap_perc['x']
y_overlap_val = ((max_val['y'] - min_val['y']) / split_number['y']) * overlap_perc['y']
z_overlap_val = ((max_val['z'] - min_val['z']) / split_number['z']) * overlap_perc['z']
overlap_val = {'x':x_overlap_val, 'y':y_overlap_val, 'z':z_overlap_val}
#columns with the x, y and z values fron the 2dFNGgals file
col_2d = [0, 1, 2]
#columns with the x, y and z values fron the best.observations file
col_ob = [4, 5, 6]
x_ponderator = split_number['x'] / (max_val['x'] - min_val['x'])
y_ponderator = split_number['y'] / (max_val['y'] - min_val['y'])
z_ponderator = split_number['z'] / (max_val['z'] - min_val['z'])
ponderator = {'x':x_ponderator, 'y':y_ponderator, 'z':z_ponderator}
def get_quadrant(val, coord):
list = []
plus_val = val + overlap_val[coord]
minus_val = val - overlap_val[coord]
quad = int((val - min_val[coord]) * ponderator[coord])
quad_plus = int((plus_val - min_val[coord]) * ponderator[coord])
quad_minus = int((minus_val - min_val[coord]) * ponderator[coord])
#correct the max vals problem (max vals get their own cell)
if val == max_val[coord]:
quad -=1
68
list.append(quad)
#if the values are out of the min and max range
if plus_val < max_val[coord] :
list.append(quad_plus)
if minus_val > min_val[coord] :
list.append(quad_minus)
return set(list)
# input comes from STDIN (standard input)
for line in sys.stdin:
# remove leading and trailing whitespace
line = line.strip()
#added cast to coords[n]
coords = line.split()
#if the input is from the 2dFNGgals file
if len(coords) == 8:
coord_x = float(coords[col_2d[0]])
coord_y = float(coords[col_2d[1]])
coord_z = float(coords[col_2d[2]])
coord = {'x':coord_x, 'y':coord_y, 'z':coord_z}
#if the input is from the best.observations file
elif len(coords) == 12:
coord_x = float(coords[col_ob[0]])
coord_y = float(coords[col_ob[1]])
coord_z = float(coords[col_ob[2]])
coord = {'x':coord_x, 'y':coord_y, 'z':coord_z}
else:
continue
#if the values are out of the min and max range
if coord['x'] > max_val['x'] or coord['x'] < min_val['x'] or coord['y'] > max_val['y'] or coord['y'] <
min_val['y'] or coord['z'] > max_val['z'] or coord['z'] < min_val['z']:
continue
quadrants = {}
quadrants['x'] = get_quadrant(coord['x'], 'x')
quadrants['y'] = get_quadrant(coord['y'], 'y')
quadrants['z'] = get_quadrant(coord['z'], 'z')
all_quadrants = []
69
for x in quadrants['x'] :
for y in quadrants['y'] :
for z in quadrants['z'] :
all_quadrants.append(`x`+','+`y`+','+`z`)
#print all quadrants to output
for quadrant in all_quadrants :
print '%s\t%s' % (quadrant, line)
70
Re Assembly Function
def merge_clusters(file1_path, file2_path) :
#----------------- create hash table with positions from the first file ------------------
#read the first gid file
file1 = open(file1_path, 'r')
#hash table with {position : (line_number, number_of_elements)}
table = {}
#list of line numbers with invalid clusters
invalid_clusters = []
#line number counter
line_num = 0
for line in file1 :
# remove leading and trailing whitespace
line = line.strip()
# parse the line
split = line.split()
#add the list of elements to the table
for elem in split :
#{pos : (line, numElems)}
table[elem] = [line_num, len(split)]
#increment line number counter
line_num += 1
#-------- read second file and find intersection of elements between both sets of clusters ---------
#open the second file
file2 = open(file2_path, 'r')
#create a new file for writing the merged result
merged_file = open('merged_file', 'w')
#compare new elements in a double for
for line in file2 :
#boolean for determinig validity of cluster
valid_cluster = True
# remove leading and trailing whitespace
line = line.strip()
# parse the line
split = line.split()
#check for repetition
for elem in split :
#if the element is part of another cluster
71
if table.has_key(elem) :
#if the cluster isn't already eliminated
if invalid_clusters.count(table[elem][1]) == 0 :
#if the cluster in file2 is larger than the one in file1
if len(split) > table[elem][1] :
#discard the cluster in file1
invalid_clusters.append(table[elem][0])
#no need to eliminate every element with that line number
#if the cluster in file1 is larger than the one in file2
else:
#eliminate the whole cluster
valid_cluster = False
break
#if none of the elements were already part of another cluster
if valid_cluster :
#write line in the merged file
merged_file.write(line + '\n')
#-------------------------- write valid clusters from file1 --------------------------
#file with clusters
file1 = open(file1_path, 'r')
#line number counter
line_num = 0
#for every cluster in file1
for line in file1 :
#if the cluster is valid
if invalid_clusters.count(line_num) == 0 :
#write cluster in the merged file
merged_file.write(line.strip() + '\n')
#increment counter
line_num += 1
72
8. Bibliografía
1. Okabe. Spatial Tessellations. s.l. : John Wiley & Sons, 2000.
2. Pizarro, Daniel. Galaxy Cluster Detection Using Nonparametric Maximum Likelihood Estimation of
Features in Voronoi Tessellations. 2007, Master thesis in computer science, University of Chile.
3. Pizarro, Daniel, Hitschfeld, Nancy & Campusano, Luis. Clustering of 3D Spatial Points Using Maximum
Likelihood Estimator over Voronoi Tessellations: Study of the Galaxy Distribution in Redshift Space. 2006,
IEEE Computer Society.
4. Large Synoptic Survey Telescope. http://www.lsst.org/lsst/faq.
5. Colless, Matthew. 2dFGRS an Introduction.
http://www2.aao.gov.au/~TDFgg/Public/Survey/description.html.
6. Intel. Excerpts from A Conversation with Gordon Moore: Moore's Law. 2005.
http://download.intel.com/museum/Moores_law/Video-
transcripts/excepts_a_Conversation_with_gordon_Moore.pdf.
7. Adve, Sarita V., Adve, Vikram S. & Agha, Gul. Parallel Computing Research at Illinois The UPCRC
Agenda. 2008. http://www.upcrc.illinois.edu/UPCRC_Whitepaper.pdf.
8. Yahoo! Inc. Yahoo! Hadoop Tutorial. Yahoo! Developer Network.
http://developer.yahoo.com/hadoop/tutorial/.
9. Voit, G. Mark. Tracing cosmic evolution with clusters of galaxies. 2004.
10. Shankland, Stephen. news.cnet.com. http://news.cnet.com/8301-10784_3-9955184-7.html.
11. Wiley, Keith. Astronomical Image Processing With Hadoop. http://escience.washington.edu/get-
help-now/astronomical-image-processing-hadoop.
12. Amazon Web Services. Amazon Web Services Blog. http://aws.typepad.com/aws/2008/02/taking-
massive.html.
13. Goodman, Jacob & O'Rourke, Joseph. Handbook of Discrete and Computational Geometry. 2004.
73
14. Ghemawat, Sanjay, Gobioff, Howard & Leung, Shun-Tak. The Google File System.
http://static.googleusercontent.com/external_content/untrusted_dlcp/labs.google.com/en//papers/gfs-
sosp2003.pdf.
15. Virtual Observatory India. http://vo.iucaa.ernet.in/2df/Survey/description.html.
16. Papadimitriou, Spiros. bitquill. 2008. http://www.bitquill.net/blog/?p=17.