Vous êtes sur la page 1sur 27

Universidad de Oriente

Ncleo de Sucre
Escuela de Ciencias
Departamento de Matemticas
Programa de la Licenciatura En Informtica

PARALELIZACION DEL ALGORITMO DE SUBSECUENCIA COMN


MS LARGA CON MPI-C.

Tutor:
Prof. Ana Fuentes
Bachiller:
Jess Alejandro Machado Ortiz
C.I: 24877491

Cuman, enero del 2014


INTRODUCCIN

La eficiencia de un computador, depende directamente del


tiempo requerido para poder ejecutar una instruccin bsica y del
nmero
de
instrucciones
que
pueden
ser
ejecutadas
concurrentemente. Esta eficiencia puede ser incrementada por
avances en la arquitectura y por avances tecnolgicos. Un caso de
esto es la nueva gama de procesadores de INTEL y AMD. Estos
avances en la arquitectura incrementa la cantidad de trabajo que se
puede realizar por el manejo de instrucciones, especficamente por su
capacidad de ciclo de cada procesador. Gracias a las memorias cache,
memorias paralelas, canales, memoria intercalada y procesadores
paralelos.
Pero a pesar de estos grandes avances, hay un problema
primordial a atacar, el cual es mejorar la eficiencia de nuestros
programas. Es decir, se debe cambiar la forma en la cual se nos ha
enseado la programacin. Ya debera considerarse la programacin
paralela como una rama primordial y activa en la incursin de nuevos
programadores. Aprovechar al mximo las caractersticas hardware
presente, realizar clster, mandar instrucciones a las tarjetas de video
a travs de CUDA para optimizar un proceso matemtico. Solo as
podramos considerar una pregunta, que es tema de debate en
muchas oportunidades: Algn da el software alcanzar al mximo al
hardware? Lo pasar de largo? Hay un lmite en alguno de ellos?
La aplicacin que se tratar en este proyecto de desarrollo,
consiste en una mezcla de estos tres tipos de aplicaciones (de gran
procesamiento de clculo, de entrada y salida de datos y de
comunicacin), como se ver ms adelante. Se ir indagando con la
forma ms factible de implementarla y ejecutarla satisfactoriamente.
Esto con el fin de solucionar una problemtica en dicha rea de
estudio, como lo es Los algoritmo de Manejo de Caracteres.
Antes de continuar, cabe a resaltar Qu es MPI? Interfaz de
Pase de Mensaje, segn la [wikipedia] es un estndar de funciones
contenidas en una biblioteca de paso de mensaje diseada con el fin
de facilitar la explotacin de mltiples procesadores. Y el paso de
mensaje en s, es una tcnica empleada en la programacin
concurrente para aportar sincronizacin entre procesos y permitir la
exclusin mutua (para saber ms de este tema, indague la temtica
de gestin de procesos y concurrencia) de manera similar a como lo
hacen los semforos, monitores entre otros.

Planteamiento

La informacin se representa normalmente a travs de


palabras, que est compuesta por smbolos o caracteres. Que vienen
a representar un lenguaje (espaol, ingls, alemn, entre otros),
permitindonos entender y comprender a travs de un proceso
cognoscitivo el mensaje. Observando esta analoga, se afirma que
las computadoras realizan el mismo proceso, reciben a travs de una
entrada, procesan y emiten una respuesta. Esto da pie al siguiente
algoritmo, el cual analiza cadenas de caracteres para obtener
informacin relevante para un propsito especfico.
En el caso que se estudiar en este trabajo, el cual deber
procesar secuencias de smbolos o caracteres, que representarn
una informacin y darn como respuesta La Subsecuencia Comn
Ms Larga. Ahora, Qu ser la Subsecuencia Comn ms Larga?
Son una forma natural para representar informacin.
Actualmente existen muchos problemas relacionados con las
cadenas de caracteres, pero el que ms frecuencia se presenta es el
de comparacin de secuencias. Como ejemplo, en el anlisis de
secuencias biolgicas la similitud de secuencias es uno de los
conceptos de mayor importancia, principalmente por el hecho de
observar las posibles relaciones de evolucin compartidas por los
organismos. Esta puede ser expresada como un porcentaje de
identidad til para determinar las homologas entre secuencias, la
cual puede ser, por ejemplo, precisar si comparten un mismo
ancestro (recordando a Charles Darwin). Este porcentaje de identidad
es una tasa entre el nmero de columnas con caracteres idnticos
encontrados en un alineamiento y el nmero de smbolos de la
secuencia ms larga.
Definicin Formal:
Navarro, Gonzalo expresa en su libro A Guided Tour To
Aproximate String Matching. ACM Computing Surveys, (2001) pp. 31
-88. Dice:
En ingls Longest Common Subsequence (LCS) Sean las
secuencias x y y que pertenecen a , y con una longitud M y N (M
>= N) respectivamente, el LCS de la secuencias x y y consiste en
encontrar una Subsecuencia de x y de y de mxima longitud posible.
Se deduce entonces como el LCS(x,y) es una Subsecuencia de x
, y e igualmente, como x y y son supersecuencias de LCS(x,y). El LCS
no permite la operacin de sustitucin, nicamente las operaciones

de insercin y eliminacin.

Un ejemplo grafico de una LCS.

Como se puede observar los comunes son los que se encuentran en ambas
cadenas, esto no es al azar, hay un mtodo mediante el cual se obtiene la LCS.
Para el llenado de esta tabla, nos basamos en las siguientes premisas:

E
M
A
C
H
A
D
O

E
0
0
0
0
0
0
0
0

M
0
1
1
1
1
1
1
1

A
0
1
2
2
2
2
2
2

N
0
1
2
2
2
2
2
2

C
0
1
2
3
3
3
3
3

H
0
1
2
3
4
4
4
4

Y escribimos la palabra de arriba hacia abajo quedandonos:


MACHA. Los marcados en rojo son los puntos dominantes para cada seccin.

A
0
1
2
3
4
5
5
5

ALGORITMO SECUENCIAL
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

Procesamiento por Filas.

/*
* Definimos dos funciones para buscar el maximo de dos numero
* y el minimo de dos numeros tambien.
*/
#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
#define MIN(x,y) (((x) <= (y)) ? (x) : (y))
/*
* @parametro char * x, primer string recibido por archivos.
* @parametro char * y, segundo string recibido por archivos.
* @parametro unsigned short int **c, matriz LCS.
*/
void LCS_procesar(char *x, char *y,unsigned short int **c);
/*
* @parametro unsigned shor int **c, la matriz LCS.
* @parametro char *x, primer string recibido por archivos.
* @parametro int i, indice de las filas de la matriz.
* @parametro int j, indice de las columnas de la matriz.
*/
void LCS_mostrar(unsigned short int **c,char *x, int i, int j);
/*
* @parametro char *infile, nombre del archivo al cual leera el string
*/
char* LCS_leer(char *infile);
//Se tiene argc, argv como puntos de entrada de datos externos al
main,
//Que en este codigo, se usan para pasar el nombre de los
archivos(flujos,
//de entrada).
int main(int argc, char** argv){
//si argc es diferente de 3, esto quiere decir que no se coloco
//el flujo de entrada tanto para string 1 y string 2.
//una validacion previa a la ejecucion.
if(argc != 3){
printf("Porfavor, use 2 archivos de lectura");
exit(0);
}
//1 - leemos los archivos, creamos los string y se lo asignamos a
//nuestras variables.

char *X = LCS_leer(argv[1]);
char *Y = LCS_leer(argv[2]);
//Tomando el tamao de X y Y, para porteriormente crear la
matriz
int len_X = (int) strlen(X);
int len_Y = (int) strlen(Y);
//En esta validacion, hacemos un posible estimado de que tan
grande
//podria ser nuestra entrada, y segun los requisitos de nuestra
maquina
//restringir un tamao en especifico.
if(len_X + len_Y +len_X*8 + (len_X*len_Y)*2 > 2147483648){
printf("Ocurrio un error de desbordamiento de datos");
return 0;
}
//Declaramos la matriz de la forma **c, para que sea dinmica.
//Permitiendo manejadar filas y columnas de tamao variable.
unsigned short int **c;
//definimos la fila
int fila;
//Asignamos memoria dinamicamente, que sera igual a [tam(x)
+1]
c = malloc(sizeof(unsigned short int*)*(len_X+1));
for(fila = 0; fila < len_X+1; fila++){
//Aqui por asignamos [tam(x) + 1][tam(y) +1] -> por
eso el ciclo,
//a cada [tam(x) + 1][0...tam(y)+1]
c[fila] = malloc(sizeof(unsigned short
int)*(len_Y+1));
}
LCS_procesar(X,Y,c);
LCS_mostrar(c,X,len_X,len_Y);
printf("\n\n");
printf("El tamano de la subsecuencia comun mas larga es:
%d\n",c[len_X][len_Y]);
for(fila = 0; fila < len_X+1; fila++){
free(c[fila]);
}
free(c);
free(X);
free(Y);
return 0;
}
//Leer un archivo que contiene cadena de caracteres,
//se ignoraran nuevas lineas y espacios.
char* LCS_leer(char *infile){
FILE *FIN;
char c = 'n';

char *S = NULL;
char *T;
int count = 0;
if((FIN = fopen(infile,"r"))==NULL){
printf("Ocurrio un problema abriendo el archivo: %s\n",infile);
exit(0);
}
while(( c = fgetc(FIN))!= EOF){
if ( c != '\n'){
count++;
//se ensancha, una forma de decirlo la cadena de caracteres
dinamicamente.
T = (char *) realloc((void *) S, (count+1)*sizeof(char));
if(T != NULL){
S = T;
S[count-1] = c;
S[count]='\0';
}
else{
free(S);
printf("Error de (re) asignacion de memoria para guardar la
cadena en %s\n",infile);
exit(1);
}
}
}
fclose(FIN);
return S;
}
void LCS_procesar(char *x, char *y, unsigned short int **c){
int fila, columna;
int len_X = (int) strlen(x);
int len_Y = (int) strlen(y);
//Hacemos 0's la primera columna y la primera fila
for(fila = 0; fila <= len_X; fila++){
c[fila][0] = 0;
}
for(columna = 1; columna <= len_Y; columna++){
c[0][columna] = 0;
}
for(fila = 1; fila <= len_X; fila++){
for(columna = 1; columna <= len_Y; columna++){
//Rellenar c[fila][columna] con el valor apropiado
if(x[fila-1] == y[columna-1]){
c[fila][columna] = c[fila-1][columna-1]+1;

}
else{
c[fila][columna] = MAX(c[fila-1][columna],c[fila][columna1]);
}
}
}
return;
}
void LCS_mostrar(unsigned short int **c,char *x,int i, int j){
if(i == 0 || j == 0)
return;
if((c[i][j] - 1 == c[i-1][j - 1]) &&
(c[i][j] - 1 == c[i-1][j ]) &&
(c[i][j] - 1 == c[i ][j - 1])){
LCS_mostrar(c,x,i-1,j-1);
printf("%c",x[i-1]);
}
else
if(c[i][j] == c[i-1][j])
LCS_mostrar(c,x,i-1,j);
else
LCS_mostrar(c,x,i,j-1);
return;
}
Este cdigo fue escrito y probado en un entorno Linux Ubuntu
14.04, teniendo como compilador base GCC. Con el IDE CODEBLOCKS,
y se deber usar la siguientes lneas para su ejecucin.
-

gcc LCS.c o LCS


./LCS file1.txt file2.txt

Si no dispone de este mtodo, coloque directamente en el


cdigo las rutas de los archivos. Ambos archivos son de lectura
(de entrada) y se muestra la respuesta por la pantalla.

PERFILADO DEL PROGRAMA (profiling)


Perfil Plano:
Cada muestra cuenta con 0.01 segundos.
%
Tiemp
o

Acumulad
o
Segundos Se
g

Llamad
as

100

2.49

Llamada
s
por
segundo
2.49

0.00

2.49

0.00

Llamada Nombre
s
por
segundo
2.49
LCS_proce
sar
0.00
LCS_leer

0.00

2.49

0.00

0.00

2.4
9
0.0
0
0.0
0

Total

LCS_mostr
ar

Leyenda:
-

%, el porcentaje de la duracin total del programa utilizado


por esta funcin.
Acumulado, suma de los nmeros de segundos
representados por esta funcin y los enumerados superior a
ella.
Segundos, el numero de segundos representados por esta
funcin solamente. Esto es una especie de listado
importante.
Llamadas, el nmero de veces que esta funcin fue
invocada, si esta funcin no fue invocada, no se perfila y
queda en blanco.
Tiempo / Llamadas, El nmero medio de milisegundos en
esta funcin llamada por llamada, s esta funcin se perfila.
Total, el nmero medio de milisegundos empleados en esta
funcin de llamada y sus descendientes por llamada.
Nombre, el nombre de la funcin. Este es el tem de menor
importancia para este listado. El ndice muestra la ubicacin
de la funcin en el listado gprof.

Grafo de Llamadas:
Granularidad: Para cada muestra abarca 2 byte(s) para 0.40% de 2.49
segundos.
ndice

[1]

100.0

[2]

100.0

Tiempo
2.49
2.49

Hijos(propagacin)
0.00
0.00

Llamadas
1/1
1

0.00
2.49

2.49
0.00

1/1

Nombre
Main [2]
LCS_procesar [1]
<espontaneo>
Main [2]
LCS_procesar [1]

0.00
0.00

0.00
0.00

2/2
1/1

LCS_leer [3]
LCS_mostrar [4]

0.00
0.00

0.00
0.00

0.00
0.00

0.00
0.00

2/2
2
13537
1/1
1 + 13537
13537

Main [2]
LCS_leer [3]
LCS_mostrar [4]
Main [2]
LCS_mostrar [4]
LCS_mostrar

[1]
[4]
[3]

0.0

[4]
[4]

Esta tabla describe el rbol de llamadas del programa, y fue


ordenada por la cantidad total de tiempo invertido en cada funcin y
sus hijos.
Cada entrada en esta tabla se compone de varias lneas. La
lnea con el nmero de ndice en el margen izquierdo enumera la
funcin actual. Las lneas anteriores se enumeran las funciones que
llaman a esta funcin, y las lneas de abajo se enumeran las funciones
de este llamado. Estas lneas se listan:
-

ndice, un nmero nico asignado a cada elemento de la


tabla. Los nmeros estn ordenados numricamente por los
ndices. El nmero de ndice se imprime al lado de cada

nombre de la funcin de modo que sea ms fcil mirar hacia


arriba cuando la funcin est en la tabla.
Tiempo, este es el tiempo total que se gast en esta funcin
y sus hijos, tenga en cuenta que debido a los diferentes
punto de vista, las funciones excluidas no se suman al 100%.
Hijos, esta es la cantidad total de tiempo que se propaga en
esta funcin por sus hijos.
Llamadas, este es el nmero de veces que la funcin sea
llamada. Si la funcin se llam de forma recursiva, y es
seguido por un + y el nmero de llamadas recursivas.
Nombre, el nombre de la funcin actual. El nmero de
clasificacin es impreso despus de ella. Esta funcin es un
miembro de un ciclo, el ciclo numrico es impreso entre los
nombres de las funciones y el ndice.

Para los padres de las funciones, los campos tienen los siguientes
significados:
-

Esta es la cantidad de tiempo que se propaga


directamente de la funcin padre.
Hijos, esta es la cantidad de tiempo que se propaga a
partir de los hijos de la funcin en este padre.
Llamadas, este es el nmero de veces que este padre
llama a la funcin, cabe resaltar que el nmero total de
veces que la funcin fue llamada. Las llamadas
recursivas a la funcin no son incluido en el nmero
despus de la /.
Nombre, este es el nombre del padre, ndice de los
padres estar impreso despus de ella. Si el padre es
un miembro de un ciclo, el nmero de ciclo se imprime
entre el nombre y el nmero de ndice.

Si los padres de la funcin no se pueden determinar, la palabra


espontnea se imprime en el nombre del campo, y todos los dems
campos estarn en blanco.
Para los hijos de las funciones, los campos tienen los siguientes
significados:
-

Hijos (propagacin), Esta es la cantidad de tiempo que


se propaga directamente desde el hijo desde la
funcin.
Esto se llama el nmero de veces que la funcin llama
a este hijo, el nmero total de veces que el hijo fue
llamado. Las llamadas recursivas por el hijo no son
enumerados en el nmero despus de /.

Nombre, este es el nombre del hijo. ndice del hijo est


impreso despus del nmero de ella. Si el nio es un
miembro de un ciclo, el nmero de ciclo se imprime
entre el nombre y el nmero de ndice.

Si hay algn ciclo en el grfico de llamadas, hay una entrada


para el ciclo como un todo. Esta entrada muestra que llam al ciclo
(como su padre) y los miembros del ciclo (como los hijos). La lnea +
llamadas recursivas muestra el nmero de llamadas a funciones que
eran interna para el ciclo, y la entrada de llamadas para cada
miembro de muestra, para ese miembro, cuntas veces se le llam
de otros miembro del ciclo?
ndice del nombre de las funciones: [3] LCS_leer - [4] LCS_mostrar [1] LCS_procesar
Se pudo observar que en el siguiente anlisis de perfilado la
funcin LCS_procesar, consumi gran cantidad de recursos, por ende
es muy factible para realizarse su paralelizacin. En un tiempo de
2.49 s, para el caso de prueba de este ejercicio, se conto con 8665 y
8504 caracteres por cadena respectivamente, generando una matriz
de 7.3704.330 posiciones. Y con un tiempo de acceso cuadrtico O (
N 2 ).
Otro detalle a resaltar es la cantidad de veces que se llam a la
funcin LCS_mostrar, con una cantidad de sub-llamadas a ella misma,
de 13537 veces. La explicacin de por qu ocurre este fenmeno est
en que es una funcin recursiva. Por ello se obtiene este valor, una
implementacin ms trivial causara ms espacio en memoria y un
rendimiento de recorrido un poco mejor. Pero en este caso se escogi
sacrificar rendimiento, por utilizacin de espacio. Ya que se pretende
a posteriori realizar una implementacin paralela de dicho algoritmo.

ALGORITMO SECUENCIAL
Procesamiento Diagonal.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
void LCS_procesar(char *x, char *y, unsigned short int **c);
void LCS_mostrar(unsigned short int **c, char *x, int i, int j);
char* LCS_leer(char *infile);
int main(int argc, char** argv)
{
if( argc != 3 )
{
printf("Porfavor use 2 archivos de lectura ");
exit(0);
}
char *X = LCS_leer(argv[1]);
char *Y = LCS_leer(argv[2]);
int len_X = (int) strlen(X);
int len_Y = (int) strlen(Y);
if( len_X+len_Y + len_X*8 + (len_X*len_Y)*2 > 4294967296 /* 4*2^30

*/ )

printf("Desbordamiento de la memoria. Limitado solo a 4gb de


ram.\n");
return 0;
}
unsigned short int **c;
int fila;
c = malloc(sizeof(unsigned short int*) * (fila+1));
for( fila = 0; fila < fila+1; fila++)
c[fila] = malloc(sizeof(unsigned short int)*(len_Y+1));
LCS_procesar(X, Y, c);
LCS_mostrar(c, X, len_X, len_Y);
printf("\n\n");
printf("El tamano del LCS: %d\n", c[len_X][len_Y]);
/* Liberando memoria :D C siempre haciendolo todo dificil */
for( fila= 0; fila < len_X+1; fila++)
free(c[fila]);
free(c);
free(X);
free(Y);
return 0;
}
char* LCS_leer(char *infile)
{
FILE *FIN;
char c = 'n';
char *S = NULL;
char *T;
int count=0;
if( (FIN = fopen(infile, "r")) == NULL )

{
printf("Problema de apertura con el archivo %s.\n", infile);
exit(0);
}

while ( (c=fgetc(FIN)) != EOF )


{
if( c != '\n' )
{
count++;
T = (char *) realloc((void *) S, (count+1)*sizeof(char));
if( T != NULL)
{
S = T;
S[count-1] = c;
S[count] = '\0';
}
else
{
free(S);
printf("Error (re)asignando memoria %s\n", infile);
exit(1);
}
}

fclose(FIN);
return S;
}

PERFILADO DEL PROGRAMA (profiling)


Cada muestra cuenta con 0.01 segundos.
%

Acumulad
o
Seg
Seg

Tiemp
o
100

13.75

0.00
0.00

13.75
13.75

13.7
5
0.00
0.00

Total
Llama
das
1

Llamadas
por
segundo
13.75

Llamadas
por
segundo
13.75

2
1

0.00
0.00

0.00
0.00

Nombr
e
LCS_pro
cesar
LCS_leer
LCS_mo
strar

%, el porcentaje de la duracin total del programa utilizado


por esta funcin.
Acumulado, suma de los nmeros de segundos
representados por esta funcin y los enumerados superior a
ella.
Segundos, el numero de segundos representados por esta
funcin solamente. Esto es una especie de listado
importante.
Llamadas, el nmero de veces que esta funcin fue
invocada, si esta funcin no fue invocada, no se perfila y
queda en blanco.
Tiempo / Llamadas, El nmero medio de milisegundos en
esta funcin llamada por llamada, s esta funcin se perfila.
Total, el nmero medio de milisegundos empleados en esta
funcin de llamada y sus descendientes por llamada.
Nombre, el nombre de la funcin. Este es el tem de menor
importancia para este listado. El ndice muestra la ubicacin
de la funcin en el listado gprof.

Grafo de Llamadas:
Granularidad: Para cada muestra abarca 2 byte(s) para 0.07% de
13.75 segundos.
ndice

[1]

100.0

[2]

100.0

Tiempo
13.75
13.75

Hijos(propagacin)
0.00
0.00

Llamadas
1/1
1

0.00
13.75

13.75
0.00

1/1

Nombre
Main [2]
LCS_procesar [1]
<espontaneo>
Main [2]
LCS_procesar [1]

0.00
0.00

0.00
0.00

2/2
1/1

LCS_leer [3]
LCS_mostrar [4]

0.00
0.00

0.00
0.00

0.00
0.00

0.00
0.00

2/2
2
13537
1/1
1 + 13537
13537

Main [2]
LCS_leer [3]
LCS_mostrar [4]
Main [2]
LCS_mostrar [4]
LCS_mostrar

[1]
[4]
[3]

0.0

[4]
[4]

Esta tabla describe el rbol de llamadas del programa, y fue


ordenada por la cantidad total de tiempo invertido en cada funcin y
sus hijos.
Cada entrada en esta tabla se compone de varias lneas. La
lnea con el nmero de ndice en el margen izquierdo enumera la
funcin actual. Las lneas anteriores se enumeran las funciones que
llaman a esta funcin, y las lneas de abajo se enumeran las funciones
de este llamado. Estas lneas se listan:
-

ndice, un nmero nico asignado a cada elemento de la


tabla. Los nmeros estn ordenados numricamente por los
ndices. El nmero de ndice se imprime al lado de cada
nombre de la funcin de modo que sea ms fcil mirar hacia
arriba cuando la funcin est en la tabla.
Tiempo, este es el tiempo total que se gast en esta funcin
y sus hijos, tenga en cuenta que debido a los diferentes
punto de vista, las funciones excluidas no se suman al 100%.
Hijos, esta es la cantidad total de tiempo que se propaga en
esta funcin por sus hijos.
Llamadas, este es el nmero de veces que la funcin sea
llamada. Si la funcin se llam de forma recursiva, y es
seguido por un + y el nmero de llamadas recursivas.
Nombre, el nombre de la funcin actual. El nmero de
clasificacin es impreso despus de ella. Esta funcin es un

miembro de un ciclo, el ciclo numrico es impreso entre los


nombres de las funciones y el ndice.

Para los padres de las funciones, los campos tienen los siguientes
significados:
-

Esta es la cantidad de tiempo que se propaga


directamente de la funcin padre.
Hijos, esta es la cantidad de tiempo que se propaga a
partir de los hijos de la funcin en este padre.
Llamadas, este es el nmero de veces que este padre
llama a la funcin, cabe resaltar que el nmero total de
veces que la funcin fue llamada. Las llamadas
recursivas a la funcin no son incluido en el nmero
despus de la /.
Nombre, este es el nombre del padre, ndice de los
padres estar impreso despus de ella. Si el padre es
un miembro de un ciclo, el nmero de ciclo se imprime
entre el nombre y el nmero de ndice.

Si los padres de la funcin no se pueden determinar, la palabra


espontnea se imprime en el nombre del campo, y todos los dems
campos estarn en blanco.
Para los hijos de las funciones, los campos tienen los siguientes
significados:
-

Hijos (propagacin), Esta es la cantidad de tiempo que


se propaga directamente desde el hijo desde la
funcin.
Esto se llama el nmero de veces que la funcin llama
a este hijo, el nmero total de veces que el hijo fue
llamado. Las llamadas recursivas por el hijo no son
enumerados en el nmero despus de /.
Nombre, este es el nombre del hijo. ndice del hijo est
impreso despus del nmero de ella. Si el nio es un
miembro de un ciclo, el nmero de ciclo se imprime
entre el nombre y el nmero de ndice.

GRAFO DE DEPENDENCIAS
Grafo de dependencias del MAIN

int main(int argc, char** argv)


{
C1: if( argc != 3 )
{
I1: printf("Porfavor use 2 archivos de lectura ");
I2: exit(0);
}
I3: char *X = LCS_leer(argv[1]);
I4: char *Y = LCS_leer(argv[2]);
I5: int len_X = (int) strlen(X);
I6: int len_Y = (int) strlen(Y);
C2: if( len_X+len_Y + len_X*8 + (len_X*len_Y)*2 > 4294967296 /* 4*2^30 */ )
{
I:7 printf("Desbordamiento de la memoria. Limitado solo a 4gb de ram.\n");
I:8 return 0;
}
I9: unsigned short int **c;
I10: int fila;
I11: c = malloc(sizeof(unsigned short int*) * (len_X+1));
I:12 for( fila = 0; fila < len_X+1; fila++)
I13: c[fila] = malloc(sizeof(unsigned short int)*(len_Y+1));
F1: LCS_procesar(X, Y, c);
F2: LCS_mostrar(c, X, len_X, len_Y);
I14: printf("\n\n");
I15: printf("El tamano del LCS: %d\n", c[len_X][len_Y]);
/* Liberando memoria :D C siempre haciendolo todo dificil */
I16: for( fila= 0; fila < len_X+1; fila++)
I17: free(c[fila]);
I18: free(c);
I19: free(X);
I20: free(Y);
I21: return 0;

Grafo de Dependencia LCS_leer

char* LCS_leer(char *infile)


{
I1: FILE *FIN;
I2: char c = 'n';
I3: char *S = NULL;
I4: char *T;
I5: int count=0;
C1: if( (FIN = fopen(infile, "r")) == NULL )
{
I6: printf("Problema de apertura %s.\n", infile);
I7: exit(0);
}
I8: while ( (c=fgetc(FIN)) != EOF )
{
C2: if( c != '\n' )
{
I9: count++;
I10: T = (char *)realloc((void *)S, (count+1)*sizeof(char));
C3: if( T != NULL)
{
I11: S = T;
I12: S[count-1] = c;
I13: S[count] = '\0';
}
C4: else
{
I14: free(S);
I15: printf("Error (re)asignando %s\n", infile);
I16: exit(1);
}
}
}
I17: fclose(FIN);
I18: return S;}

Grafo de Dependencia LCS_procesar

void LCS_procesar(char *x, char *y, unsigned short int **c)


{
I1: int fila;
I2: int columna;
I3: int diagonal;
I4: int len_X = (int) strlen(x);
I5: int len_Y = (int) strlen(y);
I6: for( fila = 0; fila <= len_X; fila++ )
I7: c[fila][0] = 0;
I8: for( columna = 1; columna<= len_Y; columna++ )
I9: c[0][columna] = 0;
I10: for(diagonal = 2; diagonal <= len_X+len_Y; diagonal++){
I11: for(fila = MIN(len_X, diagonal-1); fila >= MAX(1, diagonallen_Y);fila--){
I12: columna = diagonal - fila;
C1: if(x[fila-1] == y[columna-1]) {
I13: c[fila][columna] = c[fila-1][columna-1] + 1;
}
C2: else {
I14: c[fila][columna] = MAX(c[fila-1][columna],c[fila][columna-1]);
}
}}
I15 return;}

Grafo de Dependencia LCS_mostrar

void LCS_mostrar(unsigned short int **c, char *x, int i, int j){
c1: if( i == 0 || j == 0 )
I1: return;
&&

C2: if( (c[i][j]-1 == c[i-1][j-1]) &&(c[i][j]-1 == c[i-1][j


(c[i][j]-1 == c[i ][j-1]) {
I2: LCS_mostrar(c, x, i-1, j-1);
I3: printf("%c", x[i-1]);
}
C3: else if( c[i][j] == c[i-1][j] )
I4: LCS_mostrar(c, x, i-1, j);
C4: else
I5: LCS_mostrar(c, x, i, j-1);

C5: return;

])

ALGORITMO PARALELO
#include <mpi.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
//Estructura que contendr la informacin y argumentos para el LCS
typedef struct LCS
{
char *x; //string x
char *y; //string y
short int **c; //Matriz
int
m; // tamao de filas
int
n; //tamao de columnas
}LCS_T;
MPI_Status status; //el status, sirve para comunicacin directas e
indirectas.
void LCS_procesar(LCS_T *, int, int); //Arma la matriz del LCS
char* LCS_leer(char *);//Lee de un archivo el flujo de datos
void distribuir_datos(LCS_T *, char *, int, int);//Se aplica la
distribucin de datos a los procesadores.
void LCS_mostrar(short int **c,char *x, int i, int j); // Se
muestra el LCS.
int main(int argc, char** argv)
{
//Cuando se trabaja con MPI,
//es necesario tener identificadores por proceso.
int totalprocesos, miproc;
LCS_T LCS_info; //Meta Contenedor Global.
char* string1;
MPI_Init(&argc, &argv); //Iniciamos el entorno MPI
MPI_Comm_size(MPI_COMM_WORLD, &totalprocesos); //obtenemos el
numero de procesos
MPI_Comm_rank(MPI_COMM_WORLD, &miproc); //obtenemos el
identificador nico de ese proceso
if( miproc == 0 ) { //Se tomara al proceso 0, como al proceso
raz.
if( argc != 3 )
{
printf("Utilizar archivos externos para cargar los
datos.\n");
printf("Ejemplo: %s file1 file2\n", argv[0]);
MPI_Abort(MPI_COMM_WORLD,0); //Se aborta el entorno MPI;
}
//se lee desde el archivo
string1
= LCS_leer(argv[1]);
LCS_info.y = LCS_leer(argv[2]);
//se obtiene el tamano de filas y columnas
LCS_info.m = (int) strlen(string1);
LCS_info.n = (int) strlen(LCS_info.y);
}
//Se usara Broadcast para y & Scatter para x
distribuir_datos(&LCS_info, string1, miproc, totalprocesos);
//Crear e inicializar las matrices.
int i;
LCS_info.c = malloc(sizeof(int*)*LCS_info.m+1);
for(i = 0; i <= LCS_info.m; i++) {
LCS_info.c[i] = malloc(sizeof(int)*LCS_info.n+1);
LCS_info.c[i][0] = 0; // La primera columna es 0

}
if (miproc == 0) {
for (i = 0; i <= LCS_info.n; i++)
LCS_info.c[0][i] = 0; // La primera fila es 0
}
LCS_procesar( &LCS_info, miproc, totalprocesos );
if( miproc == totalprocesos - 1 )
printf("Length of LCS: %d\n", LCS_info.c[LCS_info.m]
[LCS_info.n]);
if( miproc == 0 ) {
LCS_mostrar(LCS_info.c, LCS_info.x, LCS_info.m, LCS_info.n);
printf("\n\n");
printf("Numero de Procesos: %d\n", totalprocesos);

}
MPI_Finalize();
return 0;
}

char* LCS_leer(char *infile) {


FILE *FIN;
char c = 'n';
char *S = NULL;
char *T;
int count=0;
int buffsize=0;
int totalprocesos;
int i;
MPI_Comm_size(MPI_COMM_WORLD, &totalprocesos);
if( (FIN = fopen(infile, "r")) == NULL ) {
printf("Problemas de apertura al archivo: %s.\n", infile);
MPI_Abort(MPI_COMM_WORLD,0);
}
while ( (c=fgetc(FIN)) != EOF ) {
if( c != '\n' ) { /* Ignore new lines & EOF */
count++;
if( count >= buffsize ){
buffsize += totalprocesos;
T = (char *) realloc((void *) S,
buffsize*sizeof(char));
if( T != NULL) {
S = T;
memset(&S[buffsize-totalprocesos], '\0',
totalprocesos);
}
else {
free(S);
printf("Error ( re) asignacin de memoria para
almacenar en cadena %s\n", infile);
MPI_Abort(MPI_COMM_WORLD,0);
}
}
S[count-1] = c;
}
}
fclose(FIN);
return S;
}

/******************************************************************
* Distribuir los datos necesarios a los procesos
*
******************************************************************/
void distribuir_datos(LCS_T *LCS_info, char* string1, int miproc, int
totalprocesos ) {
//Se envia el tamao de columnas a todos los procesadores.
MPI_Bcast( &LCS_info->n, 1, MPI_INT, 0, MPI_COMM_WORLD);
//si es el procesador principal, asignamos dinamicamente el tamano
de y igual al tamano de filas.
if( miproc!= 0 )
LCS_info->y = malloc(LCS_info->n*sizeof(char));
//Pasamos y a todos los procesadores
MPI_Bcast( LCS_info->y, LCS_info->n, MPI_CHAR, 0, MPI_COMM_WORLD
);
//Pasamos las filas a los procesadores.
MPI_Bcast( &LCS_info->m, 1, MPI_INT, 0, MPI_COMM_WORLD);
//Hacemos el balanceo de carga
int intervalo = (totalprocesos + LCS_info->m - LCS_info->m %
totalprocesos) / totalprocesos;
LCS_info->x = malloc((intervalo+1)*sizeof(char));
bzero(LCS_info->x, intervalo+1); //establece los primeros n bytes
del area a patir de de s a 0.
//Distribuir el mismo numero de elementos a cada procesador, se
requiere que sea intervalo divisible por el total de procesos.
MPI_Scatter( string1, intervalo, MPI_CHAR, LCS_info->x, intervalo,
MPI_CHAR, 0, MPI_COMM_WORLD);
//Se toma el nuevo tamao de filas.
LCS_info->m = (int) strlen(LCS_info->x);
return;
}
void LCS_diagonal(int k, LCS_T *LCS_info)
{
//El core bastante igual al secuencial, solo que aqui el trabajo
se realizara a traves de diagonales.
//Para trabajar mejor los bloques.
int i,j;
int start = MAX(1,k-LCS_info->n);
int finish = MIN(LCS_info->m,k-1);
for( i = start; i <= finish; i++ )
{
j = k - i;
if(LCS_info->x[i-1] == LCS_info->y[j-1])
LCS_info->c[i][j] = LCS_info->c[i-1][j-1] + 1;
else
{
if( LCS_info->c[i-1][j] >= LCS_info->c[i][j-1] )
LCS_info->c[i][j] = LCS_info->c[i-1][j];
else
LCS_info->c[i][j] = LCS_info->c[i][j-1];
}
}
}
void LCS_procesar(LCS_T *LCS_info, int miproc, int totalprocesos )
{
int diag, col;
//
printf("N: %d\n", LCS_info->n);
/* For each diagonal */
for(diag = 2; diag <= LCS_info->m+LCS_info->n; diag++)
{
/* Receive value of first row to myid+1 */

if (miproc != 0 && diag-1 <= LCS_info->n ) {


MPI_Recv(&LCS_info->c[0][diag-1], 1, MPI_INT, miproc-1, 0,
MPI_COMM_WORLD,&status);
}
/* Populate diagonal k of process's c matrix */
LCS_diagonal(diag, LCS_info);
/* Send value last row to myid-1 */
if (miproc!= totalprocesos-1 && diag > LCS_info->m ) {
col = diag - LCS_info->m;
MPI_Send(&LCS_info->c[LCS_info->m][col], 1, MPI_INT,
miproc+1, 0, MPI_COMM_WORLD);
//
printf("Process %d sent c[%d][%d] = %d\n", myid,
LCS_info->m, col, LCS_info->c[LCS_info->m][col]);
}
}
return;
}
void LCS_mostrar(short int **c,char *x,int i, int j){
if(i == 0 || j == 0)
return;
if((c[i][j] - 1 == c[i-1][j - 1]) &&
(c[i][j] - 1 == c[i-1][j
]) &&
(c[i][j] - 1 == c[i ][j - 1])){
LCS_mostrar(c,x,i-1,j-1);
printf("%c",x[i-1]);
}
else
if(c[i][j] == c[i-1][j])
LCS_mostrar(c,x,i-1,j);
else
LCS_mostrar(c,x,i,j-1);
return;
}

CONCLUSIONES
Durante la ejecucin de este proyecto, se observo con gran
agrado las potencialidades que tiene este nuevo paradigma de la
multiprogramacin. En donde una gran responsabilidad de las cosas
recae en los programadores, como lo es gestionar los datos,
analizando sus dependencias, observar su congruencia, estudiar las
distintas topologas fsicas que permiten la comunicacin de los
procesos. Esto con el fin de optimizar el cdigo y aprovechar en su
totalidad las nuevas caractersticas hardware.
El LCS Longest Common Substring Subsecuencia comn
ms larga es de los problemas en donde se necesita un gran estudio
de sus dependencias y del comportamiento secuencial del mismo. Ya
que como se observo en el cdigo descrito con anterioridad, el
procesamiento era complicado resolverlo a travs de sus filas, puesto
que la dependencia imposibilitaba en gran medida la reparticin
equitativa de las tareas. Por esto se opto a una nueva forma,
procesando sus diagonales. Aqu se minimiz en gran parte esta
dependencia, a sacrificio de un poco de tiempo. De 2.47 segundos
aproximadamente en el secuencial por filas, llegamos a 13s
aproximadamente en el secuencial por diagonales.
A la hora de paralelizarlo, basndome en el grafo de
dependencias. Concluimos que era factible y se llevo a cabo dicha
labor. Se escogi la funcin LCS_procesar para la realizacin de la
paralizacin, ya que LCS_mostrar se dificultaba su paralelizacin. Se
obtuvo un tiempo relativo de 9.30 seg. Un mejoramiento del 28% de
rendimiento respecto al diagonal secuencial. Y un desmejoramiento
de 256% respecto a algoritmo secuencial. Tambin cabe resaltar que
las pruebas realizadas fueron en un entorno muy simple y de bajos
recursos.
En un entorno con 8 ncleos y 16gb de ram, podra encontrarse
con un mejor rendimiento del algoritmo aqu desarrollado.

BIBLIOGRAFIA
George Em Kardiadakis y Robert M. Kirby II. University
Cambridge. Parallel Scientific Computing in C++ and MPI,
Francisco Hidrobo y Herbert
(Message Passing Interface).

Hoeger.

Introduccin

MPI

Wilson Eduardo Soto Forero. Estudio Comparativo de Algoritmos


para el Problema de la Subsecuencia Comn Ms Larga Restringida.
Bogot, Junio 2010.
Jones, Neil C: An introduction to bioinformatics algorithms. MIT
Press, Cambridge, MA, (2004). ISBN 0262101068
Navarro, Gonzalo: A Guided Tour To
Matching
.
Computing Surveys, (2001), 33(1), pp. 31 88.

Approximate

String
ACM

Vous aimerez peut-être aussi