Descrição do Funcionamento do Algoritmo Hiperquicksort
Joacir Bezerra de Siqueira, Marcos Tadeu A.
de Souza
Professor Jones Oliveira de Albuquerque
Universidade Federal Rural de Pernambuco
(UFRPE)
Departamento de Matemática e Física
Curso de Licenciatura em Computação
joacirsiqueira@terra.com.br, mtasufrpe@yahoo.com.br
Resumo. Este paper tem como finalidade principal, descrever o funcionamento do ALGORITMO HYPERQUICKSORT (HYPERCUBE MULTICOMPUTER) descrito no Capítulo 10, página 279 do livro Parallel Programming in C with MPI and OpenMP de Michael J. Quinn. Apresenta um exemplo prático onde é demonstrado passo-a-passo todo o desenvolvimento do algoritmo. No exemplo, para melhor compreensão, utiliza-se uma lista com 32 números para ser ordenado por 8 processadores de um Hypercubo.
Abstract.
This paper has as main purpose, to describe the operation of HYPERQUICKSORT
ALGORITHM (HYPERCUBE MULTICOMPUTER) described in the Chapter 10, page 279 of
the book Parallel Programming in C with MPI and OpenMP of Michael J. Quinn. It
presents a practical example where step-to-step is demonstrated the whole
development of the algorithm. In the example, for better understanding, a list
is used with 32 numbers to be ordered by 8 processors of a Hypercube.
Palavra-chave. Hypercubo, processadores, programação paralela.
Introdução. O estudo do algoritmo Hyperquicksort é bastante interessante, pois, assim pode-se ter uma idéia inteiramente precisa da Programação Paralela e Distribuída. O algoritmo Hyperquicksort que será analisado, inicia recebendo os valores aleatórios contidos em uma lista de valores. Estes valores são distribuídos uniformemente entre os processadores do hypercubo. É necessário deter o conhecimento do quicksort , pois cada processador fará inicialmente o quicksort em na relação de valores que recebeu da lista inicial. A seguir é apresentado o algoritmo e será analisado um exemplo.
ALGORITMO
HIPERQUICKSORT PARALELO.
Global n (Nº
inicial de elementos por processador)
d (Dimensão
do hipercubo)
i (Nº da
dimensão do hipercubo em uso)
Local logical.num (Numeração
única do processador)
partner (Processador
sócio na troca)
root (Processador
raiz no hipercubo em uso)
splitter (Valor
médio do processador raiz)
begin
for
all Pj , where 0 £ j < 2d do (1)
Ordene
os n elementos usando o quicksort seqüencial (2)
if d > 0 then (3)
for
i ¬ d downto 1 do (4)
root ¬ root of the binary i-cube containing processor
logical.num (5)
if logical.num = root then (6)
splitter ¬
valor médio do processador logical.num (7)
endif
Processador
raiz envia valor médio para demais processadores do i-cubo (8)
Use splitter
para partição dos valores ordenados em uma lista dos maiores e dos menores
partner ¬
logical.num Ä
2 (i-1) (9)
if logical.num > partner then (10)
Envia elementos menores para o
processador partner
Recebe
elementos maiores do processador partner
else
(logical.num < partner)
Envia
elementos maiores para o processador partner
Recebe
elementos menores do processador partner
endif
Junte as 02
listas numa lista única ordenada (11)
endfor
endif
endfor
end
(1) Para todo processador
índice j, onde j seja maior ou igual a zero e menor que a dimensão do hipercubo
(2) Cada processador executa
seqüencialmente o algoritmo quicksort, com sua lista
(3) Enquanto d for maior que
zero, executar as instruções do laço
(4) A variável i recebe o
valor de d, decrementado até 1. A cada ciclo i será decrementado até o valor
limite 1. Quando i for igual a 1, o
algoritmo se encerra.
(5) A variável root recebe o
menor número de identificação (em binário) do processador, do hipercubo que
está sendo processado.
(6) Se a numeração binária
única do processador for a mesma da variável root, executar os procedimentos
indicados no sublaço.
(7) A variável splitter recebe
o valor médio do processador cuja numeração binária única é igual a da variável
root
(8) A variável splitter é
divulgada para os demais processadores do hipercubo
(9) Efetua o cálculo do
processador parceiro, para todos os processadores do hipercubo. O cálculo é
realizado através da operação lógica OU EXCLUSIVO, entre a numeração binária do
processador e o número binário calculado pela expressão 2(i-1). Assim, para o processador 1 ( 01 em
binário ), quando i=2, tem como parceiro ( partner ):
0001+0010 = 0011 (processador
3)
(10) Para cada processador,
executa um dos procedimentos condicionais, conforme sua numeração seja menor ou
maior que a do seu parceiro. Cada processador que possui numeração maior que
seu parceiro, envia para ele os elementos de sua lista menores que o splitter
(valor médio) do processador root do hipercubo corrente e recebe deste os
valores maiores que o splitter.
(11) Após as permutações,
entre processadores e seus parceiros, junte/ordene suas listas numa lista única
de elementos.
Exemplo de execução do Algoritmo.
Aqui verificamos inicialmente os
passos (1) e (2), ou seja distribuição dos elementos entre os processadores e a
ordenação de suas listas. A seguir como d = 3 - passo (3), entra-se no laço
seguinte – passo (4), com i recebendo inicialmente o valor de d,
ou seja i=3. Como estamos inicialmente trabalhando com todo o hipercubo,
o processador de menor numeração P0, é selecionado como raiz – root recebe
a numeração de P0 - passo (5). O
valor médio de P0 (44, splitter) é divulgado para todos os demais
processadores – passo (8). A seguir cada processador calcula seu processador
parceiro – passo (9). Assim formam-se os pares P0/P4, P1/P5, P2/P6
e P3/P7.
Inicialmente temos a seguinte lista de valores aleatórios:
60 |
2 |
44 |
32 |
21 |
18 |
3 |
90 |
20 |
22 |
10 |
14 |
38 |
28 |
30 |
6 |
4 |
70 |
65 |
35 |
25 |
8 |
15 |
55 |
|
P6 P7
P4 P5
P2 P3
P0 P1
Os números da lista são distribuídos uniformemente entre os processadores do hypercubo, ficando do seguinte modo:
P2 3 90 20 P3
22 10
14 P6 65 35
25 P7 8
15 55
P0 60
2 44 P1
32 21 18
P4 38 28
30 P5 6
4 70
Neste momento cada processador realiza o seqüencial quicksort em sua lista de valores. Então fica:
P2 3 20 90 P3 10 14 22 P6 25 35 65 P7 8 15 55
P0 2 44 60 P1
18 21
32 P4 28 30
38 P5 4 6
70
Como temos um único Hypercubo, então o variável root= 0000b. Neste momento temos a logical.num = root. Daí o processador P0 informa a todos processadores que fazem parte do hypercubo, seu número médio. No exemplo é o numero (44). Portanto a variável splitter = 44. Será utilizado por todos processadores.
A Dimensão do Hypercubo é 2d.
Como d=3 temos 23 = 8 processadores
Determinação do partner:
partner ← logical.num. 2i – 1 { Na 1ª passagem temos 23-1 = 22 = 4 = 0100b }.
partner:
P0 = 0100 P1 = 0100 P2 = 0100 P3 = 0100
0000 0001 0010 0011
0100b 0101b 0110b 0111b
P4 = 0100 P5
= 0100 P6 = 0100 P7 = 0100
0100 0101 0110 0111
0000b 0001b 0010b 0011b
Portanto os processadores P0 com P4, P1
com P5, P2 com P6 e P3 com P7, farão a troca entre si da seguinte maneira: O
processado de menor índice envia seus valores maiores do que seu valor médio
(44) para seu parceiro e recebe do seu parceiro os valores menores do que o
valor médio (44). Daí fica:
P4 60 P5 70 P6 90 65 P7 55
P0 2 44
28
30 38 P1
18
21 32 4 6
P2 3 20 25 35
P3 10 14 22 8 15
De acordo com o algoritmo, faz-se um seqüencial quicksort em cada processador ficando do seguinte modo:
P4 60 P5 70 P6 65 90 P7 55
P0 2 28 30
38 44 P1 4 6
18 21
32 P2 3 20
25 35 P3 8
10 14 15 22
Neste passo d é decrementado de 1. Fica então i = 2. Portanto demos dois cubos:
P2 P3 P6 P7
P0 P1 P4 P5
O processador P0 envia seu valor médio (30) para os processadores P1, P2 e P3. O processador P4 envia seu valor médio (60) para os processadores P5, P6 e P7.
Determinação do partner:
partner ← logical.num. 2i – 1 { Na 2ª passagem temos 22-1 = 21 = 2 = 0010b }.
partner: P0 = 0010 P2
= 0010 P4 = 0010 P6 = 0010
0000 0010 0100 0110
0010b 0000b 0110b 0100b
P1 = 0010 P3
= 0010 P5 = 0010 P7 = 0010
0001 0011 0101 0111
0011b 0001b 0111b 0101b
Portanto os processadores P0 com P2, P1
com P3, P4 com P6 e P5 com P7, farão a troca entre si da seguinte maneira: O processado
de menor índice envia seus valores maiores do que seu valor médio para seu
parceiro e recebe do seu parceiro os valores menores do que o valor médio. Daí
fica:
P2 38 44 35 P3 32 P6 65 90 P7 70
P0 2 28 30 3 20 25 P1 4 6
18 21
8
10 14 15 22 P4 60 P5 55
Cada processador realiza o seqüencial quicksort. Fica ordenado da seguinte maneira:
P2 35 38 44 P3 32 P6 65 90 P7 70
P0 2 3
20 25 28 30 P1 4 6 8
10 14 15 18 21 22 P4 60 P5 55
Neste passo d é decrementado de 1. Fica então i = 1. Portanto temos:
P2 P3 P6 P7
P0 P1 P4 P5
Determinação do partner:
partner ← logical.num. 2i – 1 { Na 2ª passagem temos 22-1 = 21 = 2 = 0010b }.
partner: P0 = 0001 P2
= 0001 P4 = 0001 P6 = 0001
0000 0010 0100 0110
0001b 0011b 0101b 0111b
P1 = 0010 P3
= 0010 P5 = 0010 P7 = 0010
0001 0011 0101 0111
0000b 0010b 0100b 0110b
Portanto os processadores P0 com P1, P2
com P3, P4 com P5 e P6 com P7, farão a troca entre si da seguinte maneira: O
processado de menor índice envia seus valores maiores do que seu valor médio
para seu parceiro e recebe do seu parceiro os valores menores do que o valor
médio. Valor médio: P0 = 20; P2 = 38; P4 = 60; P6 = 65. Daí fica:
P1 25 28 30 21 22 P3 44 P5 P7 90 70
P0 2 3 20 4 6 8 10 14 15
18 P2 35 38
32 P4 60 55 P6
65
Cada processador realiza o seqüencial quicksort. Fica ordenado da seguinte maneira:
P1 21 22 25 28 30 P3 44 P5 P7 70 90
P0 2 3 4
6 8 10
14 15 18
20 P2 32 35
38 P4 55 60
P6 65
Finalmente, os processadores devolvem para Lista os valores contidos em cada processador.
2 |
3 |
4 |
6 |
8 |
10 |
14 |
15 |
18 |
20 |
21 |
22 |
25 |
28 |
30 |
32 |
35 |
38 |
44 |
55 |
60 |
65 |
70 |
90 |
Conclusão.
Verifica-se que se trata de um algoritmo eficaz, podendo efetuar implementação. Na realidade é um ótimo estudo para o entendimento da Programação Paralela e Distribuída.
Referência Bibliográfica.
Michael J. Quinn. Parallel
Programming in C with MPI and OpenMP. McGRAW-HILL, 2003.