• No results found

In the parallel operations strategy, the legacy repository and the target system operate simultaneously, with both systems performing

En esta sección trataremos de dar una visión mas técnica del trabajo llevado a cabo, para futuras modificaciones o ampliaciones del mismo y facilitar la lectura del código para el lector técnico.

En la presente sección se describe el código fuente, incluidas las funciones implementadas tanto para la versión secuencial del algoritmo como la paralela, así como el entorno de desarrollo de ambas versiones.

Entorno de desarrollo

Es importante reseñar el entorno utilizado a la hora de implementar el PFC. Para las dos versiones se ha utilizado un entorno similar:

Sistema Operativo Ubuntu 8.04

Lenguaje C

Compilador Gcc 4.2.3 (versión secuencial) mpicc 4.2.3 (versión paralela)

IDE gedit, consola y make

Librerías MPICH (versión de desarrollo)

Estructuras de datos

A continuación hacemos una descripción de las estructuras de datos creadas y utilizadas en el desarrollo. Son comunes a la versión paralela y la secuencial, por tanto sólo las describimos una vez..

Unsigned char *

Los vectores de unsigned char no son una estructura creada ex profeso para nuestro proyecto, pero dada la importancia de los mismos en el programa, daremos una pequeña explicación de su uso.

Los vectores de char se utilizan para almacenar las imágenes bsq, así como las partes en que se divide la misma para procesarlos. El motivo de utilizar vectores de unsigned char es que tomamos las imágenes bsq como una simple colección de bytes a procesar. Para hacer esto C no tiene un tipo definido (por ejemplo Byte), por lo que utilizamos un tipo similar en este caso: unsigned char.

Los datos almacenados en los vectores dependen de la imagen, ya que pueden estar agrupados de 1 en 1, de 2 en 2, etc. Esta agrupación depende de la imagen en concreto y se define en la cabecera de la imagen.

BsqHeader

La estructura BsqHeader ha sido creada en exclusiva para la aplicación y nos permite almacenar los parámetros de la cabecera de las imágenes dentro de nuestro programa. Gracias a esta estructura podremos pasar información básica sobre la imagen entre las funciones de la aplicación. Esta estructura se rellena a partir del fichero .hdr (cabecera) de las imágenes bsq. La estructura de la misma es:

typedef struct{  int iNumSamples;  int iNumLines;  int iNumBands;  int iDataType;  int iMult;  long int iFileSize;  }BsqHeader;

El significado de cada término es el siguiente:

 iNumSamples: Número de samples, píxeles, por línea que tiene la imagen.  iNumLines: Número total de líneas que tiene la imagen bsq.

 iNnumBands: Número de bandas de la imagen.

 iDataType: Tipo de datos de la imagen. Este valor nos dice la forma en que se agrupan los bytes de la imagen.

 iMult: Una vez conocemos la agrupación de bytes, tenemos esta variable para saber como recorrer la imagen, es decir, almacena el tamaño del “paso” en bytes a la hora de recorrer la imagen.

 iFileSize: Tamaño de la imagen, en bytes.

sequential.c

Este fichero implementa la versión secuencial del algoritmo. Debido a que algunas funciones son comunes a los dos algoritmos, se omitirán en la descripción

del fichero parallel.c. ReadHeader

Lee el fichero de cabecera de una imagen, en formato hdr, y almacena los datos en una estructura de datos.

 Parámetros:

 filename_header: Cadena con el nombre de la cabecera

 BsqHeader *BH: Estructura de datos donde se almacena la salida. Es un parámetro de E/S.

 Retorna: Entero. 1 si todo ha funcionado bien, 0 si no. ReadPiece

Lee una porción de una imagen hiperespectral, devolviendo el resultado como un array de unsigned char..

 Parámetros:

 fp: Fichero donde leeremos.

 lines_offset: Dirección del fichero a partir de la cual se lee  lines: Número de líneas del fichero a leer.

 trozo: Vector de unsigned char que retorna la porción leída.  BH: Cabecera de la imagen.

 Retorna: Entero. 1 si todo ha funcionado bien, 0 si no. char2*

Transforman una serie de valores de un vector (dependiendo del tamaño del tipo destino) de unsigned char en un número con diferentes formatos: short, entero o double.

 Parámetros:

 num: Vector de unsigned char que transformaremos.  inic: Offset desde el que calcular el resultado.

 Retorna: Short, Entero o double. Valor transformado. Process

Procesa la imagen hiperespectral, recorriendo los píxeles y calculando la media, tomando una ventana definida por el usuario.

 Parámetros:

 piece: Imagen a procesar.

 Lines: Número de líneas de la imagen.  tam_ventana: Tamaño de la ventana

 bh: Cabecera de la imagen

El siguiente fragmento recoge el bucle que recorre la imagen y procesa cada uno de los píxeles:

for(i=0;i<lines;i++) { for(j=0;j<bh.iNumSamples;j++) { for(k=0;k<bh.iNumBands;k++) { num_valores[k]=0; csuma[k]=0; ssuma[k]=0; isuma[k]=0; dsuma[k]=0; }

for(in=(i-(tam_ventana/2));in<=i+((tam_ventana/2));in++) {

for(jn=(j-(tam_ventana/2));jn<=(j+(tam_ventana/2));jn++) {

if(in>=0 && jn>=0 && (in!=i && jn!=j) && (in<lines) && (jn<bh.iNumSamples)) { for(k=0;k<bh.iNumBands;k++) { switch (bh.iMult) { case 1: csuma[k] +=piece[(k*piece_band_size)+((in*bh.iNumSamples)+jn)*bh.iMult]; break; case 2: ssuma[k] +=char2short(piece,(k*piece_band_size)+(((in*bh.iNumSamples)+jn)*bh.iMult)); break; case 4: isuma[k]+=char2int(piece, (k*piece_band_size)+(((in*bh.iNumSamples)+jn)*bh.iMult)); break; case 8: dsuma[k] +=char2double(piece,(k*piece_band_size)+(((in*bh.iNumSamples)+jn)*bh.iMult)); break; } num_valores[k]++; } } } } for(k=0;k<bh.iNumBands;k++) { switch (bh.iMult) { case 1:

cresult=(unsigned char) (csuma[k]/num_valores[k]); piece[(k*piece_band_size)+(i*bh.iNumSamples) +j]=cresult; break; case 2:

sresult=(short)(ssuma[k]/num_valores[k]); memcpy(&piece[(k*piece_band_size)+

(((i*bh.iNumSamples)+j)*bh.iMult)],&sresult,2);

break;

case 4:

iresult=(int)(isuma[k]/num_valores[k]); memcpy(&piece[(k*piece_band_size)+ (((i*bh.iNumSamples)+j)*bh.iMult)],&iresult,4);

break;

case 8:

dresult=(double)(dsuma[k]/num_valores[k]); memcpy(&piece[(k*piece_band_size)+ (((i*bh.iNumSamples)+j)*bh.iMult)],&dresult,8); break; } } } } ParameterProcess

Procesa los parámetros de entrada del programa.

 Parámetros:

 argc: Número de parámetros.  Argv: Valor de los parámetros.

 Retorna: Entero. 1 si todo ha funcionado bien, 0 si no.

SecuencialProcess

Procesa de forma secuencial una imagen hiperespectral. Utiliza la función process para realizar los cálculos.

 Parámetros:

 filename: Nombre de la imagen a procesar.  BH: Cabecera de la imagen.

 Retorna: Entero. 1 si todo ha funcionado bien, 0 si no.

parallel.c

Fichero donde se implementa la versión paralela del algoritmo, usando la librería MPI para ello.

ImagePartition

que procesar.

 Parámetros:

 filename: Nombre de la imagen a procesar.  BH: Cabecera de la imagen.

 num_nodes: Número de procesadores/ equipos.  Retorna: Entero. 1 si todo ha funcionado bien, 0 si no.

La forma en que se particiona la imagen, es:

for(n=0;n<num_nodes;n++) { if(n==0) { lines=LinesAssign[n]+(HalfWinSize); offset_mod=0; } else if(n==num_nodes-1) { lines=LinesAssign[n]+(HalfWinSize); offset_mod=-HalfWinSize; } else { lines=LinesAssign[n]+(HalfWinSize*2); offset_mod=-HalfWinSize; }

Pieces[n]=(unsigned char*)malloc(BH.iMult*lines*BH.iNumSamples*BH.iNumBands); ReadPiece(fp,lines_offset+offset_mod,lines,Pieces[n],BH);

lines_offset+=LinesAssign[n]; }

SaveImage

Guarda los resultados del proceso en un archivo bsq válido, teniendo en cuenta las piezas en que se divide la imagen.

 Parámetros:

 BH: Cabecera de la imagen.

 num_nodes: Número de procesadores/ equipos.  Retorna: Entero. 1 si todo ha funcionado bien, 0 si no. WorkloadCalculation

Calcula el tamaño de la carga de trabajo que se repartirá entre los procesadores. Se hace una prueba de carga, enviando a los procesadores una parte de la imagen. Con los tiempos de respuesta se calcula la carga asignada a cada procesador.

 Parámetros:

 BH: Cabecera de la imagen.

 Retorna: Entero. 1 si todo ha funcionado bien, 0 si no.

La parte central del algoritmo es la siguiente:

MedEstimatedTime=0;

for(i=0;i<MAX_PROBES;i++){ st_master=MPI_Wtime(); Process(P,EstimationLines,DEFAULT_WINSIZE,header); end_master=MPI_Wtime(); MedEstimatedTime+=(end_master-st_master); } total=MedEstimatedTime/MAX_PROBES; double NodeTime[MAX_NODES]; double Alpha=0; double K=0; int Total=0; NodeTime[0]=total;

for(i=1;i<num_nodes;i++) {

MPI_Recv(&time,sizeof(time),MPI_CHAR,i,TAG,MPI_COMM_WORLD,&status); NodeTime[i]=time;

total+=time; }

for(i=0;i<num_nodes;i++) {

Alpha+=(1/NodeTime[i]); }

K=header.iNumLines/Alpha;

for(i=0;i<num_nodes;i++) { LinesAssign[i]=K/NodeTime[i]; Total+=LinesAssign[i]; } OrderIndex(NodeTime,Index,num_nodes); i=0; while(i<header.iNumLines-Total) { LinesAssign[Index[(num_nodes-(i+1))]]++; i=(i+1)%num_nodes; }