Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Capítulo 6
Grafos con Pesos
• Un grafo dirigido en la cual hay asociado
con cada arista un número positivo (el
“peso”) se llama un grafo dirigido con
pesos.
• El largo de una trayectoria de un vértice u
a otro vértice v es la suma de los pesos de
las aristas que componen la trayectoria.
El Problema de Todos Pares
Distancias mas Cortas
• Dado un grafo dirigido con pesos, ¿cuales
son las trayectorias de largos mínimos (es
decir “distancias mas cortas”) entre todos
los pares de vértices?
La Matríz de Adyacencias
Matríz de Adyacencias
El Algoritmo de Floyd
for k 0 to n-1
for i 0 to n-1
for j 0 to n-1
a[i,j] min (a[i,j], a[i,k] + a[k,j])
endfor
endfor
endfor
La Solución al Ejemplo Anterior
4 A B C D E
A
A 0 6 3 6 4
B
3 6 B 4 0 7 1 8
1 3 C 10 6 0 3 1
C 5
1 D 7 3 10 0 11
D
E 9 5 12 2 0
E 2
Matríz de Distancias
La Idea del Algoritmo
La trayectoria mas corta de i a k
que pasa por 0, 1, …, k-1
i
Computed k
in previous
La trayectoria mas corta iterations
de i a j que pasa por
0, 1, …, k-1 La trayectoria mas corta
de k a j que pasa por
j 0, 1, …, k-1
El Diseño del Algoritmo Paralelo
• Particionar
• Patronos de Comunicaciones
• Aglomeración y Asignación
Particionar
• En el seudo código la misma asignación
se ejecuta n3 veces.
• No hay paralelismo funcional.
• Usemos descomposición de dominio:
particionar la matriz A en sus n2
elementos.
Comunicaciones
Poner al dia
Tareas a[3,4]
priimitivas Cuando k=1
Iteración k:
Iteración k:
Toda tarea
Toda tarea
en la fila k
en la columna
emite su valor
k emite su valor
a los procesos
a los procesos
en la misma
en la misma
columna
fila
Aglomeración y Asignación
• El número de tareas es estatico
• Las comunicaciones entre las tareas son
regulares
• El tiempo de computación por tarea es
constante
• Un buena estrategia en este caso es
– Aglomerar tareas para minimizar las
comunicaciones
– Crear una tarea por proceso MPI
Dos Descomposiciones
Bloques de Filas Bloques de Columnas
“Rowwise block striped” “Columnwise block striped”
Comparación de
Descomposiciones
• Bloques de Columnas
– Se eliminan las emisiones dentro de las
columnas
• Bloques de Filas
– Se eliminan las emisiones dentro de las
columnas
– Escribir la salida es mas simple
• Escoger descomposión en bloque de filas
Unos Preliminares sobre
Descomposición en Bloques
• Dividir una sucesión de datos en p
bloques de aproximadamente el mismo
tamaño de tal forma que se puede
computar “facilmente” lo siguiente:
• Dado el rango #i de un proceso,
determinar los datos que el proceso #i
maneja
• Dado i-ėsimo dato, determinar el proceso
que lo maneja.
Una Descomposición de Bloque de
datos numerados 0,1,…,n-1
• El primer dato manejado por el proceso i
es i n/p y el último es (i + 1) n/p -1
p=8
0 26
1 27 n=213
P
=
2 26
3 27
4 27
5 26
6 27
7 27
Macros
• La directiva #define define un identificador
y una sucesión de caracteres que se
sustituirá por el identificador cada vez que
se encuentra en el programa.
• Macros pueden tener argumentos, como
funciones.
• Para definir un macro que usa
argumentos, insertar parametros entre
parenteses en la definición.
Macros para la Descomposición en
Bloques con el Método 2
#define BLOCK_LOW(id,p,n) ((id)*(n)/(p))
//El indice menor controlado por el proceso id
Archivo
La Entrada de la Matríz de
Adyacencias
• La matríz se guarda en el orden de filas (“row
major order” en un archivo”.
• Si hay p procesos, entonces para cada
i=0,1,…,p-2, el proceso p-1 lee fila in/p hasta
la fila (i+1)n/p -1 y las envia al proceso i.
Después, lee las últimas filas para el mismo.
• La razón por la cual p-1 hace este trabajo es
que no hay ningun proceso que va a ser
responsible por mas filas que el p-1 (Ejercicio
6.1)
Comunicación Punto-Punto
• Envuelve dos procesos
• Un proceso envia un mensaje
• El otro proceso receibe el mensaje
Enviar
int MPI_Send (
void *mensage,
int cantidad,
MPI_Datatype tipo,
int dest,
int etiqueta,
MPI_Comm comunicador
)
Recibir
int MPI_Recv (
void *mensage,
int cantidad,
MPI_Datatype tipo,
int fuente,
int etiqueta,
MPI_Comm comunicador,
MPI_Status *estatus//un apuntador a un
//record de tipo MPI_Status
)
El Código para Eniviar/Recibir
if (ID == j) {
…
Recibir de i
…
}
…
if (ID == i) {
…
Enviar a j
…
}
Volver de MPI_Send
• La función bloquea hasta que el buffer
este libre
• Tipicamente el mensaje se envia a un
buffer de mensaje que permite la
devolución de control al proceso que llama
Volver de MPI_Recv
• La función bloquea hasta que el mensaje
se haya recibido o hasta que un error se
haya detectado
• Cuando occure un error, el record
contiene información acerca del proceso
que envió el mensaje, el valor de la
etiqueta, y la condición de error.
Punto Muerto (“Deadlock”)
• Ocurre cuando un proceso espera una
condición que nunca occura.
• Es facil escribir código de enviar/recibir
que llegue a un punto muerto
– Dos procesos reciben antes de enviar.
– La etiqueta de enviar no es la misma como la
etiqueta de recibir.
– Un proceso envia un mensaje a una
destinación incorrecta.
Ejemplo
Float a,b,c;
Int id;
MPI_Status estatus;
…
If(id==0)
MPI_Recv(&b,1,MPI_FLOAT,1,0,MPI_COMM_WORLD,&estatus);
MPI_Send (&a,1,MPI_FLOAT,1,0,MPI_COMM_WORLD);
C=(a+b)/2.0;
else if (id==1){
MPI_Recv(&a,1,MPI_FLOAT,0,0,MPI_COMM_WORLD,&estatus);
MPI_Send (&b,1,MPI_FLOAT,0,0,MPI_COMM_WORLD);
C=(a+b)/2.0
La Función MPI_Bcast
• int MPI_Bcast (
void *buffer, /* Direccion del primer
elemento */
int count, /* número de elementos para
emitir */
• MPI_Datatype datatype, /* Tipo de
elementos */
• int root, /* ID de proceso que emite */
• MPI_Comm comm) /* Communicador */
Ejemplo de MPI_Bcast
MPI_Bcast(&k,1,MPI_INT,0,MPI_COMM_WORLD)
asigna el valor de la variable entero k del
proceso 0 a la variable k de todos los
procesos en MPI_COMM_WORLD.
Arreglos Dinámicos
Unidimensionales/
main()
{
Int i, n=10;
int *A;
A=(int *) malloc (n* sizeof(int));
//Ahora, A se puede usar como un arreglo ordinario.
// Por ejemplo,
for (i=0;i<n;i++)
{
A[i]=2*i;
printf("%d\n",A[i]);
}
}
Arreglos Dinámicos
Bidimensionales
main()
{
int i,j,m=4,n=3;
int **B, *Bstorage;
Bstorage=(int *) malloc (m*n*sizeof(int));//asignar memoria para arreglo mXn
B= (int **) malloc (m*sizeof(int *)); //asignar memoria para el arreglo
//de apuntadores a las filas
for (i=0;i<m;i++)
B[i]=&Bstorage[i*n]; //Inicializar el arrego de apuntadores a las filas
for (i=0;i<m;i++)
{
for (j=0;j<n;j++)
{
B[i][j]=i*j;
printf("%d ",B[i][j]);
}
printf("\n");
}
}
main
• /*Leer archivo que contiene la matríz de
distancias*/
• /*Imprimir la matríz de distancias*/
• /*Llamar la función
compute_shortest_paths*/
• /*Computar tiempo total*/
• /*Imprimir la nueva matríz de distancias
Algoritmo de Floyd
Declaraciones y Inicializaciones
#include <stdio.h>
#include <mpi.h>
#include "../MyMPI.h"
typedef int dtype;
#define MPI_TYPE MPI_INT
int main (int argc, char *argv[])
{ dtype **a; /* Doubly-subscripted array */
dtype *storage; /* Local portion of array elements */
int i, j, k;
int id; /* Process rank */
int m; /* Rows in matrix */
int n; /* Columns in matrix */
int p; /* Number of processes */
double time, max_time;
void compute_shortest_paths (int, int, int**, int);
MPI_Init (&argc, &argv);
MPI_Comm_rank (MPI_COMM_WORLD, &id);
MPI_Comm_size (MPI_COMM_WORLD, &p);
Algoritmo de Floyd (2)
read_row_striped_matrix (argv[1],(void **) a,MPI_type,m,n,MPI_COMM_WORLD);
MPI_Finalize();
}
compute_shortest_paths
• void compute_shortest_paths (int id, int p, dtype **a, int n)
• {
• int i, j, k;
• int offset; /* Local index of broadcast row */
• int root; /* Process controlling row to be bcast */
• int *tmp; /* Holds the broadcast row */
• tmp = (dtype *) malloc (n * sizeof(dtype));
• for (k = 0; k < n; k++)
• { root = BLOCK_OWNER(k, p, n);
• if (root == id)
• { offset = k - BLOCK_LOW(id, p, n);
• for (j = 0; j < n; j++)
• tmp[j] = a[offset][j];
• }
• MPI_Bcast (tmp, n, MPI_TYPE, root, MPI_COMM_WORLD);
• for (i = 0; i < BLOCK_SIZE(id, p, n); i++)
• for (j = 0; j < n; j++)
• a[i][j] = MIN(a[i][j], a[i][k] + tmp[j]);
• }
• free (tmp);
• }
Complexidad Computacional
• El bucle exterior se ejecuta n veces
• El bucle en el medio se ejecuta a lo sumo
most n/p veces
• La complexidad es (n3/p)
Complexidad de Comunicaciones
• El bucle exterior contiene un Broadcast, lo
que contribuye una complexidad de
(nlog p) para cada una de las n
iteraciones. La complexidad de tiempo
total para comunicaciones es (n2log p)
• La complexidad de tiempo total es
(n2log p +n3/p)
La Entrada al Programa Paralela
de Floyd
• La entrada (y también la salida) es una
matríz de distancias.
• La función
read_row_striped_matrix
lee esta matríz de un archivo y
distribuye las filas entre los procesos
en un arreglo dinámico bidimensional
• La entrada tiene que ser de la forma de
un arreglo dinámico bidimensional
Un Program para Generar una
Matríz de Distancias Aleatorias
• Este programa es una variación de un
programa, genMat4floyd, escrito por
Andrea di Blas
• Hay 2 entradas en la linea de comando:
n (el número de vértices)
salida (el nombre del archivo de salida)
Un Programa para Crear un
Archivo con una Matríz de
• *
Distancias Dada
#include <stdio.h>
#include <stdlib.h>
/******************************************************************************/
int main(int argc, char *argv[])
{ int i, j;
int n;
FILE *fp;
int *Astorage;
int **A;
int x;
if(argc != 3)
{ printf("\nDebe ser: generar <n> <archivo> ");
printf("\ndonde la matriz de distancias es nxn");
printf("\n");
exit(1);
}
Programa generar(cont)
n=atoi(argv[1]);
//Abrir archivo para escribir
if((fp = fopen(argv[2], "w")) == NULL)
{ printf("\n*** no se puede escribir en archivo %s ***\n", argv[2]);
exit(1);
}
fclose(fp);
return(0);
}
• }
Función para Leer Matríz de
Distancias
/** Process p-1 opens a file and inputs a two-dimensional
matrix, reading and distributing blocks of rows to the
other processes.*/
void read_row_striped_matrix (
char *s, /* IN - File name */
void ***subs, /* OUT - 2D submatrix indices */
void **storage, /* OUT - Submatrix stored here */
MPI_Datatype dtype, /* IN - Matrix element type */
int *m, /* OUT - Matrix rows */
int *n, /* OUT - Matrix cols */
MPI_Comm comm) /* IN - Communicator */
{
int datum_size; /* Size of matrix element */
int i;
int id; /* Process rank */
FILE *infileptr; /* Input file pointer */
int local_rows; /* Rows on this proc */
void **lptr; /* Pointer into 'subs' */
int p; /* Number of processes */
void *rptr; /* Pointer into 'storage' */
MPI_Status status; /* Result of receive */
int x; /* Result of read */
read_row_striped_matrix (cont)
MPI_Comm_size (comm, &p);
MPI_Comm_rank (comm, &id);
datum_size = get_size (dtype);
if (id == (p-1)) {
infileptr = fopen (s, "r");
if (infileptr == NULL) *m = 0;
else {
fread (m, sizeof(int), 1, infileptr);
fread (n, sizeof(int), 1, infileptr);
}
}
MPI_Bcast (m, 1, MPI_INT, p-1, comm);
if (id == (p-1)) {
for (i = 0; i < p-1; i++) {
x = fread (*storage, datum_size,
BLOCK_SIZE(i,p,*m) * *n, infileptr);
MPI_Send (*storage, BLOCK_SIZE(i,p,*m) * *n, dtype,
i, DATA_MSG, comm);
}
x = fread (*storage, datum_size, local_rows * *n,
infileptr);
fclose (infileptr);
} else
MPI_Recv (*storage, local_rows * *n, dtype, p-1,
DATA_MSG, comm, &status);
}
Función para Imprimir la Matríz de
Distancias
• print_row_striped_matrix
• Tambien en MyMPI.c
Como Usar el Programa Paralelo
de Floyd
• Crear un archivo que contiene la matríz de
distancias utilizando o generar.c (para un
grafo especifico) o genMat4floyd.c (para un
grafo a la azar)
• Si el archivo de objeto está en generar o
genMat4floyd, respectivamente, entonces se
ejecutan por
• ./generar n archivo
• o
./genMat4floyd n r archivo semilla
Compilar y Ejecutar
• Compilar MyMPI.c y floyd.c separadamente para
crear archivos o
mpicc –c MyMPI.c
mpicc –c floyd.c
• Link:
mpicc MyMPI.o floyd.o -o floyd
• Ejecutar:
mpirun –np p ./floyd archivo
donde p es un número específico de
procesadores
Ejercicio
• (6.3 del texto)
Indicar los cambios que hay que hacer en
la implementación de Floyd si asignamos
bloques de columnas a los procesos en
vez de bloques de filas.