El Cedazo de Eratosthenes Capítulo 5. Metas Analizar métodos de asignar bloques (“block...

Preview:

Citation preview

El Cedazo de Eratosthenes

Capítulo 5

Metas

• Analizar métodos de asignar bloques (“block allocation schemes”)

• Entender la función MPI_Bcast

• Estudiar métodos para mejorar rendimiento

El Cedazo de Erastothenes

• Un número primo es un entero mayor que 1 que no tiene factores ademas de 1 y si mismo.

• El número de primos es infinito.• No hay ninguna fórmula para generar los primos• Sin embargo, el matemático griego

Erastothenes (276-194 BC) inventó un algoritmo para generar los primos menores que n, para cualquier entero dado n.

El Algoritmo de Erastothenes Secuencial

1. Crear una lista de enteros 2, 3, …, n.2. Sea k = 2.3. Repeat

(a) Marcar todos los multiplos de k entre k2 y n

(b) Sea k el entero no marcado menor que es mayor que k until k2 > n4. Los números no marcados son primos.

Ejemplo

• 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

■ multiplos de 2

■ multiplos de 3

■ multiplos de 5

■ multiplos de 7

Estructura de Datos para el Algoritmo Secuencial

• Usar un arreglo booleano con n-1 elementos

• Elementos i, 0,1,…,n-2, representan 2,3,…,n, respectivamente

• Elemento en posición i, i=0,1,…,n-2, es 1 si y solo si el número i+2 se ha marcado, es decir, es un número compuesto..

• Inicialmente todas las posiciones son 0, es decir ningun número está marcado.

Descomposición de Dominio

• Particionar el arreglo en n-1 pedazos.• Luego, para cada k, se puede buscar los

multiplos de k entre k2 y n en paralelo• Mucho paralelismo, pero muchas

comunicaciones pues• En el próximo paso, hay que usar una

reducción-min para deteminar el menor número no marcado y una emisión para avisar todas las tareas lo que es

Descomposición en Bloques

• Dividir el arreglo en p bloques de aproximadamente el mismo tamaño de tal forma que se puede computar “facilmente” lo siguiente:

• Dado un valor de i, determinar el rango de datos que el proceso #i maneja

• Dado un elemento del arreglo de datos, determinar el proceso que lo maneja.

Método 1

• Si n = 0 mod p, dividir los n datos en bloques de tamaño n/p.

• Si n ≠ 0 mod p y si n = pq + r para enteros q y r, 0<r<p, entonces asignar bloques de tamaño n/p a los primeros r procesos y bloques de tamaño n/p a los otros p-r procesos.

(Note que si n=pq+r, donde 0<r<p, entonces n/p=q+r/p y por lo tanto

r n/p + (p-r) n/p = r(q+1) + (p-r)q = r + pq = n)

Ejemplo del Método 1

• n = 213 y p=8. 212 = 8 x 26 + 5 Se asignan a los procesos 0,1,2,3,4 bloques de tamaño 27 y a los procesos 5,6,7 bloques de tamaño 26.

Ejemplo del Método 1 (contd)

• Proceso 0 maneja a a0,a1,a2, … , a26

• Proceso 1 maneja a a27,a28,a29, … , a53

• Proceso 2 maneja a a54,a55,a56, … , a80

• Proceso 3 maneja a a81,a82,a83, … , a107

• Proceso 4 maneja a a108,a109,a110, … , a134

• Proceso 5 maneja a a135,a136,a137, … , a160

• Proceso 6 maneja a a161,a162,a163, … , a186

• Proceso 7 maneja a a187,a188,a189, … , a212

Rango y Número de Proceso para el Método 1

• El primer dato manejado por el Proceso #i es i n/p + min(i,r) y el último es (i+1)n/p + min(i+1,r)-1, donde n es el número de datos, p es el número de procesos y r es el residuo cuando n se divide entre p.

• El proceso que maneja al dato aj es

min(j / (n/p +1) ,(j-r)/ n/p )

Método 2

• El primer dato manejado por el dato ai es

i n/p y el último es (i + 1) n/p -1

• El proceso que maneja al dato aj es (p (j+1)-1)/ n

Ejemplo de Método 2

• Proceso 0 maneja a a0, … , a25

• Proceso 1 maneja a a26, … , a52

• Proceso 2 maneja a a53, … , a78

• Proceso 3 maneja a a79, … , a105

• Proceso 4 maneja a a106, … , a132

• Proceso 5 maneja a a133, … , a158

• Proceso 6 maneja a a159, … , a185

• Proceso 7 maneja a a186, … , a212

Comparación de Tamaños de Bloque para los dos Métodos

Proceso

Tamaño de

Bloque Método 1

Tamaño de

Bloque Método 2

0 27 26

1 27 27

2 27 26

3 27 27

4 27 27

5 26 26

6 26 27

7 26 27

P=

p=8

n=213

Comparación de Cantidades de Operaciones

Operación Método 1 Método 2

Indice

Menor

4 2

Indice

Mayor

6 4

Dueño 6 4

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))

• #define BLOCK_HIGH(id,p,n) (BLOCK_LOW((id)+1,p,n)-1)

• #define BLOCK_SIZE(id,p,n) (BLOCK_LOW((id)+1)-

BLOCK_LOW(id))

• #define BLOCK_OWNER(index,p,n) (((p)*((index)+1)-1)/(n))

La Necesidad de Parenteses en Macros con Argumentos

• Son necesarias para asegurar la sustitución correcta en todos los casos.

• Ejemplo: #define ABS(a) (a) < 0 ? –(a) : (a)

Sin parentesis, el compilador convertiriaABS(10-20) a 10-20 < 0 ? -10-20 : 10-20cuyo valor sería -30 en vez de 10.

Indices Locales versus Indices Globales

• Cuando un solo proceso brega con un arreglo de n datos, el indice (global) varia entre 0 y n-1

• Cuando p procesos brega con un arreglo de n datos, bajo la descomposición en bloques, el indice (local) varia entre 0 y el tamaño -1 del bloque que corresponde a cada proceso.

Comparasión de los Indices Locales y Globales

• Program Secuencial for (i = 0; i < n; i++) {

…}

• Program Paralelosize = BLOCK_SIZE (id,p,n);for (i = 0; i < size; i++) { gi = i + BLOCK_LOW(id,p,n);}// gi es el indice global y i el indice // local

Consecuencias de la Descomposión en Bloques

• Si el primer proceso es responsible por los enteros hasta que y incluyendo a n, entonces se puede determinar el próximo valor de k sin ninguna comunicación.

• Esto occure si n/p ≥ n, es decir si

p ≤n

Traducción del Algoritmo Secuencial

• Paso 2: Cada proceso deja que k=2.• Paso 3a: Cada proceso marca sus datos

mayor que k2 que son multiplos de k. Si el indice del primer multiplo es j, entonces se marca cada número en el bloque con indice de la form j+k,j+2k,j+3k,….

• Paso 3b: Proceso 0 determina el próximo valor de k y emite el valor a los otros procesos.

La Función 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(&prime,1,MPI_INT,0,MPI_COMM_WORLD)

asigna el valor de la variable entero prime del proceso 0 a la variable primo de todos los procesos en MPI_COMM_WORLD.

Código del programa MPI para contar el número de primos < n

#include <mpi.h>

#include <math.h>

#include <stdio.h>

#include "MyMPI.h"

/* MyMPI.h es un archivo “header” que contiene los macros previamente discutido y los prototipos de otras funciones que vamos a desarollar */

#define MIN(a,b) ((a)<(b)?(a):(b))

/* Define el mínimo de a y b*/

int main(int argc,char *argv[])

{

\* Declaraciones:*/

Código del Programa MPI para contar el número de primos < n (cont)

int count; //Local prime countint first; //index of first multipleint global_count; //global prime countint high_value; // highest value on this processorint i;int id; //processor idint index; //index of current prime int low_value; //lowest value on this processorchar *marked; //portion of 2,…,’n’int n; //number of dataint p; //number of processorsint proc0_size; //size of processor 0’s subarrayint prime; //current primeint size; //elements in ‘marked’

Código (cont)

MPI_Init(&argc,&argv); //Inicialize MPIMPI_Comm_rank(MPI_COMM_WORLD,&id); //assign ranks to procs MPI_Comm_size(MPI_COMM_WORLD,&p);//find number of procs

if (argc != 2) if (!id) { printf ("Command line: %s <m>\n", argv[0]); MPI_Finalize();

exit (1);}

n = atoi(argv[1]);/*Si falta el argumento n (argc != 2), terminamos el proceso y devolvemos un 1 (falló execución).

De otro modo convertimos la cadena de caracteres de la linea de comando a un entero utilizando la rutina predefinida atoi.*/

Código (cont)

low_value = 2 + BLOCK_LOW(id,p,n-1);

high_value = 2 + BLOCKHIGH(id,p,n-1);

size = BLOCK_SIZE(id,p,n-1);

/* Hacen uso de los macros guardados en MyMPI.h para determinar el primero y ultimo números y el tamaño del bloque */

proc0_size = (n-1)/p;

if ((2 + proc0_size) * (2+proc0_size) < n ){

if (!id) printf ("Too many processes\n");

MPI_Finalize();

exit (1);

}// Verificar que el Próximo Valor de k siempre esté en el Proceso 0

Código (cont)

marked = (char *) malloc (size);/* Cada elemento del arreglo marked consistirá de un byte*/

if (marked == NULL) { printf ("Cannot allocate enough memory\n"); MPI_Finalize(); exit (1); }//Assign memory for data in each procfor (i=0; i<size; i++) marked[i]=0;/* Inicialmente ningún número está marcado*//* prime juega del papel de k y index es su indice */if(!id) index = 0; /*El proceso 0 es el único proceso que

necesita el valor de esta variable*/prime = 2;

Código (cont)• do { //Marcar los no primos• if (prime * prime > low_value)• first = prime * prime - low_value;//la posicion de prime2

• else {• if (!(low_value % prime)) first = 0;//primer elemento del bloque es • //un multiplo de prime• else first = prime - (low_value % prime);//posición del primer • //multiplo de un multiplo de prime.• }• for (i = first; i < size; i += prime) marked[i] = 1;//marcar todos los multiplos • //de prime en el bloque• if (!id) {• while (marked[++index]);//buscar próxima posición no marcada• prime = index + 2; // el primo que corresponde a esta posición• }• MPI_Bcast (&prime, 1, MPI_INT, 0, MPI_COMM_WORLD); //enviar nuevo • //valor de prime a los otros procesos• } while (prime * prime <= n);

Contar el Número de Primoscount = 0; //Count the primes found by this processor for (i = 0; i < size; i++) if (!marked[i]) count++; MPI_Reduce (&count, &global_count,1,MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); //Find the total number of primesif (!id) printf ("%d primes are less than or equal to %d\n", global_count, n); MPI_Finalize (); return 0;}

Mejorar el Rendimiento

• Eliminar los números pares

Esto reduce por la mitad el número de computaciones y reduce la cantidad de almacenaje

Eliminar Broadcast

• Hacer el programa de tal forma que todo proceso identifique el próximo valor de k.

• Esto se puede hacer si

todo proceso tiene su propio arreglo que contiene los enteros 3,5,7,…, n y que luego compute secuencialmente la lista de primos entre 3 y n

Asignación (véase Sección 5.9 del texto)

Escribir un programa que cuenta el número de primos ≤ n haciendo los siguientes dos cambios al programa que estudiamos en clase:

(a) Considerar solamente los impares ≤ n

(b) Eliminar broadcast

Para entregar no mas tarde que el martes,

10 de mayo.

Reorganizar los Bucles

• El bucle exterior itera sobre primos entre 3 y

n y el bucle interior itera sobre un arreglo que representa la porción de datos de un proceso.

• La razón de “cache misses” puede ser grande debido a la disperción grande de los primos.

• Esto se puede mejorar intercambiando los bucles.

Recommended