Está en la página 1de 31

Universidad del Zulia

Facultad de Ingeniera
Divisin de Estudios para Graduados
Instituto de Clculo Aplicado

Postgrado en Computacin Aplicada

Ctedra: Computacin Paralela


Profesor: Nelson Arap / Carlos Arevalo

Trabajo Prctico #1
Implementacin de un algoritmo paralelo
para procesar archivos de texto

Autor: Jaime Soto


C.I.: 7.715.519

Maracaibo, lunes 12 de abril de 2010

REQUISITOS DEL TRABAJO PRCTICO

Funcin del programa:


Programa paralelo que utilice MPI (MPJ) que liste las 20 palabras que aparecen con
ms frecuencia en un archivo de texto y la cantidad de veces que aparece la palabra.
Informe debe incluir:
1.- Una descripcin del diseo del programa que incluya los siguientes puntos:
a.- Diseo paralelo utilizado
b.- Estructura de datos utilizada
c.- Estrategia de divisin del procesamiento de cada CPU
2.-Resultados de las corridas del programa con 1, 2, 4, 8, 12 y 16 nodos. Para cada cantidad
de nodos se deben hacer 3 corridas y se debe crear una tabla con los tiempos de ejecucin
de cada corrida, el tiempo mnimo, el speedup (basado en el tiempo mnimo) y la eficiencia
(speedup/num_cpus)
3.- Cdigo fuente del programa

INDICE
1.- Introduccin
2.- Plataforma de desarrollo
3.- Diseo del Algoritmo
3.1.- Diseo del algoritmo serial
3.2.- Diseo del algoritmo paralelo
3.3.- Estructura de datos utilizada
3.4.- Estrategia de divisin del procesamiento de cada CPU
4.- Procesamiento
4.1.- Procesamiento en el Cluster del Instituto de Clculo Aplicado
4.2-. Procesamiento en un Computador Porttil HP Core Duo
5.- Resultados
5.1.- Indicadores de rendimiento del Procesamiento
5.2.- Resultados de la corrida en el Cluster del ICA
5.3.- Resultados de la corrida en el porttil HP Core Duo
5.4.- Anlisis de los resultados
6.- Cdigo fuente

INTRODUCCIN
El procesamiento en paralelo no es ciertamente un tema reciente. Desde hace
unas cuantas dcadas este tema como el procesamiento distribuido han sido motivo de
investigacin

inversin

en

diferentes

reas

de

la

ciencia

la

tecnologa.

Desafortunadamente, el costo de computadores con mltiples procesadores relegaba el


tema a las pocas personas que tenan acceso a estas. Sin embargo, el desarrollo de las
tecnologas de computadores personales con varios procesadores a un costo accesible al
pblico permite el desarrollo de la computacin paralela, tanto software como hardware,
en diferentes escenarios, desde centros de investigacin avanzada hasta el desarrollo de
aplicaciones en la paz del hogar en un humilde computador personal.
El

presente

trabajo

tiene

el

propsito

de

resolver

un

problema

de

procesamiento de informacin de archivos texto a travs de el diseo, codificacin y


ejecucin de un algoritmo paralelo. Para codificar dicho algoritmo se utilizar el lenguaje
Java y la plataforma IBIS MPJ desarrollada en la Universidad Libre de Holanda. Esta
herramienta de tipo explcito es una plataforma flexible y eficiente de pase de mensajes
para la ejecucin de algoritmos en Paralelo tanto en Grids como en clusters de
computadoras. Tambin se disear y codificar el algoritmo secuencial para poder tener
la informacin suficiente por medio de unos indicadores bien definidos.
Finalmente, se correr el programa o aplicacin en varias configuraciones de
procesadores para verificar la rapidez de ejecucin del algoritmo tomando en cuenta los
indicadores: speedup y eficiencia. Estos valores permitirn verificar lo planteado por la
Ley de Amdhal y las limitaciones del procesamiento en paralelo.

2.-1.- PLATAFORMA DE DESARROLLO


La aplicacin se desarroll en el ambiente de programaci NetBeans IDE 6.8 que
incluye el Java Standard Development Kit (JDK) version 6.0. Para desarrollar un algoritmo
paralelo se requiere una plataforma particular que provea los mecanismos para efectuar el
procesamiento en paralelo. Tambin se compilaron los archivos en el ambiente Unix del
cluster del Instituto de Clculo Aplicado (ICA).
En este caso, se utiliz la herramienta IBIS MPJ, derivada de la Biblioteca en C MPI
(Message Passing Interface), que es una herramienta explicita que tiene como modelo de
comunicacin el pase de mensajes. Es un sistema de software para computacin paralela en
Grids, escrito en Java, desarrollado por la Universidad Vrije de Holanda. Este da soporte al
desarrollo de aplicaciones escritas en Java (la mayora de las bibliotecas de computacin
paralela estn escritas para C o Fortran).
El modelo de pase de mensajes se define como un conjunto de procesos que se
comunican enviando y recibiendo mensajes y la transferencia de data requiere operaciones
cooperativas para ser ejecutada por cada proceso (una operacion de envio debe tener una
operacion de recepcion). La programacion con pases de mensajes es hecha enlazando y
haciendo llamadas a bibliotecas las cuales administran el intercambio de data entre los
procesadores.
Siendo una herramienta explicita, la librera IBIS MPJ requiere que el programador
identifique las partes del programa que pueden ser resueltas en paralelo. Adems, tiene que
asignar las tareas a procesadores, realizar balance de carga y la comunicacin entre
procesos. Sirven en prcticamente todas las circunstancias y permiten obtener un alto
rendimiento.
Siendo una herramienta de paralelismo explcito, se define e implementa un
conjunto de primitivas de intercambio de mensajes. Incluye comunicacin punto a punto
sncrona y asncrona y varios modos de comunicacin colectiva: broadcast, scatter, gather.
Generalmente se usa para paralelismo SPMD (Single Program Mltiple Data)

Para instalar la libreria IBIS MPJ y ejecutar los programas se efectuan los pasos
siguientes:
1.- Bajar el archivo ZIP de Ibis MPJ http://www.cs.vu.nl/ibis/mpj.html
Release ID
Nombre Publicacin
Fecha de Publicacin
Publicado Por

:
:
:
:

138
ibis-mpj-2.2
20-11-2009
Ceriel Jacobs

2.- Descomprimir el archivo en una carpeta


3.- Agregar la variable de ambiente y entorno en el sistema operativo (para que
pueda ver los archivos de IBIS). En Windos se accede a estas a travs del Panel de control->
Sistema->Opciones de avanzada->Variables de entorno. En Linux/Unix se exporta la variable
MPJ_HOME y se ejecutan los programas desde la misma carpeta donde se encuentran estos.
4.-

La

variable

MPJ_HOME

se

valoriza

con

la

carpeta

(ejemplo:

C:\Users\jsoto\Desktop\CParalela\IBIS\MPJ)
5.- Entrar en netbeans. Crear un proyecto de aplicacin java. Agregar la libreria
Ibis MPJ que agregamos en la opcin de herramientas-biblioteca.
6.- Colocar los archivos de ejemplo en la carpeta de archivos fuentes del proyecto
(src). Tambin los archivos texto se colocan en la misma carpeta. Adems de
estos archivos, hay que agregar el archivo de propiedades que requiere el servidor
mpj para arrancar los servicios de procesamiento paralelo.
7.- El servidor se ejecuta a travs del archivo batch o script mpj-server con el
parmetro --events para visualizar los mensajes de la actividad del mismo.
8.- Se debe crear un archivo de configuracin (ibis.properties) contiene valores
importantes para la ejecucin del servidor entre ellos: el nombre del pool o
grupo de trabajo y la cantidad de procesadores que van a participar en la corrida.
Por lo tanto, se considera una corrida predeterminada.

3.- DISEO DEL ALGORITMO


La estrategia para resolver el problema es elaborar, en primera instancia, un
algoritmo secuencial. Una vez elaborado el diseo de este algoritmo secuencial, se debe
codificar y ejecutar.
El presente problema requiere el conteo de palabras a medida que va recorriendo
un archivo texto. Se deben escoger las veinte (20) palabras que aparecen con ms frecuencia
en dicho texto. Para realizar esto, es necesario identificar cada palabra que se haya ledo y
almacenarla para tener un registro de la frecuencia de aparicin de la misma.
Un paso importante es el esfuerzo de diseo que hay que hacer para paralelizar al
mximo el algoritmo secuencial diseado.
3.1.- Diseo serial
El algoritmo serial simplemente abre un archivo texto, recorre dicho archivo de
incio a fin, tomando cada lnea leda y procesando la lnea por un bloque de instrucciones
encargadas de extraer las palabras y almacenarlas en un arreglo o tabla actualizando el
contador

de

apariciones

en

el

entero

texto.

Este

algoritmo

puede

ser

resuelto

secuencialmente al recorrer el archivo texto desde la primera palabra hasta la ltima


utilizando un nico procesador encargado del recorrido completo. El algoritmo se debe
comportar ms o menos de esta forma:
1.- Se abre el archivo texto y se prepara para ser recorrido del comienzo al final.
2.- Lectura de la primera palabra.
3.- Se crea un arreglo dinmico con un registro para almacenar esta palabra.
3.- Se almacena la palabra y se inicializa su contador en 1.
4.- Se lee la segunda palabra, se compara con la primera. (recorriendo el arreglo
del inicio hasta el final. Si son iguales, el contador aumenta y si son diferentes,
se crea un nuevo registro para almacenar la segunda palabra inicializando su
contador en 1.
5.- Se lee la tercera palabra y as sucesivamente se aplica el paso 4 hasta llegar al
final del archivo.
6.- Concluida la lectura del archivo texto, se obtiene un arreglo con n palabras
con sus respectivos contadores de aparicin.
7.- Se ordena el arreglo de mayor a menor, seleccionando las primeras veinte
palabras.

3.2.- Diseo paralelo


El diseo del algoritmo en paralelo requiere de un enfoque totalmente diverso al
que se plantea en el algoritmo serial, ya que el procesamiento ser compartido por ms de
un procesador. El API de Ibis MPJ, la herramienta utilizada para resolver el problema
presente, define e implementa un conjunto de primitivas de intercambio de mensajes e
incluye comunicacin punto a punto sncrona y asncrona y varios modos de comunicacin
colectiva: broadcast, scatter, gather.
A diferencia del algoritmo secuencial que se encarga de procesar el total de lneas
del archivo texto, el algoritmo paralelo adopta la estrategia SPMD (Single Programa Multiple
Data). El algoritmo se ha diseado con el propsito de ejecutar el mismo cdigo en cada
nodo pero con data diferente. El procesador Root se encarga de leer las lneas de texto y
distribuirlas a los procesadores que se suscriban a la ejecucin del programa. En el caso de
IBIS MPJ, el algoritmo no se ejecuta hasta que se suscriban todos los nodos o la cantidad de
nodos especificados en el archivo de configuracin. Este archivo de configuracin incluye tres
parmetros requeridos para el arranque y ejecucin del programa paralelo.
El servidor de Ibis MPJ se debe ejecutar y requiere de estos parmteros: tamao
del pool, nombre del pool y el nmero de procesadores que intervienen en la corrida (en este
caso, ibis.properties).

3.3.- Estructura de Datos

La estructura de datos ms adecuada para resolver el problema es la Tabla Hash.


Si se utiliza un arreglo o Lista-Arreglo (ArrayList), se requiere la utilizacin de un objeto de
un tipo que contenga la palabra y el nmero de veces que esta se encuentra presente en el
archivo de texto leido. Cada vez que se tome una palabra de cada lnea leda, se debe
recorrer el archivo hasta encontrar la palabra y as aumentar el contador de fecuencias. En el
mejor de los casos, la bsqueda es 1 (si la palabra se encuentra al inicio); sin embargo, el
peor de los casos es N (nmero de palabras presentes en la lista hasta el momento). Esta
situacin ser la misma para cada una de las palabras que se extraen de las lneas de texto
procesadas. Es evidente que este tipo de solucin tiene un alto costo computacional debido al
recorrido que hay que realizar. Por lo tanto, la utilizacin de la palabra como la clave de la
tabla Hash permite el acceso inmediato a esta ya que si no existe la clave, se agrega, en caso

contrario, se actualiza el contador. Para ambos caso, bsqueda e insercin, el tiempo es el


mismo.
Cada instancia de ejecucin de cada nodo administra su propia tabla hash para
almacenar las palabras de las lneas procesadas por este. La tabla hash almacena objetos que
son instancias de la clase

Palabra que contiene la palabra y la cantidad de apariciones

adems de los mtodos estndar set y get. El procesador RAIZ (ROOT) contiene otra tabla
hash que al final recibe y almacena los datos procesados por por l y los otros nodos.
Adems de las tablas hash y los objetos de tipo Palabra, el procesador ROOT utiliza un
arreglo del tipo ArrayList para disponer de las funciones de ordamiento (sort) de mayor a
menor segn el dato de frecuencia de apariciones de la palabra.

3.4.- Estrategia de divisin del procesamiento de cada CPU


La estrategia para dividir el procesamiento en cada CPU ms apropiada para
abordar la solucin del problema es SIMD (Single Instruction Multiple Data) que significa la
aplicacin del mismo conjunto de instrucciones a cada procesador para procesar un conjunto
de datos en porciones separadas. Tambin se utiliza el trmino de paralelismo SPMD (Single
Program Multiple Data).
El algoritmo paralelo diseado incluye los mtodos SEND y RECV bloqueantes
entre el procesador ROOT y el resto de los nodos. Esto quiere decir que se enva un mensaje
con la informacin y permanece en espera hasta que el receptor no reciba la informacin y
contine sus tareas. Lo mismo sucede con el receptor, este permanece en espera por la
informacin que enva el otro nodo. El nodo ROOT se encarga de leer las lneas del archivo
texto y distribuirlas al resto de los nodos. En el caso del presente algoritmo, se le permite al
procesador ROOT procesar lneas de texto tambin para sincerar los resultados y los valores
de los indicadores speedup y eficiencia. Es decir, no se utiliz el nodo ROOT simplemente
como el despachador de data sin intervenir en el proceso.
Dado que se trata de una estrategia de SPMD, se consider la utilizacin de un
parmetro que permite hacer dinmica la cantidad de lneas que el procesador ROOT
distribuye a los nodos durante cada comunicacin. Este parmetro es implementado para
verificar el impacto de la granularidad fina y gruesa en la rapidez de ejecucin del algoritmo.
En vista de que el costo computacional es alto cada vez que el nodo ROOT distribuye
informacin al resto de los nodos, el parmetro de nmero de lneas transmitidas a la vez
permite verificar el modo en que el tipo de granularidad afecta la velocidad de ejecucin de la

aplicacin. El ROOT lee lneas del archivo texto y enva a travs de una arreglo alfanumrico
un bloque que contiene un nmero de lneas en diferentes configuraciones desde 40 lneas
por bloque hasta 1200.
En el caso de distribucin de las lneas leidas, para que cada nodo realice el
procesamiento y la extraccin de las palabras y su frecuencia en el texto, el ROOT utiliza el
mtodo SEND bloqueante y el resto de los nodos el mtodo bloqueante RECV. Los nodos
reciben el bloque de lneas por parte del ROOT, procesan todas las lneas presentes, extraen
las palabras de cada lnea, actualizan la tabla hash y esperan a una nueva lnea o el fin de la
aplicacin.
Una vez concluida la lectura total del archivo texto, los nodos envan la tabla hash
con las palabras extraidas y la frecuencia de aparicin al ROOT y este fusiona los datos
propios y los datos de cada uno de los nodos (las tablas hash). En este caso, los nodos
utilzan el comando SEND y el nodo ROOT el RECV bloqueante.
Durante el envo de los bloques de lneas ledas, el ROOT enva las lneas a travs
de un arreglo alfanumrico (tipo String[]) a los nodos. Por otro lado, una vez que el ROOT
concluye la lectura del archivo y el procesamiento de lneas, los nodos envan al ROOT las
tablas hash con las palabras almacenadas. Para realizar esto, el parmetro de tipo de dato
utilizado en la API De Ibis/MPJ es MPJ.OBJECT. La clase Palabra (contiene la palabra y su
frecuencia) debe implementar la interfaz Serializable para que pueda participar en la
comunicacin de los objetos entre los nodos y el procesador ROOT. Este ltimo recibe las
tablas hash una por una de los diferentes nodos, extrae las palabras y las almacena en su
propia tabla hash actualizando las frecuencias de cada palabra. Una vez actualizada esta
tabla, el ROOT transfiere las palabras almacenadas en la tabla hash a un arreglo (ArrayList) y
se ordena la informacin de mayor a menor, basndose en el dato de frecuencia de aparicin,
el conjunto de las primeras 20 palabras y lo presente en pantalla.

4.- PROCESAMIENTO
4.2.- Procesamiento en el Cluster del Instituto de Clculo Aplicado
Se

descarg

el

archivo

comprimido

ibis-mpj-2.2.zip

del

sitio

web

http://www.cs.vu.nl/ibis/downloads.html, se descomprimi en una carpeta y se estableci la


variable de ambiente MPJ_HOME (export) para tener acceso a los programas/script para
correr el servidor y ejecutar el programa paralelo en cada nodo.
Para acceder como terminal al cluster se utiliz el paquete PuTTy.exe (software de
comunicacin gratuito) y se utiliz FileZilla (software ftp gratuito) para transferir los archivos
(fuentes, texto y propiedades) desde el pc al cluster.

Figura.- Acceso a un terminal del cluster (Host: Godzilla / Puerto: 22)


Pasos para ejecutar el SERVIDOR IBIS MPJ:
1.- Abrir terminal con putty.exe. Host name = godzilla Puerto= 22
2.- Ingresar usuario y password en el terminal
3.- Acceder al nodo a travs del comando: $ssh godzilla
4.- Establecer la variable de ambiente donde se encuentra la libreria y los programas de IBIS
MPJ: $export MPJ_HOME=/home/jsoto/mpj

5.- Correr el script que arranca el servidor: $ $MPJ_HOME/bin/mpj-server events


6.- El servidor arranque y queda en espera de la suscripcin de un grupo de procesadores
para ejecutar un programa en paralelo.

Figura.- Ejecucin del Servidor Ibis-MPJ

Pasos para ejecutar el Programa Paralelo y Serial en un NODO:


1.- Abrir terminal con putty.exe. Host name = godzilla Puerto= 22
2.- Ingresar usuario y password en el terminal.
3.- Acceder al nodo a travs del comando: $ssh C-02 (C-##) ##=cada otro procesador.
4.- Cambiar de directorio para ubicarse donde se encuentran los programas a ser ejcutados, el archivo de
texto y el archivo de propiedades: $ cd tarea
4.- Establecer la variable de ambiente donde se encuentra la libreria y los programas de IBIS MPJ: $export
MPJ_HOME=/home/jsoto/mpj
5.- Correr el script para ejecutar cualquier programa:
$ $MPJ_HOME/bin/mpj-run ProcesarTextoParalelo textox5.txt 100
6.- Este comando puede ser ejecutado como un script: $ bash set_programa
7.- El script mpj-run requiere del parmetro que especifica el programa paralelo y sus parmetros propios.
El programa ProcesarTextoParalelo requiere de 2 parmetros. El primero es el texto que va a ser procesado y
el segundo es el nmero de lneas que

conforman el bloque de transferencia del nodo ROOT a los otros

nodos cada vez que este lee lneas del archivo. Este parmetro es importante ya que permite ver el impacto
de la granularidad fina y gruesa en la velocidad de ejecucin de la aplicacin. Este proceso hay que realizarlo
para cada nodo que participe en la corrida. En una corrida con 2 procesadores ejecutando el mismo
programa la nica diferencia est en la instruccin: $ssh C-03. Para cualquier otra configuracin con ms
procesadores se tendr que ejecutar el mismo nmero de comandos en cada nueva pantalla cambiando
simplemente el nombre del nodo.

Figura .- Ejecucin de una aplicacin desde un nodo Ibis

Para cambiar el nmero de procesadores o algn otro dato del archivo


ibis.properties utilice un simple editor de texto, modifique el parmetro y guarde los cambios.
Abrir el archivo ibis.properties: $ nano ibis.properties (Ctrl-O=Guardar Ctrl-X=Salir) modificar
el parmetro deseado, guardar, salir y volver a realizar la corrida ejecutndo el programa el
nmero de veces definido en la propiedad size.

Figura .- Acceso al archivo de propiedades utilizando el editor de texto nano.

Figura.- Archivo de propiedades ibis.properties

Figura.- Resultados de una corrida del programa (nodo ROOT)

4.3-. Procesamiento en un computador Intel Core Duo 1.83 GHZ bajo Windows
Para ejecutar una corrida de un programa en paralelo en Windows no es muy
diferente. Hay que abrir un terminal y ejecutar el servidor a travs del archivo mpj-server
events. Los programas podran ejecutarse de igual manera en diferentes terminales que se
vayan activando; sin embargo, el ambiente netbeans ofrece la ventaja de establecer un
programa y sus parmetros de ejecucin en la pestaa de propiedades del proyecto. Es
necesario definir la variable MPJ_HOME en las variables de ambiente del sistema operativo.

Figura .- Ejecucin del servidor IBIS MPJ en Windows

Figura.- Configuracin de ejecucin del programa en las propiedades del proyecto netbeans

5.- RESULTADOS
Los programas secuencial y paralelo se ejecutaron en el cluster del Instituto de
Clculo Aplicado para medir la velocidad de procesamiento en diferentes configuraciones de
procesadores. Es decir, la propiedad size (tamao) del grupo (pool) fue variada desde 1
hasta 16 procesadores. Por otro lado, el programa secuencial y paralelo tambin se corri en
un computador porttil HP Intel Core Duo T2400 a 1.84 GHZ in 1GB de RAM pero solamente
para un mximo de dos procesadores y poder realizar comparaciones de desempeo del con
el cluster.
5.1.- Indicadores de rendimiento del Procesamiento
Los indicadores a tomar en cuenta para medir el rendimiento del programa
paralelo son: speedup y eficiencia. En computacin paralela, el speedup se refiere a cuanto

ms rpido es un algoritmo paralelo comparado con un algoritmo secuencial.

p
T1
Tp

: Nmero de procesores
: Tiempo de ejecucin del algoritmo secuencial
: Tiempo de ejecucin del algoritmo paralelo con p procesadores

El speedup lineal o ideal se obiene cuando

. Cuando se corre una

algoritmo con speedup lineal, al duplicar el nmero de procesadores, se duplica la


velocidad de procesamiento.
El speedup se considera absoluto cuando T1 es el mejor tiempo de ejecucin
del algoritmo secuencial, y el speedup es relativo cuando T1 es el tiempo de ejecucin
del mismo algoritmo paralelo en un solo procesador. El speedup relativo es usualmente
utilizado si el tipo de speedup no es especificado, en vista de que no requiere la
implementacin del algoritmo serial.
La Eficiencia es un indicador de rendimiento

El valor de eficiencia, se encuentra tipicamente entre 0 y 1, sirve para estimar


que tan bien son utilizados los procesadores para resolver el problema, comparado con el
esfuerzo que se pierde en la comunicacin y sincronizacin entre los procesadores. Los
algoritmos com speedup lineal y algoritmos corriendo en un solo procesador tiene una
eficiencia de 1, mientra que muchos algoritmos dificiles de ser paralelizados tienen una
eficiencia de (1/log(p)) que se aproxima a 0 en la medida que aumenta el nmero de
procesadores.
5.2.- Resultados de la corrida en el Cluster
En la siguiente tabla se muestran las corridas del algoritmo secuencial y el
algoritmo paralelo. La primer a columna muestra el tiempo en segundos de la corrida del
algoritmo secuencial. A partir de la segunda columna hasta la octava se muestran los
resultados de las corridas del programa paralelo en 1, 2, 4, 8, 12 y 16 procesadores. Estas
tres corridas se realizaron con bloques de 100 lneas.
Tabla. - Tiempos de ejecucin, speedup y eficiencia del algoritmo paralelo y secuencial en el
Cluster del ICA

SECUENCIAL
Corridas
1
2
3

Speedup
Eficiencia

1
14,8160
14,9720
14,7550
14,8160
1
1

1
16,2860
16,4160
15,7070
16,2860
0,91
0,91

2
11,6420
11,3080
11,5130
11,5130
1,29
0,64

PARALELO
4
8,2660
8,2320
8,3920
8,2660
1,79
0,45

8
7,1900
7,1100
7,2990
7,1900
2,06
0,26

12
7,0120
7,0820
7,1370
7,0820
2,09
0,17

16
7,7470
7,3640
7,3740
7,3740
2,01
0,13

En la misma tabla se muestra el speedup y la eficiencia de ambos algoritmos. Para


este caso con el archivo texto leido y el parmetro de 100 lneas por bloque, el tiempo
mnimo result 7.01 segundos al igual que el mejor speedup (2.09) cuando se utilizaron 12
procesadores. En lneas generales se verifica la Ley de Amdhal via a via que se aumentan los
procesadores que ejecutan el algoritmo.
Por otro lado, tambin se verific el comportamiento de la Eficiencia ya que sus
valores tienden a 0 cada vez que se incrementa el nmero de procesadores.

La siguiente tabla muestra los valores del tiempo de ejecucin del algoritmo
paralelo cambiando la granularidad de los datos (fina y gruesa). Para cumplir este objetivo,
se le pas al algoritmo como parmetro el nmero de lneas que conforman un bloque de
lectura para ser procesado por el propio nodo ROOT y el resto de los nodos. En pocas
palabras, el nodo ROOT forma bloques de 40, 100, 400, 800 y 1.200 lneas y los distribuye
enviando un bloque a cada nodo. En este caso se considera granularidad fina al bloque de 40
lneas y granularidad gruesa al de 1.200.
Tabla.- Granularidad fina y gruesa (bloques de lneas de diverso tamao)

40
100
400
800
1200

1
16,28
16,29
16,37
16,24
16,04

2
11,91
11,51
12,87
12,53
12,23

4
8,47
8,27
8,16
7,79
7,70

8
7,66
7,19
7,23
6,95
7,15

12

16

6,8960

6,8940

En la anterior tabla se observa que al aumentar los procesadores disminuy el


tiempo de ejecucin del algoritmo. Se puede verificar que el menor tiempo de ejecucin
obtenido fue el de un bloque de 400 lneas con 16 procesadores.

Servidor Ibis MPJ


Procesamiento Paralelo
18
16

12
10
8
6
4
2
0
0

10

12

14

16

18

Nro. Procesadores
Speedup

Speedup Ideal

Figura.- Speedup del programa paralelo ejecutado en el cluster del ICA

Servidor Ibis MPJ


Algoritmos Paralelos
1
0,9
0,8
0,7

Eficiencia

Speedup

14

0,6
0,5
0,4
0,3
0,2
0,1
0
1

12

16

Nro. Procesadores
Figura.- Eficiencia del programa paralelo ejecutado en el cluster del ICA

Servidor Ibis MPJ


Granularidad (1 procesador)
16,4
16,3

Tiempo

16,2
16,1
16
15,9
15,8
40

100

400

800

1200

Nro. lneas por bloque

Fuente.- Granularidad fina y gruesa (1 procesador)

Servidor Ibis MPJ


Granularidad (2 procesadores)
13,00

Tiempo

12,50
12,00
11,50
11,00
10,50
40

100

400

800

Nro. lneas por bloque

Fuente.- Granularidad fina y gruesa (2 procesadores)

1200

Servidor Ibis MPJ


Granularidad (4 procesadores)
8,60
8,40

Tiempo

8,20
8,00
7,80
7,60
7,40
7,20
40

100

400

800

1200

Nro. de lneas por bloque


Fuente.- Granularidad fina y gruesa (4 procesadores)

Servidor Ibis MPJ


Granularidad (8 procesadores)
7,80
7,60

Tiempo

7,40
7,20
7,00
6,80
6,60
6,40
40

100

400

800

Nro. de lneas por bloque

Fuente.- Granularidad fina y gruesa (8 procesadores)

1200

5.3.- Resultados de la corrida en el porttil HP Core Duo


Tabla. - Tiempos de ejecucin, speedup y eficiencia del algoritmo paralelo y secuencial en el
Computador Porttil HP Core Duo

Secuencia

Paralelo
1
21,7290
18,8780
19,0800
19,0800
0,78
0,78

1
18,1720
18,1630
18,0340
18,1630
0,82
0,82

Corrida
1
2
3
Promedio
Speedup
Eficiencia

2
14,7490
14,6030
14,5610
14,6030
1,01
0,51

Servidor Ibis MPJ


Granularidad (1 procesador)
19,25
19,20
19,15

19,05
19,00
18,95
18,90
18,85
40

100

400

800

1200

Nro. de lneas por bloque

Fuente.- Granularidad fina y gruesa (1 procesador)

Servidor Ibis MPJ


Granularidad
15,2
15
Tiempo

Tiempo

19,10

14,8
14,6
14,4
14,2
40

100

400

800

Nro. de lneas por bloque

Fuente.- Granularidad fina y gruesa (2 procesadores)

1200

5.4.- Anlisis de los resultados

De los resultados obtenidos, se ve claramente que se comprob la Ley de


Amdhal dada la tendencia de la curva a estabilizarse cerca de una valor sin que creciera
ms. Es decir, a medida que se aumentaban los procesadore el tiempo de ejecucin no
disminuye y el Speedup llega a un lmite. De igual manera, se verific la tendencia a 0 de
los valores de Eficiencia cada vez que se incrementaba el nmero de procesadores que
participaron en la ejecucin del algoritmo.
Tambin se observ que los valores resultantes del Cluster fueron ligeramente
mejores que los del Porttil HP.
Por otro lado, al efectuar las corridas con diferentes parmetros de bloques de
lnea, se verific que la granularidad es un elemento para tomar en cuenta ya que los
resultados varan de manera importante cuando este valor se modifica. De hecho, los
mejores tiempos se obtuvieron con un bloque de 400 lneas para cada procesador en un
pool de 12 procesadores.
En el caso del HP Core Due, el mejor tiempo de ejecucin (14.6 seg) se obtuvo
con una granularidad fina de un bloque de 100 lneas por procesador.
Finalmente, todos los resultados obtenidos se adecan a lo que en teora y
experiencia se ha planteado durante el curso.

6.- CDIGO FUENTE

La aplicacin que resuelve el problema est compuesta por varios programas que
trabajan en conjunto. Primero se dise el algoritmo secuencial (ProcesdorTextoSecuencial) y
despus el paralelo (ProcesadorTextoParalelo). Se dise una Clase Palabra para albergar
los objetos de ese tipo y poder almacenarlos en las tablas hash. La serializacin permiti que
la tabla hash se pudiera enviar por medio de mensajes entre los diferentes nodos.
Hubo la necesidad de crear una Clase ComparadorPalabras para poder
determinar el orden de la lista final de las 20 palabras con mayor frecuencia de aparicin en
el texto procesado. Adems de estos archivos, se requiere el archivo texto a ser procesado y
el archivo de propiedades (ibis.properties) donde se encuentran los parmetros de la corrida:
lugar de ejecucin del servidor, nombre del pool o grupo al que se unirn los nodos
participantes y el nmero de procesadores que van a ejecutar el programa. Por lo tanto, el
inventario de archivos es el siguiente:

ProcesadorTextoSerial

Programa Java para correr el algoritmo serial

ProcesadorTextoParaelo

Programa Java para correr el algoritmo paralelo

Palabra

Clase para las instancias de los objetos palabra

Utilidades

Clase con mtodos estticos para clculos y


procesamiento de apoyo a los porgramas
secuencial y paralelo

ibis.properties

Archivo de configuracin de la corrida

textox5.txt

Archivo texto a ser procesado

ProcesadorTextoSerial.java
/import java.io.*;
import java.util.Hashtable;
public class ProcesadorTextoSecuencial {
private static final int MAX_PALABRAS = 20;
private static Hashtable tabla
= new Hashtable();
public static void main(String [] arg) {
File archivo
= null;
FileReader fr = null;
BufferedReader br = null;
String nombre_archivo = arg[0];
try {
archivo = new File (nombre_archivo);
fr
= new FileReader (archivo);
br
= new BufferedReader(fr);
String linea;
long tiempo = System.currentTimeMillis();
//
while((linea = br.readLine()) != null) {
if (linea.trim().length() != 0)
Utilidades.procesarLinea(linea, tabla);
}
//
tiempo = System.currentTimeMillis() - tiempo;
System.out.printf("Tiempo total para el proceso fue de %f segundos.\n", tiempo/1000d);
Utilidades.mostrarTabla(tabla, MAX_PALABRAS);
}
catch(Exception excepcion_1){
excepcion_1.printStackTrace();
}finally{
try{
if( null != fr ){
fr.close();
}
}catch (Exception excepcion_2){
excepcion_2.printStackTrace();
}
}
} // Fin del main
} // Fin de la Clase

ProcesadorTextoParalelo.java
import ibis.mpj.MPJ;
import ibis.mpj.MPJException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Hashtable;
/**------------------------------------------------------[DESCRIPCIN ELEMENTOS]
* Todos tienen que entrar en este mtodo
* linea_leida = Solamente interesa a ROOT para ser enviada
* datos
= Arreglo para albergar las palabras ledas
* procesos = Numero de nodos
* rango
= identificacin del procesador
*----------------------------------------------------------------------------*/
/**-----------------------------------------------[C L A S E P R I N C I P A L]
* Clase : Principal
* Funcin:
------------------------------------------------------------------------------*/
public class ProcesadorTextoParalelo {
//-----------------------------------------------------[C O N S T A N T E S]
private static final int ERROR_PARAMETROS = -1;
private static final int ROOT
= 0;
private static final int MAX_PALABRAS = 20;
private static final int LINEAS
= 100;
private static final int TAG_A
= 1;
//-------------------------------------------------------[V A R I A B L E S]
public static Hashtable tabla
= new Hashtable();
public static Hashtable tabla_final = new Hashtable();
public static String[] lista_palabras = new String[0];
/*-----------------------------------------------------------------[M A I N]
* Mtodo MAIN
-------------------------------------------------------------------------*/
public static void main(String[] args) throws MPJException {
//-----------------------------------------------------[PARMETROS IBIS]
MPJ.init(args);
//-----------------------------------------------[PARMETROS APLICACIN]
int param_lineas = 0;
String param_texto = "";
//-----------------------------------[VARIABLES - GESTIN ARCHIVO TEXTO]
File
archivo
= null;
FileReader fileReader = null;
BufferedReader buffer
= null;
int
int

contador_lineas= 0;
contador_nodos = 0;

try {
// Nmero de Procesos e Identificacin del nodo
int procesos = MPJ.COMM_WORLD.size();
int rango = MPJ.COMM_WORLD.rank();
if (rango == ROOT) {
if (args.length >= 2) {
param_texto = args[0];
param_lineas = Integer.parseInt(args[1]);

} else {
Utilidades.imprimirUso();
}
if (param_lineas == 0) {
MPJ.finish();
System.exit(ERROR_PARAMETROS);
}
}
System.out.println( "PROCESOS: "+procesos);
System.out.println( "Nodo: "+rango);
if (rango == ROOT) {
archivo
= new File(param_texto); //---------[ARCHIVO DE TEXTO]
fileReader = new FileReader(archivo);
buffer
= new BufferedReader(fileReader);
}
String[] arreglo_lineas = new String[param_lineas];
MPJ.COMM_WORLD.barrier(); // Espera que estn listos todos los nodos
long tiempo = System.currentTimeMillis(); //--------[TIEMPO INICIAL]
// El procesador ROOT distribuye las lneas que va leyendo del texto seleccionado entre los diferentes
// nodos. Esta operacin la realiza hasta que no hayan ms lneas. La parada se activa cuando el valor "null" se presenta.
//-------------------------------------------[CICLO LECTURA ARCHIVO]
int nodo
= 0;
String linea_leida = "";
if (rango == ROOT) {
//---------------------------------------[LECTURA ARCHIVO TEXTO]
do {
// Limpieza del arreglo para recibir lneas del archivo
arreglo_lineas = limpiarArreglo(arreglo_lineas,"");
contador_lineas = 0;
// 1.- Se leen lneas del archivo
// 2.- Se verifica que no sea el fin del archivo ("null")
// 3.- Si no ocurre el fin de archivo, el arreglo se carga con el bloque de lneas especificadas y se enva al root o al nodo de
turno
// 4.- Si ocurre el fin de archivo. El arreglo puede estar vaco o contener elementos. Si est vaco, termina
// el algoritmo. En caso contrario, se procesan los elementos pendientes y se finaliza el programa
do {
linea_leida = buffer.readLine();
if (linea_leida != null) {
linea_leida = linea_leida.trim();
if (linea_leida.length()!=0) {
arreglo_lineas[contador_lineas]=linea_leida.trim();
contador_lineas++;
}
}
} while ( (linea_leida != null) && (contador_lineas < param_lineas) );
if (linea_leida == null) {
// Se verifica si el arreglo de lineas ledas tiene elementos pendientes cuando ocurri el "null". En
// caso afirmativo, el Root procesa el ltimo bloque
if (contador_lineas > 0) {
procesarBloque( arreglo_lineas );
}
// Se envia el arreglo con elemento null a todos los nodos para finalizar el algoritmo
arreglo_lineas = limpiarArreglo(arreglo_lineas,null);
for (int jNodo=1; jNodo < procesos; jNodo++) {
operaciones( arreglo_lineas, procesos, jNodo, ROOT);
}
} else {
nodo = (nodo < procesos ? nodo: ROOT);
if (nodo == ROOT) {

procesarBloque( arreglo_lineas );
} else {
// cargar un arreglo con varias lineas. El nmero de lneas a pasar en bloque debe ser un parmetro
operaciones( arreglo_lineas, procesos, nodo, ROOT);
}
nodo++;
}
} while (linea_leida != null); //---------------------------------------------------[FIN WHILE]
} else {
// PROCESAMIENTO DE LOS NODOS------------------------------[***]
// NODO - Se mantiene recibiendo lneas para procesar. La regla de parada es recibir un arreglo con el elemento "0" es "null"
nodo
= rango;
boolean fin_texto = false;
do {
fin_texto = operaciones(arreglo_lineas, procesos, nodo, rango);
} while ( !fin_texto );
}
MPJ.COMM_WORLD.barrier();
// Fin del algoritmo y recuperacin de todas las tablas
// Root solicita a cada nodo el envo de la tabla correspondiente
// Los nodos envan su correspondiente tabla y ROOT las procesa
// en un tabla definitiva
if (rango == ROOT) {
Object[] buffObj = new Object[1];
for (int jNodo=1; jNodo < procesos; jNodo++) {
MPJ.COMM_WORLD.recv(buffObj, 0, 1, MPJ.OBJECT, jNodo, 0);
Hashtable tabla_apoyo = (Hashtable) buffObj[0];
unirTablas( tabla_apoyo, jNodo );
}
} else {
Object[] buff = new Object[1];
buff[0] = tabla;
MPJ.COMM_WORLD.send(buff, 0, 1, MPJ.OBJECT, ROOT, 0);
}
MPJ.COMM_WORLD.barrier();
tiempo = System.currentTimeMillis() - tiempo;
System.out.printf("Tiempo total para el proceso #%d fue de %f segundos.\n", rango, tiempo/1000d);
//------------------------------------------------------[RESULTADOS]
if (rango == ROOT) {
Utilidades.mostrarTabla(tabla, MAX_PALABRAS);
} else {
Utilidades.mostrarTabla(tabla, MAX_PALABRAS);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if( null != fileReader )
fileReader.close();
} catch (Exception excepcion_2) {
excepcion_2.printStackTrace();
}
System.out.println("Fin del programa");
MPJ.finish();
} //try-catch-finally
} //--------------------------------------------------------------[FIN MAIN]
/*-------------------------------------------------------------------------* OPERACIONES
* Este mtodo se requiere para poder adjudicar a cada nodo una linea de lectura para ser procesada.
* SE PROCESA LA LINEA Y SE ACTUALIZA LA TABLA CON LAS NUEVAS PALABRAS
-------------------------------------------------------------------------*/

public static boolean operaciones( String[] _arreglo_lineas, int _procesos,


int _nodo, int _rango) throws MPJException {
boolean fin_texto = false;
if (_rango == ROOT) {
Object[] buff = new Object[1];
buff[0] = _arreglo_lineas;
MPJ.COMM_WORLD.send(buff, 0, buff.length, MPJ.OBJECT, _nodo, TAG_A);
} else {
Object[] buffObj = new Object[1];
MPJ.COMM_WORLD.recv(buffObj, 0, buffObj.length, MPJ.OBJECT, ROOT, TAG_A);
String[] buff = (String[]) buffObj[0];
String linea = buff[0];
if (linea == null) {
fin_texto = true;
} else {
procesarBloque(buff); //-----------------------------------[***]
}
}
return fin_texto;
} /*--------------------[O P E R A C I O N E S]---------------------------*/
/*------------------------------------------------------------------[METODO]
* 1.- El nodo recibe la linea de root
* 2.- Este Mtodo se encarga de extraer las palabras de la linea recibida
* 3.- Actualiza la tabla hash del nodo
* 4.- Devuelve una lista de tipo "String[]" para preparar el gather
--------------------------------------------------------------------------*/
public static void procesarBloque(String[] _lineas) {
for (int j=0; j < _lineas.length; j++) {
Utilidades.procesarLinea(_lineas[j], tabla);
}
}
/* */
public static String[] limpiarArreglo( String[] _arreglo, String _elemento ) {
for (int k=0; k < _arreglo.length; k++)
_arreglo[k]="";
_arreglo[0]=_elemento;
return _arreglo;
}
* TAREAS DEL ROOT
public static void unirTablas( Hashtable _apoyo, int _nodo ) {
ArrayList lista = new ArrayList(_apoyo.values());
for (int i=0; i < lista.size(); i++) {
Palabra objeto = (Palabra) lista.get(i);
Palabra palabra = (Palabra) tabla.get( objeto.getTexto());
if (palabra == null) {
palabra = new Palabra( objeto.getTexto(), objeto.getFrecuencia());
} else {
palabra.setFrecuencia( palabra.getFrecuencia() + objeto.getFrecuencia());
}
tabla.put( palabra.getTexto(), palabra );
}
}
} // Fin Clase

Utilidades.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.StringTokenizer;
public class Utilidades {
/* */
public static void procesarLinea(String _linea, Hashtable _tabla) {
String palabra = "";
StringTokenizer st = new StringTokenizer(_linea);
int total
= st.countTokens();
for (int i=1; i<= total; i++) {
palabra = st.nextToken();
palabra = verificarPalabra( palabra );
palabra = palabra.trim();
if (palabra.length() != 0) {
actualizarTabla(_tabla, palabra); //------------------------[***]
}
}
}
/* */
public static String verificarPalabra(String _palabra) {
char caracter;
String revisada = "";
for(int i=0; i < _palabra.length(); i++){
caracter = _palabra.charAt(i);
if (Character.isLetter(caracter))
revisada = revisada + caracter;
}
return revisada;
}
/*
* Este mtodo sirve para actualizar la tabla Hash de cada nodo
* Se verifica si ya la palabra se encuentra en la tabla
*/
public static void actualizarTabla( Hashtable _tabla, String _palabra_key ) {
Palabra palabra = (Palabra) _tabla.get(_palabra_key);
if (palabra == null) {
palabra = new Palabra(_palabra_key, 1);
} else {
palabra.setFrecuencia( palabra.getFrecuencia()+1);
}
_tabla.put(_palabra_key, palabra );
}
/* */
public static void mostrarTabla( Hashtable _tabla, int _maximo ) {
ArrayList lista
= new ArrayList(_tabla.values());
Collections.sort( lista, new ComparadorPalabras() );
int elementos = (lista.size() < _maximo ? lista.size() : _maximo );
System.out.printf("Posicin Frecuencia Palabra\t\n");
System.out.printf("======== ========== =======\t\n");
for (int i=0; i < elementos ; i++) {
Palabra palabra = (Palabra) lista.get(i);
System.out.printf("[%d]\t %d\t %s\n",(i+1), palabra.getFrecuencia(),palabra.getTexto());
}
}
/* */
public static void imprimirUso() {
System.out.println("Uso: java ProcesadorTextoParalelo param_texto param_lineas");
System.out.println("\ndonde:");
System.out.println("\t\tparam_texto = nombre del archivo texto");
System.out.println("\t\tparam_lineas= nombre del archivo donde se encuentra el vector");
}
} // Fin Clase

Palabra.java
import java.io.Serializable;
* Clase : Palabra
* Nota
: Se implementa la interfaz "Serializable" como requisito para
public class Palabra implements Serializable {
String texto;
int frecuencia;
public Palabra(String texto, int frecuencia) {
this.texto = texto;
this.frecuencia = frecuencia;
}
public int getFrecuencia() {
return frecuencia;
}
public void setFrecuencia(int frecuencia) {
this.frecuencia = frecuencia;
}
public String getTexto() {
return texto;
}
public void setTexto(String texto) {
this.texto = texto;
}
}

que el objeto pueda transferirse entre los nodos.

ComparadorPalabras.java
import java.util.Comparator;
/* Comparar dos objetos "Palabra" para determinar su posicin
* dentro una lista en base a la frecuencia en que se presenta en un texto seleccionado al azar.
*/
public class ComparadorPalabras implements Comparator {
public int compare(Object o1, Object o2) {
Palabra p2 = (Palabra) o1;
Palabra p1 = (Palabra) o2;
Integer fr1 = p1.getFrecuencia();
Integer fr2 = p2.getFrecuencia();
//return p1.getFrecuencia().compareTo( p2.getFrecuencia() );
return fr1.compareTo(fr2);
}
@Override
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
public boolean equals(Object o) {
return this == o;
}
@Override
public int hashCode() {
int hash = 7;
return hash;
}
}

ibis.properties
ibis.server.address = localhost
ibis.pool.name = dante
ibis.pool.size = 2