Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
1
The Sieve of Eratosthenes
Jorge Barbosa
2
Objectivos
1. Análise de sistemas de distribuição de dados por blocos
• Função MPI_Bcast
• Análise de desempenho
3
Modelos Computacionais
ÍÍndicendiceAlgoritmo SequencialIdentificação de fontes de paralelismoPartição de dadosDesenvolvimento e análise do algoritmo paraleloPrograma MPIBenchmarkingOptimização
4
Algoritmo Sequencial
2 3 4 5 6 7 8 9 10 11 12 13 14 15 1617 18 19 20 21 22 23 24 25 26 27 28 29 30 3132 33 34 35 36 37 38 39 40 41 42 43 44 45 4647 48 49 50 51 52 53 54 55 56 57 58 59 60 61
2 4 6 8 10 12 14 1618 20 22 24 26 28 30
32 34 36 38 40 42 44 4648 50 52 54 56 58 60
3 9 1521 27
33 39 4551 57
525
3555
7
49
Complexidade: Θ(n ln ln n)
5
Pseudo-código
1. Criar a lista de números naturais 2, 3, …, n2. k ← 23. Repetir
(a) Marcar todos os múltiplos de k entre k2 e n(b) k ← número mais pequeno não marcado e > k
Até k2 > n4. Os números não marcados são números primos
6
Fontes de paralelismo
• Decomposição de Domínio– Dividir os dados em blocos– Associar processamento a cada bloco
• Uma tarefa básica por elemento do array
7
Tornar o ponto 3(a) Paralelo
Marcar todos os múltiplos de k entre k2 e n
⇒
for all j where k2 ≤ j ≤ n doif (j mod k == 0) then
mark j (j não é número primo)endif
endfor
8
Tornar o ponto 3(b) Paralelo
Determinar o menor número > k que não esteja marcado
⇒
Min-reduction (para determinar o menor número não marcado)
Broadcast (enviar o valor obtido para todos os processos)
9
Aglomeração de tarefas
• Objectivos:– Obter tarefas com granularidade adequada– Reduzir o custo de comunicação– Balanceamento da carga pelos processadores
10
Decomposição dos dados
• Cíclica– Fácil de encontrar o processador que detém um dado elemento– Para este problema resulta em load imbalance
• Por Blocos– Balanceamento de carga– Mais difícil de determinar a localização dos dados se n não for múltiplo
de p
11
Decomposição dos dados
• Balanceamento da carga quando n não é múltiplo de p
• Cada processo recebe ⎡n/p⎤ ou ⎣n/p⎦ elementos
• Obter expressões simples para:
– Determinar primeiro e último índice dado um processador
– Determinar o processador dado um índice
12
Método 1
• r = n mod p
• If r = 0, todos os blocos têm o mesmo tamanho
• Else
– Primeiros blocos r ficam com o tamanho ⎡n/p⎤
– Restantes blocos p-r ficam com o tamanho ⎣n/p⎦
13
Exemplos
17 elementos divididos por 7 processos
17 elementos divididos por 5 processos
17 elementos divididos por 3 processos
14
Método 1: Cálculos
• Primeiro elemento controlado pelo processo i
• Último elemento controlado pelo processo i
• Processo que controla o elemento j
⎣ ⎦ ),min(/ ripni +
⎣ ⎦ 1),1min(/)1( −+++ ripni
⎣ ⎦⎣ ⎦ ⎣ ⎦⎣ ⎦)//)(,)1//(max( pnrjpnj −+
15
Método 2
• “Espalha” os blocos maiores pelos processos• Primeiro elemento controlado pelo processo i
• Último elemento controlado pelo processo i
• Processo que controla o elemento j
⎣ ⎦pin /
⎣ ⎦ 1/)1( −+ pni
⎣ ⎦njp /)1)1(( −+
16
Exemplos
17 elementos divididos por 7 processos
17 elementos divididos por 5 processos
17 elementos divididos por 3 processos
17
Comparação dos métodos
24Primeiro índice
47Processo
46Último índice
Método 2Método 1Operações
Operação “floor” não incluída
Melhor
18
Aplicação método 2
• Ilustração do método 2 na divisão de 13 elementos por 5 processos
13(0)/ 5 = 0
13(1)/5 = 2
13(2)/ 5 = 5
13(3)/ 5 = 7
13(4)/ 5 = 10
processo
19
Macros para a Decomposição dos dados
#define BLOCK_LOW(id,p,n) ((i)*(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))
20
Índices Locais vs. Globais
L 0 1
L 0 1 2
L 0 1
L 0 1 2
L 0 1 2
G 0 1 G 2 3 4
G 5 6
G 7 8 9 G 10 11 12
21
Acesso aos Elementos
• Programa Sequencialfor (i = 0; i < n; i++) {
…}
• Programa Paralelosize = BLOCK_SIZE (id,p,n);for (i = 0; i < size; i++) {
gi = i + BLOCK_LOW(id,p,n);…
}Índice i neste processo…
…Corresponde ao índice gi do programa sequencial
22
A Decomposição efectuada afecta a Implementação
• O maior valor usado como semente é √n
• O primeiro processo tem ⎣n/p⎦ elementos
• Terá todas as sementes se p < √n
• Como consequência será sempre o primeiro processo a transmitir a
semente
• Então não é necessária a operação de redução
23
Marcação rápida
• A decomposição por blocos permite usar o mesmo algoritmo usado na versão sequencial:
j, j + k, j + 2k, j + 3k, … (o processo contém lista sequencial)
em vez de
for all j in blockif j mod k = 0 then mark j (it is not a prime)
24
Desenvolvimento do algoritmo paralelo
1. Criar lista de números naturais não marcados 2, 3, …, n
2. k ← 2
3. Repeat
(a) Marca todos os múltiplos de k entre k2 e n
(b) k ← o mais pequeno numero por marcar > k
until k2 > m
4. Os números não marcados são primos
Cada processo cria a sua parte da listaTodos os processos fazem o ponto 2
Cada processo marca na sua sublista
Apenas Processo 0
(c) Processo 0 broadcasts k para os restantes
5. Redução para obter todos os números primos
25
Função MPI_Bcast
int MPI_Bcast (
void *buffer, /* Addr of 1st element */
int count, /* # elements to broadcast */
MPI_Datatype datatype, /* Type of elements */
int root, /* ID of root process */
MPI_Comm comm) /* Communicator */
MPI_Bcast (&k, 1, MPI_INT, 0, MPI_COMM_WORLD);
26
Análise de complexidade
• χ tempo necessário para marcar uma célula• Tempo de execução sequencial: χ n ln ln n• Número de broadcasts: √n / ln √n• Tempo de Broadcast : λ ⎡ log p ⎤• Tempo de execução total:
⎡ ⎤pnnpnn log)ln/(/lnln λχ +
27
Código (1/4)
#include <mpi.h>#include <math.h>#include <stdio.h>#include "MyMPI.h"#define MIN(a,b) ((a)<(b)?(a):(b))
int main (int argc, char *argv[]){
...MPI_Init (&argc, &argv);MPI_Barrier(MPI_COMM_WORLD);elapsed_time = -MPI_Wtime();MPI_Comm_rank (MPI_COMM_WORLD, &id);MPI_Comm_size (MPI_COMM_WORLD, &p);
if (argc != 2) {if (!id) printf ("Command line: %s <m>\n", argv[0]);MPI_Finalize(); exit (1);
}
28
Código (2/4)
n = atoi(argv[1]);low_value = 2 + BLOCK_LOW(id,p,n-1);high_value = 2 + BLOCK_HIGH(id,p,n-1);size = BLOCK_SIZE(id,p,n-1);proc0_size = (n-1)/p;if ((2 + proc0_size) < (int) sqrt((double) n)) {
if (!id) printf ("Too many processes\n");MPI_Finalize();exit (1);
}
marked = (char *) malloc (size);if (marked == NULL) {
printf ("Cannot allocate enough memory\n");MPI_Finalize();exit (1);
}
29
Código (3/4)
for (i = 0; i < size; i++) marked[i] = 0;if (!id) index = 0;prime = 2;do {
if (prime * prime > low_value)first = prime * prime - low_value;
else {if (!(low_value % prime)) first = 0;else first = prime - (low_value % prime);
}for (i = first; i < size; i += prime) marked[i] = 1;if (!id) {
while (marked[++index]);prime = index + 2;
}MPI_Bcast (&prime, 1, MPI_INT, 0, MPI_COMM_WORLD);
} while (prime * prime <= n);
30
Código (4/4)
count = 0;for (i = 0; i < size; i++)
if (!marked[i]) count++;MPI_Reduce (&count, &global_count, 1, MPI_INT, MPI_SUM,
0, MPI_COMM_WORLD);elapsed_time += MPI_Wtime();if (!id) {
printf ("%d primes are less than or equal to %d\n",global_count, n);
printf ("Total elapsed time: %10.6f\n", elapsed_time);}MPI_Finalize ();return 0;
}
31
Benchmarking
• Estimar o tempo de execução para o calculo de números primos até 100 milhões.
• Determinar χ = 85.47 nanosec• Executar a versão sequencial num dos processadores.
Tempo de execução de 24.90 s.logo χ = 24.90/(10^8 ln ln 10^8) = 85.47 ns
• Determinar λ, o tempo para um broadcast.• Executar sucessivamente vários broadcasts para 2, …, p
processadores.– Obtém-se por exemplo λ = 250 μsec
Tempo total estimado em segundos:
⎡ ⎤p./p. log271409024 +
32
Tempo de execução (seg)
4.222
4.687
5.159
5.993
7.055
9.039
13.011
24.900
Actual (seg)
2.23%
Erro
3.9278
4.3717
4.9646
5.7945
6.7684
8.8433
12.7212
24.9001
EstimadoProcessadores
33
Melhoramentos
• Apagar os números pares
– Reduz o número de cálculos para metade
– Liberta memória para valores elevados de n
• Cada processo determina os números primos que servem de semente
– Replicação da computação dos valores primos até √n
– Elimina o passo de broadcast
• Reorganização dos ciclos
– A troca dos ciclos, i.e. para um segmento do vector de dados procurar os múltiplos de todas as sementes até √n (sendo n relativo ao segmento actual), aumenta a ‘cache hit rate’ (i.e. reduz as falhas de cache)
34
Reorganização dos Ciclos
Cache hit rate
Mais falhas de cache
Melhor desempenho da cache
35
Comparação das 4 Versões
1.585
1.820
2.127
2.559
3.201
4.272
6.378
12.466
Sieve 3 Sieve 4
0.3422.8563.9278
0.3913.0594.3717
0.4563.2704.9646
0.5433.6525.7945
0.6794.0726.7684
0.9015.0198.8433
1.3306.60912.7212
2.54312.23724.9001
Procs Sieve 2Sieve 110-vezes menor
7-vezes menor
36
Sumário
• Sieve of Eratosthenes: a paralelização pela decomposição do domínio
• Comparação de duas distribuições
– Escolhida a que apresenta formulas mais simples
• A optimização considerada revelou-se importante para maximizar o desempenho sequencial (um processador)