implementações paralelas para os problemas do fecho transitivo e

118
U NIVERSIDADE F EDERAL DE G OIÁS I NSTITUTO DE I NFORMÁTICA ROUSSIAN D I R AMOS A LVES G AIOSO Implementações Paralelas para os Problemas do Fecho Transitivo e Caminho Mínimo APSP na GPU Goiânia 2014

Upload: vodat

Post on 07-Jan-2017

220 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Implementações Paralelas para os Problemas do Fecho Transitivo e

UNIVERSIDADE FEDERAL DE GOIÁSINSTITUTO DE INFORMÁTICA

ROUSSIAN DI RAMOS ALVES GAIOSO

Implementações Paralelas para osProblemas do Fecho Transitivo eCaminho Mínimo APSP na GPU

Goiânia2014

Page 2: Implementações Paralelas para os Problemas do Fecho Transitivo e

UNIVERSIDADE FEDERAL DE GOIÁS

INSTITUTO DE INFORMÁTICA

AUTORIZAÇÃO PARA PUBLICAÇÃO DE DISSERTAÇÃO

EM FORMATO ELETRÔNICO

Na qualidade de titular dos direitos de autor, AUTORIZO o Instituto de Infor-mática da Universidade Federal de Goiás – UFG a reproduzir, inclusive em outro formatoou mídia e através de armazenamento permanente ou temporário, bem como a publicar narede mundial de computadores (Internet) e na biblioteca virtual da UFG, entendendo-seos termos “reproduzir” e “publicar” conforme definições dos incisos VI e I, respectiva-mente, do artigo 5o da Lei no 9610/98 de 10/02/1998, a obra abaixo especificada, sem queme seja devido pagamento a título de direitos autorais, desde que a reprodução e/ou publi-cação tenham a finalidade exclusiva de uso por quem a consulta, e a título de divulgaçãoda produção acadêmica gerada pela Universidade, a partir desta data.

Título: Implementações Paralelas para os Problemas do Fecho Transitivo e CaminhoMínimo APSP na GPU

Autor(a): Roussian Di Ramos Alves Gaioso

Goiânia, 13 de Fevereiro de 2014.

Roussian Di Ramos Alves Gaioso – Autor

Wellington Santos Martins – Orientador

Page 3: Implementações Paralelas para os Problemas do Fecho Transitivo e

ROUSSIAN DI RAMOS ALVES GAIOSO

Implementações Paralelas para osProblemas do Fecho Transitivo eCaminho Mínimo APSP na GPU

Dissertação apresentada ao Programa de Pós–Graduação doInstituto de Informática da Universidade Federal de Goiás,como requisito parcial para obtenção do título de Mestre emCiência da Computação.

Área de concentração: Ciência da Computação.

Orientador: Prof. Wellington Santos Martins

Goiânia2014

Page 4: Implementações Paralelas para os Problemas do Fecho Transitivo e

ROUSSIAN DI RAMOS ALVES GAIOSO

Implementações Paralelas para osProblemas do Fecho Transitivo eCaminho Mínimo APSP na GPU

Dissertação defendida no Programa de Pós–Graduação do Instituto deInformática da Universidade Federal de Goiás como requisito parcialpara obtenção do título de Mestre em Ciência da Computação, aprovadaem 13 de Fevereiro de 2014, pela Banca Examinadora constituída pelosprofessores:

Prof. Wellington Santos MartinsInstituto de Informática – UFG

Presidente da Banca

Prof. Hugo Alexandre Dantas NascimentoInstituto de Informática – UFG

Prof. Edson Norberto CárceresFaculdade de Computação –UFMS

Prof. Thierson Couto RosaInstituto de Informática – UFG

Page 5: Implementações Paralelas para os Problemas do Fecho Transitivo e

Todos os direitos reservados. É proibida a reprodução total ou parcial dotrabalho sem autorização da universidade, do autor e do orientador(a).

Roussian Di Ramos Alves Gaioso

Graduou-se em Sistema de Informação na UEG - Universidade Estadual deGoiás. Ao longo da carreira, foi Desenvolvedor e Analista de Sistema.

Page 6: Implementações Paralelas para os Problemas do Fecho Transitivo e

Aos meus pais, irmãos e esposa pelo apoio e alegria em todos os momentos.Ao orientador pela paciência no decorrer deste trabalho.

Page 7: Implementações Paralelas para os Problemas do Fecho Transitivo e

Agradecimentos

Aos meus pais e irmãos, que merecem os créditos pelo apoio que sempre meincentivaram a atingir meus objetivos.

A minha esposa que me apoiou e motivou na concretização do presente trabalho.Ao professor Wellington, que dedicou de seu tempo e compartilhou seu conhe-

cimento, cujas orientações permitiram que este trabalho se concretizasse.A todos, os meus sinceros agradecimentos. Muito obrigado.

Page 8: Implementações Paralelas para os Problemas do Fecho Transitivo e

O primeiro e indispensável passo para obter as coisas que você deseja davida é decidir o que você quer.

Ben Stein,Utilizando UML e Padrões.

Page 9: Implementações Paralelas para os Problemas do Fecho Transitivo e

Resumo

Gaioso, Roussian Di Ramos Alves. Implementações Paralelas para os Proble-mas do Fecho Transitivo e Caminho Mínimo APSP na GPU. Goiânia, 2014.116p. Dissertação de Mestrado. Instituto de Informática, Universidade Federalde Goiás.

Este trabalho apresenta implementações paralelas baseadas em Graphics Processing Unit

(GPU) para os problemas da identificação dos caminhos mínimos entre todos os paresde vértices e do fecho transitivo em um grafo. As implementações são baseadas nosprincipais algoritmos sequenciais e tiram o máximo proveito da arquitetura multithreaded

das GPUs atuais. Nossa solução reduz a comunicação entre a Central Processing Unit

(CPU) e a GPU, melhora a utilização dos Streaming Multiprocessors (SMs) e faz umuso intensivo de acesso aglutinado em memória para otimizar o acesso de dados dografo. As vantagens dessas implementações propostas são demonstradas por vários grafosgerados aleatoriamente utilizando a ferramenta GTgraph. Grafos contendo milhares devértices foram gerados e utilizados nos experimentos. Nossos resultados confirmam queimplementações baseadas em GPU podem ser viáveis mesmo para algoritmos de grafoscujo acessos à memória e distribuição de trabalho são irregulares e causam dependênciade dados.

Palavras–chave

GPU, GPGPU, CUDA, Teoria dos Grafos, Fecho Transitivo, APSP, CaminhoMínimo, BFS, Warshall, Dijkstra, FloydWarshall, Paralelismo

Page 10: Implementações Paralelas para os Problemas do Fecho Transitivo e

Abstract

Gaioso, Roussian Di Ramos Alves. Parallel Implementations for TransitiveClosure and Minimum Path APSP Problems in GPU. Goiânia, 2014. 116p.MSc. Dissertation. Instituto de Informática, Universidade Federal de Goiás.

This paper presents a Graphics Processing Unit (GPU) based parallels implementationsfor the All Pairs Shortest Paths and Transitive Closure problems in graph. The imple-mentations are based on the main sequential algorithms and takes full advantage of thehighly multithreaded architecture of current manycore GPUs. Our solutions reduces thecommunication between CPU and GPU, improves the Streaming Multiprocessors (SMs)utilization, and makes intensive use of coalesced memory access to optimize graph dataaccess. The advantages of the proposed implementations are demonstrated for severalgraphs randomly generated using the widely known graph library GTgraph. Graphs con-taining thousands of vertices and different edges densities, varying from sparse to com-plete graphs, were generated and used in the experiments. Our results confirm that GPUimplementations can be competitive even for graph algorithms whose memory accessesand work distribution are both irregular and data-dependent.

Keywords

GPU, GPGPU, CUDA, Graph Theory, Transitive Closure, APSP, Warshall, BFS,FloydWarshall, Dijkstra, Parallel, Minimum Path, Parallelism.

Page 11: Implementações Paralelas para os Problemas do Fecho Transitivo e

Sumário

Lista de Figuras 11

Lista de Tabelas 14

Lista de Algoritmos 16

Lista de Abreviaturas e Siglas 17

1 Introdução 19

2 Computação Paralela 222.1 Conceitos Preliminares 222.2 Taxonomia de Computadores 252.3 Modelos Computacionais Paralelos 30

2.3.1 Parallel Random Acess Machine (PRAM) 322.3.2 Modelos de Rede 342.3.3 Bulk Synchronous Parallel (BSP) 34

2.4 Medidas de Desempenho 36

3 Unidade de Processamento Gráfico para Propósito Geral 393.1 Conceitos Preliminares 393.2 Hardware CUDA 403.3 Modelo de Programação CUDA 443.4 Otimizações em CUDA 48

4 Algoritmos em Grafos 514.1 Conceitos Preliminares 514.2 Fecho Transitivo 53

4.2.1 Algoritmo Sequencial de Warshall 564.2.2 Algoritmo Sequencial de Busca em Largura 574.2.3 Outras Abordagens 59

4.3 Caminho Mínimo entre Todos os Pares de Vértices 614.3.1 Algoritmo Sequencial de Floyd-Warshall 624.3.2 Algoritmo Sequencial de Dijkstra 634.3.3 Outras Abordagens 65

Page 12: Implementações Paralelas para os Problemas do Fecho Transitivo e

5 Implementações Propostas 695.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 695.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 785.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 85

6 Resultados Experimentais 926.1 Materiais e Métodos 926.2 Resultados dos Algoritmos de Fecho Transitivo 93

6.2.1 Resultados das Implementações Paralelas do Warshall 936.2.2 Resultados da Implementação Paralela do BFS 946.2.3 Análise Comparativa dos Resultados das Implementações Paralelas Propostas 98

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 1006.3.1 Resultados das Implementações Paralelas do Floyd-Warshall 1006.3.2 Resultados das Implementações Paralelas de Dijkstra 1016.3.3 Análise Comparativa dos Resultados das Implementações Paralelas Propostas 104

7 Conclusão 111

Referências Bibliográficas 113

Page 13: Implementações Paralelas para os Problemas do Fecho Transitivo e

Lista de Figuras

2.1 Execução de um algoritmo sequencial em uma Unidade de Processamento. 232.2 Execução de um algoritmo paralelo em várias Unidades de Processamento. 232.3 Modelo Computacional de von Neumann 242.4 Taxonomia de Flynn 252.5 Categoria SISD 262.6 Categoria SIMD 262.7 Categoria MIMD 272.8 Taxonomia de Flynn-Johnson 282.9 Arquitetura Acesso Uniforme à Memória (UMA) 292.10 Arquitetura Acesso Não-Uniforme à Memória (NUMA) 302.11 Arquitetura de Acesso Não-Uniforme à Memória (NUMA) 302.12 Máquina de Acesso Aleatório (RAM) 312.13 Máquina Paralela de Acesso Aleatório (RAM) 322.14 Modelo Bulk Synchronous Parallel (BSP) 352.15 Super-etapa do BSP: computação, comunicação e sincronização. 362.16 Lei de Amdahl [15] 372.17 Lei de Gustafson [15] 38

3.1 Arquitetura CPU/GPU simplificada [40]. 403.2 Estrutura do Processador de Fluxo (SP). 413.3 Estruturas dos SMs de CUDA, sendo que (a) é a geração Kerpler com

192 SPs e (b) é a geração Fermi com 32 SPs 423.4 Arquitetura Geral dos SMs [8]. 443.5 Execução de um programa CUDA [21]. 453.6 Hierarquia de threads no modelo de programação CUDA. 463.7 Blocos de threads da grade executando o mesmo código, mas com fluxos

diferentes. 473.8 Hierarquia de Memória do Modelo de Programação CUDA [9]. 483.9 Algoritmo de soma de vetores em CUDA. 49

4.1 Exemplos de dois grafos, sendo o grafo (a) não-direcionado e o grafo (b)direcionado 52

4.2 Representações de Grafos: (a) Matriz de Adjacência e (b) Lista de Adja-cência 53

4.3 Representação de grafo usando a lista compactada. 544.4 Grafo original em (a) e seu respectivo fecho transitivo em (b). 544.5 Sequência de etapas para resolver o problema do Fecho Transitivo 554.6 Exemplo de execução do BFS em um grafo 594.7 Divisão de uma matriz de distâncias em ladrilhos. 65

Page 14: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.8 Solução sequencial proposta por Venkataraman et al. [39]. 664.9 Solução paralela do algoritmo Floyd-Warshall proposta por Harish e Na-

rayanan [16]. 664.10 Visão geral do algoritmo proposto por Katz e Kider [20]. 68

5.1 Exemplo de ocupação intensiva dos SM’s: (a) Número de blocos dethread; (b) Divisão das partes da matriz por bloco de threads. 71

5.2 Iterações da implementação paralela descrita no Algoritmo 5.2 755.3 Primeira etapa da Fase 1 da implementação do Floyd-Warshall. 765.4 Segunda etapa da Fase 1 da implementação do Floyd-Warshall. 775.5 Paralelização de vários algoritmos BFS na GPU 795.6 Listas das Fronteiras do algoritmo paralelo proposto 805.7 Divisão de trabalhos entre as warps 815.8 Paralelização de visitas a vértices contidos nas fronteiras 825.9 Comunicação entre as threads ativas de uma warp sobre uma parte da

lista da fronteira atual 845.10 Execução simultânea de vários algoritmos de Dijkstra. 855.11 Atualização do menor caminho entre fronteiras. 86

6.1 Ganho computacional da implementação proposta com ocupação inten-siva em relação às implementações de Harish e Narayanan [16]; e Katz eKider [20]. 94

6.2 Ganhos computacionais das implementações paralelas do Warshall emrelação ao sequencial. 95

6.3 Ganho computacional da implementação paralela do BFS em relação aoalgoritmo sequencial com grafos randômicos de 600|V | e 6000|V | arestas. 96

6.4 Tempo computacional (s) da implementação paralela do BFS com grafosrandômicos de 6|V |, 600|V | e 6000|V | arestas. 98

6.5 Tempo computacional (s) do algoritmo sequencial do BFS com grafosrandômicos de 6000|V | arestas. 99

6.6 Comparação de tempo entre os algoritmos paralelos propostos doWarshall em Blocos e do BFS com grafos randômicos (gerador Random). 100

6.7 Comparação de tempo entre os algoritmos paralelos propostos doWarshall com Ocupação Intensiva e do BFS com grafos randômicos (ge-rador Random). 101

6.8 Comparação de tempo entre os algoritmos paralelos propostos doWarshall em Blocos e do BFS com grafos de diâmetro ≤ 10 (R-MAT). 102

6.9 Comparação de tempo entre os algoritmos paralelos propostos doWarshall com Ocupação Intensiva e do BFS com grafos de diâmetro≤ 10(R-MAT). 104

6.10 Tempo de execução dos algoritmos para grafos com 1024, 2048, 3072,4096 e 5120 vértices. 106

6.11 Tempo de execução dos algoritmos para grafos com 6144, 7168, 8192 e16384 vértices. 106

6.12 Ganhos de speedup em relação às implementações de Harish et al. [16]e Katz e Kider [20]. 107

6.13 Ganhos de speedup em relação à implementação sequencial do algoritmoFloyd-Warshall. 107

Page 15: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.14 Tempo computacional (s) do algoritmo sequencial do Dijkstra com grafosde 6000|V | arestas. 108

6.15 Ganho computacional da implementação paralela do Dijkstra com grafosrandômicos de 6|V |, 600|V | e 6000|V | arestas. 109

6.16 Tempo computacional (s) da implementação paralela do Dijkstra comgrafos randômicos de 6|V |, 600|V | e 6000|V | arestas. 109

6.17 Comparação de tempo entre os algoritmos paralelos propostos do Floyd-Warshall com Dijkstra paralelo com grafos randômicos (Random). 110

6.18 Comparação de tempo entre os algoritmos paralelos propostos do Floyd-Warshall com Dijkstra paralelo com grafos de diâmetro ≤ 10 (R-MAT). 110

Page 16: Implementações Paralelas para os Problemas do Fecho Transitivo e

Lista de Tabelas

6.1 Tempo computacional (s) das implementações paralelas do algoritmo doWarshall 94

6.2 Tempo computacional (s) e ganho computacional para grafos randômicoscom 1024, 2048, 3072, 4096 vértices e 6|V | arestas. 95

6.3 Tempo computacional (s) e ganho computacional para grafos randômicoscom 5120, 6144, 7168, 8192 vértices e 6|V | arestas. 96

6.4 Tempo computacional (s) e ganho computacional com grafos randômicosde 600|V | arestas. 97

6.5 Tempo computacional (s) e ganho computacional com grafos randômicosde 6000|V | arestas. 97

6.6 Tempo computacional (s) e ganho computacional para grafos com 1024,2048, 3072, 4096 vértices, 6|V | arestas e com diâmetro < 10. 97

6.7 Tempo computacional (s) e ganho computacional para grafos com 5120,6144, 7168, 8192 vértices, 6|V | arestas e com diâmetro < 10. 97

6.8 Tempo computacional (s) e ganho computacional com grafos de |600V |arestas e com diâmetro < 10. 97

6.9 Tempo computacional (s) e ganho computacional com grafos de |6000V |arestas e com diâmetro < 10. 98

6.10 Tempo computacional (s) das implementações paralelas do algoritmo doFloyd-Warshall 100

6.11 Tempo computacional (s) e ganho computacional para grafos randômicoscom 1024, 2048, 3072, 4096 vértices e 6|V | arestas. 103

6.12 Tempo computacional (s) e ganho computacional do algoritmo paralelo deDijkstra para grafos randômicos com 5120, 6144, 7168, 8192 vértices e6|V | arestas. 103

6.13 Tempo computacional (s) e ganho computacional do algoritmo paralelo deDijkstra com grafos randômicos de 600|V | arestas. 103

6.14 Tempo computacional (s) e ganho computacional do algoritmo paralelo deDijkstra com grafos randômicos de 6000|V | arestas. 103

6.15 Tempo computacional (s) e ganho computacional do algoritmo paralelo deDijkstra para grafos com 1024, 2048, 3072, 4096 vértices, 6|V | arestas ecom diâmetro < 10. 105

6.16 Tempo computacional (s) e ganho computacional do algoritmo paralelo deDijkstra para grafos com 5120, 6144, 7168, 8192 vértices, 6|V | arestas ecom diâmetro < 10. 105

6.17 Tempo computacional (s) e ganho computacional do algoritmo paralelo deDijkstra para grafos de |600V | arestas e com diâmetro < 10. 105

Page 17: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.18 Tempo computacional (s) e ganho computacional do algoritmo paralelo deDijkstra com grafos de |6000V | arestas e com diâmetro < 10. 105

Page 18: Implementações Paralelas para os Problemas do Fecho Transitivo e

Lista de Algoritmos

2.1 Algoritmo de Soma de Vetores no modelo PRAM 33

4.1 Algoritmo Sequencial do Warshall 564.2 Algoritmo Sequencial BFS 584.3 Algoritmo Sequencial BFS para o Problema do Fecho Transitivo 594.4 Código CPU - Implementação do Algoritmo BFS proposta por Harish e

Narayanan [16] 604.5 Algoritmo em BSP/CGM para Computar o Fecho Transitivo proposto por

Cáceres et al [7] 614.6 Algoritmo Sequencial do Floyd-Warshall 634.7 Algoritmo Sequencial de Dijsktra 644.8 Algoritmo Sequencial de Dijkstra para o Problema APSP 654.9 Código CPU - Implementação Paralela do Algoritmo do Floyd-Warshall

proposta por Harish et al. [16]. 67

5.1 Código da CPU - Implementação Paralela Proposta dos Algoritmos doWarshall e Floyd-Warshall em Blocos 72

5.2 Código da GPU - Implementação Paralela Proposta dos Algoritmos doWarshall e Floyd-Warshall em Blocos 73

5.3 Função - Cálculo do Fecho Transitivo de um Grafo 735.4 Função - Cálculo do Caminho Mínimo Entre Todos os Pares de Vértices

de um Grafo (APSP) 745.5 Código da CPU - Implementação Proposta dos Algoritmos Warshall e

Floyd-Warshall 785.6 Implementação Paralela Proposta do BFS 835.7 Implementação Paralela Proposta do Dijkstra 895.8 Relaxamento das Arestas 90

Page 19: Implementações Paralelas para os Problemas do Fecho Transitivo e

Lista de Abreviaturas e Siglas

APSP All-Pair Shortest-Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19SSSP Single-Source Shortest-Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19GPU Graphics Processing Unit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19API Application Programming Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20CAM Computer Aided Manufacturing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20CAD Computer Aided Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20CPU Central Processing Unit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20CUDA Compute Unified Device Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .20OpenCL Open Computing Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20ALU Arithmetic Logic Unit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24ILP Instruction-level Parallelism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24T LP Thread-level Parallelism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24SIMD Single Instruction Multiple Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25SISD Single Instruction Single Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25MISD Multiple Instruction Single Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26MIMD Multiple Instruction Multiple Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26SPMD Single Program, Multiple Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27DMMP Distributed Memory, Message Passing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28DMSV Distributed Memory, Shared Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28GMMP Global Memory, Message Passing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28GMSV Global Memory, Shared Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28SMP Symmetric Multi-Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28UMA Uniform Memory Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28NUMA Non-Uniform Memory Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29CC−NUMA Cache Coherence Non-Uniform Memory Access . . . . . . . . . . . . . . . . . . . . . 29PC Program Counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31RAM Random-Access Machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31PRAM Parallel Random Acess Machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32CREW Concurrent Read Exclusive Write . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33CRCW Concurrent Read Concurrent Write . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33ERCW Exclusive Read Concurrent Write . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33EREW Exclusive Read Exclusive Write . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33MMD Program Counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34BSP Bulk Synchronous Parallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34CGM Coarse Grained Multicomputer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35GPGPU General Purpose Graphics Processing Unit . . . . . . . . . . . . . . . . . . . . . . . . . . 39SM Multiprocessador de Fluxo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40SP Processador de Fluxo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .40FMA Fused Multiply-add . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Page 20: Implementações Paralelas para os Problemas do Fecho Transitivo e

FPU Floating Point Unit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41MAD Multiply Add . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41SFU Special Function Unit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42BFS Breadth-First Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55DFS Depth-First Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55FIFO First-In First Out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .57SDSP Single-Destination Shortest-Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62SPSP Single-Pair Shortest-Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

Page 21: Implementações Paralelas para os Problemas do Fecho Transitivo e

CAPÍTULO 1Introdução

Problemas relacionados a grafos são geralmente complexos e exigem bastanteesforço computacional para sua resolução. O problema do fecho transitivo e o problemado caminho mínimo, quando aplicado a todos os pares de vértices num grafo conexo edirigido, são exemplos desta dificuldade.

Na Teoria dos Grafos existem alguns algoritmos sequenciais capazes de identi-ficar os caminhos mínimos entre todos os pares de vértices existentes (APSP) e o fechotransitivo num grafo conexo, dirigido. Na literatura, destacam-se dois deles para o pro-blema do caminho mínimo entre todos os pares de vértices: (1) o de Donald B. Johnson,proposto em 1977; e (2) o de Robert Floyd e Stephen Warshall (Floyd-Warshall), pu-blicado em 1962. Um outro método simples de resolver o problema APSP é através desoluções do problema do menor caminho a partir de uma única origem (SSSP), quandoaplicados a todos os pares. Exemplo dessa categoria de algoritmos é o algoritmo propostopor Dijkstra (1959). Já em relação ao problema do fecho transitivo, há dois tipos de so-luções: (1) por multiplicação de matrizes, por exemplo, o algoritmo de Warshall (1962);e (2) por buscas, por exemplo, busca em largura e busca em profundidade. Todos pos-suem uma ampla variedade de aplicações em diversas áreas, tais como: bioinformática,roteamento de tráfego em redes, sistemas distribuídos, ou qualquer problema que possaser representado por um grafo no qual as arestas sejam ponderadas e cujos valores sejamlinearmente acumulados à medida que a rede é percorrida [11].

Os algoritmos Warshall e Floyd-Warshall são considerados eficientes em relaçãoao espaço de armazenamento por possuir uma complexidade de espaço O(|V |2). Entre-tanto, possui uma complexidade de tempo O(|V |3), onde |V | representa o número devértices do grafo. Devido a essa complexidade cúbica de tempo, trabalhos recentes têmexplorado o uso de GPUs (Unidade de Processamento Gráfico ) na tentativa de obtersoluções paralelas de melhor desempenho. Dentre eles destacam-se as implementaçõespropostas por Dehne et al. [11], Aini et al. [1], Harish et al. [16], Katz e Kider [20], Ridiet al. [32], Borgwardt et al. [6] e Jian et al. [24]. Por outro lado, acelerado com o heap de

Fibonacci, o algoritmo de Dijkstra pode identificar o menor caminho do problema APSPem tempo de O(|V ||E|+ |V |2log|V |), onde |E| representa o número de arestas do grafo.

Page 22: Implementações Paralelas para os Problemas do Fecho Transitivo e

20

GPUs são microprocessadores dedicados a realizar operações ligadas a aplicati-vos gráficos 2D e 3D. Dentre tais aplicativos, podem ser citados os de Computer Aided

Design (CAD ), Computer Aided Manufacturing (CAM), jogos, interface gráfica com ousuário. Graças à sua arquitetura altamente paralelizada e especializada, são muito maiseficientes na manipulação de gráficos que as Central Processing Units (CPUs ), projeta-das para a execução de código sequencial. Em essência, as GPUs consistem de vários nú-cleos primariamente focados em operações de ponto flutuante, massivamente usados nasfunções gráficas dos algoritmos de arte-finalização (ou rendering). A grande quantidadedestes microchips, trabalhando em paralelo, é o que permite o alto poder computacionalde tais processadores.

A partir do ano 2000, as GPUs incorporaram técnicas de pixel shading, ondecada pixel pode ser processado por um pequeno programa que inclui texturas adicionais,o que é feito de maneira similar com vértices geométricos, antes mesmo de serem pro-jetados na tela. A fabricante de GPUs NV IDIA R© foi a primeira a produzir placas comtais características [9]. À medida que as GPUs evoluem, mais flexibilidade de programa-ção é introduzida, adicionando suporte a programas maiores e de maior complexidade(incluindo controladores de fluxo, tais como: laços, sub-rotinas, branches, etc.), mais re-gistradores e aumento na precisão numérica.

Hoje em dia, devido ao altíssimo poder computacional oriundo de uma arqui-tetura intrinsecamente paralela, as GPUs são capazes de manipular enormes cargas detrabalho, com programação flexível e facilitada por meio do uso de diversas Application

Programming Interfaces (APIs) disponíveis. Isto têm motivado pesquisadores ao redor domundo a conceberem algoritmos para execução sob estes equipamentos, usando-as comocoprocessadores matemáticos para computação de propósito geral.

De forma correspondente à evolução do hardware, novos modelos de programa-ção capazes de aproveitar o poder desta nova tecnologia têm sido elaborados, destacando-se CUDA (Compute Unified Device Architecture) [9] e OpenCL (Open Computing Lan-

guage) [37]. Em ambos, devido a uma ampla disponibilidade de APIs, a implementaçãode aplicações paralelas eficientes é facilitada, embora as GPUs ainda sejam mais difíceisde serem programadas que as CPUs. Diferentemente da paralelização em CPUs, a or-ganização e o número de threads são gerenciadas manualmente pelo programador. Valedestacar que CUDA foi a primeira arquitetura e interface de programação a permitir queas GPUs pudessem ser usadas para aplicações não gráficas [9].

Este trabalho tem o objetivo de investigar o uso de paralelismo em arquiteturasda GPU para solução de problemas envolvendo grafos, cuja paralelização eficiente édifícil de alcançar. Isso é realizado através de novas soluções paralelas desenvolvidas eanalisadas no modelo de programação disponibilizado pela arquitetura da GPU. Soluçõesbaseadas em algoritmos sequenciais que têm a finalidade reduzir o tempo de resolução dos

Page 23: Implementações Paralelas para os Problemas do Fecho Transitivo e

21

problemas do fecho transitivo e do caminho mínimo entre todos os pares de vértices. Paraisso, essas novas abordagens precisam fazer o uso de propriedades intrínsecas da naturezaparalela da arquitetura para obter um desempenho escalável em relação ao tamanho doproblema solucionado e independência do modelo do hardware da GPU. Essas soluçõessão codificadas na linguagem CUDA, e um ambiente de experimentação com diferentesclasses de grafos gerados é criado para obtenção de resultados comparativos com osmelhores resultados atualmente disponíveis.

O restante deste trabalho está organizado da seguinte maneira: no capítulo 2,são descritos conceitos de computação paralela e medidas de desempenho. O capítulo 3descreve a arquitetura CUDA da GPU, em nível de hardware e software. O capítulo 4destina-se à apresentação de conceitos de grafos, algoritmos sequenciais e paralelos queabordaram o mesmo tema. Os detalhes das soluções propostas são descritos no capítulo 5.O capítulo 6 lista as características dos recursos computacionais e os métodos usadosdurante o processo de experimentaçãos e descreve os resultados obtidos. Por fim, ocapítulo 7 apresenta as conclusões do trabalho.

Page 24: Implementações Paralelas para os Problemas do Fecho Transitivo e

CAPÍTULO 2Computação Paralela

A computação paralela envolve o processamento de tarefas em vários processa-dores. Os processadores devem comunicar entre eles usando de alguma forma uma redede comunicação. Um processador pode ser um simples elemento ou pode envolver umprocessador superescalar. Já a rede de comunicação pode oferecer um gargalo se ela nãosuportar comunicações simultâneas entre vários pares de processadores. Assim, a estru-tura de uma máquina paralela envolve considerações sobre diversas características tantodos processadores quanto da rede de comunicação.

Neste capítulo, são apresentados conceitos essenciais da Computação Paralela. Oobjetivo é obter uma visão geral da evolução das arquiteturas de computadores paralelos edo processo de modelagem de algoritmos paralelos sobre modelos de máquinas abstratasparalelas. Com isso, uma breve introdução da computação paralela é feita na seção 2.1;algumas classificações de computadores são apresentadas na seção 2.2; modelos compu-tacionais paralelos são na seção 2.3; e por fim, medidas de desempenho de algoritmosparalelos são descritas na seção 2.4.

2.1 Conceitos Preliminares

Em geral, os algoritmos são desenvolvidos de forma sequencial para serem exe-cutados em um único núcleo de processamento. Esses algoritmos sequenciais são partici-onados e ordenados em uma sequência de instruções no momento do processamento. Aexecução pode ocorrer em máquinas com um ou vários núcleos. Neste último caso, a exe-cução é realizada somente em um, deixando os outros ociosos. Essa maneira de computaralgoritmos é conhecida como Computação Sequencial.

Uma outra maneira de computar, menos comum, é execução do algoritmoem paralelo, conhecida como Computação Paralela. A computação paralela exploraa concorrência em problemas computacionais. Concorrência é a execução simultâneade tarefas ou dados. Ela ocorre quando os problemas são decompostos em problemasmenores nos quais podem ser, seguramente, executados simultaneamente. A fim deexplorar essa concorrência, são necessários técnicas e linguagens de programação, nas

Page 25: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.1 Conceitos Preliminares 23

quais possibilitam estruturar os subproblemas de tal forma que sejam executados emmáquinas adequadas em paralelo, ou seja, máquinas com mais de uma unidade deprocessamento. Sendo assim, esses algoritmos são conhecidos como algoritmos paralelos[26].

Uma comparação entre execuções de algoritmos paralelos e sequencias são ilus-tradas nas Figuras 2.1 e 2.2, onde uma máquina com um processador e outra máquina comquatro, respectivamente, são usadas. O problema é particionado em pequenos conjuntosde instruções. Entretanto, somente um conjunto de instruções é executado em uma uni-dade do tempo na Figura 2.1. Em contrapartida, a Figura 2.2 apresenta várias instruçõessendo executadas em uma mesma unidade do tempo. Essa diferença na execução é devidoà quantidade de núcleos de processamento disponíveis na máquina e, principalmente, àestrutura do algoritmo. A quantidade de núcleo depende do modelo computacional doprocessador.

Figura 2.1: Execução de um algoritmo sequencial em uma Uni-dade de Processamento.

Figura 2.2: Execução de um algoritmo paralelo em várias Unida-des de Processamento.

Page 26: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.1 Conceitos Preliminares 24

A grande maioria dos processadores atuais trabalham baseadas no modelo com-putacional desenvolvido por von Neumann por volta de 1946 [8]. Esse modelo consisteem uma memória principal, uma única unidade central de processamento (CPU) e umarede de interconexão situada entre a CPU e a memória principal [30]. A CPU é compostapor uma unidade de controle e uma unidade lógica e aritmética (do inglês, Arithmetic

Logic Unit). A unidade de controle é responsável por decidir qual instrução no programadeve ser executada. A ALU é responsável por executar as operações lógico-aritméticas.Os dados, as instruções e as informações sobre o estado de execução do programa sãoarmazenados na memória e são transferidos via a rede de interconexão para a CPU. Asmáquinas que implementam esse modelo de von Neumann executam uma única instruçãoem um dado momento, sendo definidas como máquinas sequenciais [36].

A Figura 2.3 ilustra, de forma geral, a arquitetura proposta por von Neumann.Nela, fica visível uma separação entre a memória e o processador, sendo necessária umaconexão. Essa separação é conhecida por produzir um gargalo, uma vez que a rede deinterconexão determina a taxa de instruções e dados que poderão ser acessados pela CPU.A fim de resolver esse gargalo e, consequentemente, ganhar desempenho, os fabricantesfizeram várias alterações nessa arquitetura. Algumas delas são: adição de vários níveisde memória cache, paralelismo em nível de instrução (ILP, do inglês Instruction-level

Parallelism), paralelismo em nível de thread (TLP, do inglês Thread-level Parallelism).

Figura 2.3: Modelo Computacional de von Neumann

O ILP tenta executar instruções independentes e concorrentes. Para isso, adicionamúltiplos componentes ou unidades funcionais para execução de instruções simultâneas.Ele segue duas abordagens principais: pipeline, na qual várias unidades são arranjadasem estágios, e multiple-issue, na qual várias instruções podem ser executadas simultane-amente por meio de múltiplos e independentes pipelines de instruções [36].

O TLP tenta fornecer paralelismo através de execução simultânea de diferentesthreads. Essa técnica fornece um meio de continuar a execução de um programa enquanto

Page 27: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.2 Taxonomia de Computadores 25

a tarefa que está sendo executada está parada. Ao invés de procurar paralelismo na execu-ção de uma thread, ele simplesmente pode trocar a thread que está sendo trabalhada [36].

De acordo com Pacheco [30], o multiple-issue e pipeline podem ser consideradosparalelismo de hardware, uma vez que as unidades funcionais são replicadas. Contudo,essas formas de paralelismos não são, usualmente, visíveis ao programador. Por isso,podem ser consideradas como extensão do modelo básico de von Neumann.

2.2 Taxonomia de Computadores

Em Computação Paralela, a forma mais frequente de classificar arquiteturasde computadores é pela Taxonomia de Flynn (1966). Ela classifica os computadoresde acordo com o número de fluxo de instruções e de dados que um computador podegerenciar simultaneamente. Essa taxonomia pode ser visualizada na Figura 2.4. Nataxonomia, há quatro possibilidades: SISD, SIMD, MISD e MIMD, detalhadas a seguir.

Figura 2.4: Taxonomia de Flynn

SISD - Único fluxo de instruções, único fluxo de dados (modelo SISD, do inglêsSingle Instruction, Single Data): uma sequência de instruções é buscada e interpretadapor uma unidade de controle; há somente uma unidade de processamento que operasobre os dados armazenados em uma única memória. Essa categoria apresenta a estruturamais simples da taxonomia. O modelo clássico de von Neumann, que os tradicionaiscomputadores sequenciais implementam, está incluído nesta categoria. A Figura 2.5ilustra uma representação das máquinas SISDs na qual há uma entrada de instruções paraa unidade de controle e outra de dados para o processador.

SIMD - Único fluxo de instruções, múltiplos fluxo de dados (modelo SIMD,do inglês Single Instruction, Multiple Data): uma unidade de controle que busca einterpreta as instruções e as transmite para uma quantidade de processadores, ondecada qual possui seu próprio fluxo de dados como entrada. A Figura 2.6 ilustra umarepresentação de uma máquina SIMD com quatro processadores conectados a uma

Page 28: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.2 Taxonomia de Computadores 26

Figura 2.5: Categoria SISD

unidade de controle. As aplicações de processamento de sinal digital, imagem (gráfica)e multimídia são normalmente executadas em computadores que se enquadram nestacategoria, por exemplo, as Unidades de Processamento Gráfico (GPU).

Figura 2.6: Categoria SIMD

MISD - Múltiplos fluxos de instruções, único fluxo dado (modelo MISD, doinglês Multiple Instruction, Single Data). Uma sequência de dados é transmitida paravários processadores. Cada processador opera sobre um fluxo de instruções distinto. Ostipo de computadores que podem ser considerados nesta categoria são os computadoressistólicos.

MIMD - Múltiplos fluxos de instruções, múltiplos fluxos de dados (modeloMIMD, do inglês Multiple Instruction, Multiple Data). Cada elemento de processamentotem o seu próprio fluxo de instruções e de dados. Isso faz com que cada unidade deprocessamento seja completamente independente. A arquitetura MIMD é representada naFigura 2.7, onde uma máquina com quatro processadores conectados por uma unidadede controle diferente. Esta categoria é a mais geral das arquiteturas. Cada uma das outrascategorias podem ser representadas por esta [26].

Page 29: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.2 Taxonomia de Computadores 27

Figura 2.7: Categoria MIMD

As arquiteturas MIMD e SIMD são consideradas sistemas paralelos. Este traba-lha com paralelismo de dados e aquele trabalha tanto com paralelismo de dados quantode instruções. As primeiras máquinas paralelas continham a arquitetura SIMD, por exem-plo, o computador ILLIAC IV que foi o primeiro computador paralelo de grande escalaimplementado. O interesse inicial nas máquinas SIMD resultou nas características dasaplicações e na necessidade da economia. Pela perspectiva das aplicações, a programa-ção em linguagens SIMD era mais fácil e isso leva o hardware SIMD ser mais rentável.Já pelo lado da economia, os processadores totalmente independentes com grande ve-locidade eram muito caros naquela época, limitando, assim, qualquer sistema altamenteparalelo diferente da arquitetura SIMD.

Na arquitetura SIMD, há duas formas de execução: síncrono e assíncrono.Quando se recebe uma instrução, na versão síncrona, cada processador pode executarou ignorar a instrução baseado no estado local ou na condição de dependência. Comisso, existe um tempo de espera, ociosidade, na computação de instruções de condições,deixando ineficientes os processadores em tais computações. Por exemplo, a instruçãoi f − then− else é executada primeiro habilitando os processadores nos quais a condiçãoé satisfeita e, em seguida, o restante em que a condição else os satisfazem. Isso se torna opior caso para execução das máquinas SIMD.

Em contrapartida, os processadores na versão assíncrona do SIMD, conhecidacomo SPMD (Único Programa, Múltiplos Dados), executam sua própria copia do pro-grama. Com isso, a vantagem está em que cada unidade de processamento vai gastartempo no ramo da condição que lhe é pertinente, ou seja, cada processador pode seguirdiferentes caminhos através do programa. Isso traz para o SPMD flexibilidade e cober-tura para as mais importantes estruturas de algoritmos usados computação científica, porexemplo, decomposição geométrica, paralelismo de tarefas e padrões de dividir e con-quistar [26]. Entretanto, uma interação entre várias unidades de processamento e umaalta complexidade em cada processador, tornam-se uma desvantagem para essa classe de

Page 30: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.2 Taxonomia de Computadores 28

computadores.A arquitetura MIMD tornou-se a mais popular recentemente. Isso deve a vários

fatores: a flexibilidade de explorar várias formas de paralelismo; em ambientes de mul-tiusuários, há a facilidade de particionamento das tarefas entre os processadores; comocada processador é independente e assíncrono, torna-se mais fácil os algoritmos (progra-mas) serem escaláveis. Essa categoria inclui uma classe ampla de computadores. Comintuito de facilitar o entendimento, o E. E. Johnson propôs, em 1988, uma classificaçãobaseada na estrutura da memória (global ou distribuída) e no mecanismo utilizado para co-municação/sincronização (variáveis compartilhadas ou passagem de mensagem): GMSV,GMMP, DMSV e DMMP. De acordo com essa classificação (Figura 2.8), a classe MIMDem relação a memória pode ser dividida em memória global ou distribuída.

Figura 2.8: Taxonomia de Flynn-Johnson

No sistema de memória global, conhecida, também, por memória compartilhada,todos os processadores compartilham o mesmo espaço de endereçamento, isto é, cadaprocessador consegue ter acesso a todo espaço de memória da arquitetura. A comunicaçãoentre eles é realizada através de leitura/escrita de variáveis compartilhadas ou por enviode mensagens.

A forma mais comum desse sistema é chamada de SMPs (MultiprocessadoresSimétricos). Os processadores SMP são semelhantes em suas estruturas e desempenhamas mesmas funções. Além de compartilhar o mesmo espaço de endereço, esses processa-dores utilizam a mesma rede de conexão à memória e aos dispositivos de E/S. Sendo as-sim, qualquer processador tem mesmo tempo de acesso a qualquer endereço de memória.Motivo pelo qual essa arquitetura é conhecida por UMA (Acesso Uniforme à Memória).Nesse sistema, usualmente, é mais fácil de programar, pois o programador não precisa sepreocupar com diferença de tempo de acesso a diferentes endereços de memória. Tam-bém, não precisa distribuir as estrutura de dados entre os processadores.

A Figura 2.9 ilustra, de forma genérica, uma arquitetura SMP com dois chipssemelhantes, cada um tendo dois processadores ou núcleos, compartilhando uma redede interconexão à uma única memória. Nesta arquitetura, ao aumentar o número de

Page 31: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.2 Taxonomia de Computadores 29

processadores, o número de acesso à rede de interconexão à memória também aumenta.Logo, a largura de banda torna-se um fator limitante da arquitetura, ou seja, torna-seum gargalo de desempenho na arquitetura. Assim, os sistemas SMPs não são totalmenteescaláveis ao número de processadores.

Figura 2.9: Arquitetura Acesso Uniforme à Memória (UMA)

Um outro modelo de sistema de memória global é chamado de NUMA (AcessoNão-Uniforme à Memória). Como mostra a Figura 2.10, a memória é separada em blo-cos, cada bloco é conectado via barramento a um processador, como memória local. Noentanto, há apenas uma única memória endereçável, onde cada posição possui um únicoendereço no sistema inteiro. Isso faz com que memória continue sendo logicamente com-partilhada, porém, o acesso não é mais uniforme. Esse esquema é conhecido por Arquite-tura de Memória Compartilhada-Distribuída. Os endereços da memória podem ser maispróximos de alguns processadores do que outros. Entretanto, como resultado, o tempode acesso de um processador a um endereço de memória pode ter diferença significantedependendo de quanto perto está esse endereço do processador. Uma desvantagem dessaarquitetura está na grande quantidade de tráfego às posições de memória não-locais. To-davia, para diminuir esses efeitos, cada processador pode conter um conjunto de memóriacache, conhecida por CC-NUMA (NUMA com Coerência de Cache). Apesar de a memó-ria ser compartilhada, as arquiteturas NUMA mais modernas oferecem bibliotecas paraprogramação utilizando troca de mensagens, por exemplo, o computador Cray T3D.

No sistema de memória distribuída, ao contrário da memória compartilhada, cadaprocessador enxerga apenas seu espaço de memória. A comunicação ocorre sobre a redede interconexão por envio explícito de mensagem ou por funções especiais que fornecemacesso à memória de outro processador. A velocidade de comunicação nesta estruturadepende da tecnologia e da topologia usada na interconexão entre os processadores. Essesistema é extremamente escalável e permite a construção de projetos altamente paralelos.Os Clusters (sistemas compostos por vários computadores conectados por uma rede) estãoincluídos nesta classe. Eles fornecem alto desempenho e disponibilidade. A Figura 2.11

Page 32: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.3 Modelos Computacionais Paralelos 30

Figura 2.10: Arquitetura Acesso Não-Uniforme à Memória(NUMA)

ilustra uma representação do sistema de memória distribuída, onde há cinco pares deprocessador-memória conectados via rede de interconexão.

Figura 2.11: Arquitetura de Acesso Não-Uniforme à Memória(NUMA)

2.3 Modelos Computacionais Paralelos

Nesta seção, são apresentados modelos computacionais paralelos com o objetivode facilitar o entendimento dos algoritmos paralelos propostos neste trabalho. De acordocom Skillicorn e Talia [35], um modelo computacional paralelo é uma máquina abstrataparalela que fornece abstração e estabilidade do software. Abstração, pois disponibilizaoperações a nível mais elevado do que aquelas das arquiteturas subjacentes, o quesimplifica a estrutura do software e reduz a dificuldade da sua construção. Estabilidade,porque o software pode assumir uma interface padrão independente da evolução daarquitetura de computadores paralelos.

Uma vez que um modelo é apenas uma máquina abstrata, eles apresentammuitos níveis de abstração. Por consequência, muitos modelos de alto nível podemser emulados por outros modelos de níveis inferiores. Sendo assim, torna-se difícila comparação entre eles. Além disso, um modelo abstrato não é de grande interesse

Page 33: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.3 Modelos Computacionais Paralelos 31

prático, se um método eficiente não pode ser encontrado para a execução dos programasdescritos por ele. Um modelo para ser útil deve abordar duas questões, abstração eeficácia, conforme Skillicorn e Talia [35] e Skillicorn [34]. Não obstante, um bommodelo de computação paralela, segundo esses autores [35] [34], deve ter as seguintespropriedades: facilidade na programação, possuir metodologia de desenvolvimento desoftware, arquitetura independente, facilidade de entendimento, garantia de desempenhoe medidas de custos.

Nesta seção, mostramos vários modelos computacionais paralelas, cada qualcom um conjunto de características diferentes. Entre eles, estão: PRAM (2.3.1), Modelosde Rede (2.3.2) e BSP (2.3.3). Porém, antes de apresentar esses modelos, é importantefazer uma breve descrição do modelo computacional das máquinas sequenciais, chamadode Máquina de Acesso Aleatório, com intuito de simplificar a descrição dos modelosparalelos, pois o modelo sequencial é a base dos computadores atuais.

O modelo computacional comumente utilizado para representar computadoressequenciais (classe SISD) é conhecido como Máquina de Acesso Aleatório (RAM, doinglês Random-Access Machine). A RAM é composta principalmente por memória eUnidade de Controle. A memória é formada por uma sequência ilimitada de registradores,cada qual capaz de conter um valor inteiro. O registrador especial, chamado de Contadorde Programa (PC, do inglês Program Counter), possui o endereço da próxima instruçãoque deverá ser executada. Nenhuma hierarquia de memória é mencionada. Já na Unidadede Controle, encontra-se as instruções do programa [14]. Para executá-las, a RAM executauma instrução do programa a cada ciclo de clock, ou seja, a cada intervalo de tempo. Osdados de entrada do programa são armazenados nos registradores. O tempo de acesso aesses dados, localizados na memória, é constante, ou seja, a complexidade de tempo é deordem O(1). Uma representação do modelo RAM pode ser visualizado na Figura 2.12,onde os registradores são representados por R0, R1, R2 e o registrador Contador dePrograma é representado por PC.

Figura 2.12: Máquina de Acesso Aleatório (RAM)

Page 34: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.3 Modelos Computacionais Paralelos 32

2.3.1 Parallel Random Acess Machine (PRAM)

Um modelo computacional semelhante ao RAM, entretanto, utilizado para oscomputadores paralelos, é chamado de Máquina Paralela de Acesso Aleatório (PRAM)proposto por Karp e Ramachandran em 1990 [19]. O PRAM é um modelo básico paraanálise teórica da computação paralela [35].

De acordo com Goodmane e O’Rourke [14], o modelo PRAM é uma generaliza-ção das máquinas SISD, consequentemente, do modelo RAM. A PRAM é constituída porvários processadores independentes que, geralmente, são vistos como sendo da mesmaarquitetura e são conectados por uma memória compartilhada. Esses processadores tra-balham sincronizados em paralelo e utilizam a memória compartilhada para computaçãoe comunicação entre eles. Em consequência de serem RAM, os processadores possuemuma coleção ilimitada de células de memória, como pode ser visto na Figura 2.13. Nessafigura, os processadores RAM são representados por P0, P1, P2 e os registradores por R0,R1, R2.

Figura 2.13: Máquina Paralela de Acesso Aleatório (RAM)

As instruções dos programas correspondentes aos algoritmos representadosnuma PRAM são carregadas simultaneamente para todos processadores. Entretanto, de-vido à existência de vários processadores RAMs, uma única instrução é executada emuma unidade de tempo em cada processador. Sendo assim, o modelo PRAM fica contidona classe SIMD da classificação de Fynn (seção 2.2).

O algoritmo 2.1 mostra um exemplo de soma de dois vetores no modelo PRAM.A entrada são os vetores A, B e C de tamanho n. A soma de A e B é armazenada novetor C. O processamento paralelo está no laço da linha 1 que indica que todos osprocessadores com identificadores entre 0 e n− 1 irão processar a instrução contida nalinha 2 em paralelo. Com isso, o número de processadores necessário para a computaçãodo algoritmo 2.1 é da ordem de O(n) e a complexidade de tempo do algoritmo é O(1).

Page 35: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.3 Modelos Computacionais Paralelos 33

Algoritmo 2.1: Algoritmo de Soma de Vetores no modelo PRAM

Entrada: Vetores A[n], B[n] e C[n].Saída: Vetor C[n].

1 para todo 0≤ id ≤ n−1 em paralelo faça2 C[id] = A[id]+B[id];3 fim

Como há vários processadores trabalhando ao mesmo tempo, a operação de sin-cronização da PRAM pode resultar em acessos para uma mesma localidade na memóriacompartilhada. Os acessos podem ser de leitura ou escrita de acordo com as variantes domodelo PRAM. Os acessos concorrentes em uma mesma localidade podem ser aceitos ounão. Essas variantes são [34]:

1. Leitura Exclusiva e Escrita Exclusiva (EREW): não permite qualquer tipo deconcorrência para a mesma localidade;

2. Leitura Concorrente e Escrita Exclusiva (CREW): aceita somente acessos concor-rentes de leitura. Assim, todos os processadores participantes desses acessos obtémo mesmo valor;

3. Leitura Exclusiva e Escrita Concorrente (ERCW): permite concorrência em acessossomente de escrita;

4. Leitura Concorrente e Escrita Concorrente (CRCW): a concorrência dos dois tiposde acesso à memória (leitura e escrita) é permitido.Na leitura, um mesmo valor é passado para todos os processadores. Entretanto,há várias formas de tratar a concorrência de escrita. O modo comum permite aconcorrência de escrita se e somente se todos os processadores estão tentandoescrever o mesmo valor. No modo arbitrário, um valor arbitrário é escolhido paraser armazenado. No modo prioridade, o valor escolhido pertence ao processadorcom a maior prioridade. Por último, no modo combinação, o valor armazenado éuma combinação dos valores escritos.

O modelo PRAM é um dos principais modelos paralelos atuais, sendo muitoutilizado na literatura [34]. Pois, ele despreza qualquer problema da rede de comunicação,deixando o foco somente na computação e, consequentemente, apresenta uma facilidadede aprendizado. Em contrapartida, o modelo PRAM torna-se um modelo muito teórico.Para satisfazer as suas características, seria necessário uma grande área para atender amemória e a rede de comunicação para uma quantidade ilimitada de processadores. Comisso, o modelo PRAM torna-se impraticável sua implementação nos dias atuais.

Page 36: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.3 Modelos Computacionais Paralelos 34

2.3.2 Modelos de Rede

A insatisfação relacionada ao tempo de acesso constante a um espaço de ende-reçamento global levou ao desenvolvimento de muitas variantes do modelo PRAM. Umamudança fundamental de paradigma foi a introdução de modelos em que os módulos dememória são associados aos processadores, em outras palavras, modelos de memória dis-tribuída [25]. Dentre eles, destacam-se: o Modelo de Memória Distribuída (MMD) [38];o Modelo Postal (do inglês, Postal Model) [5]; o Modelo Atômico para Passagem deMensagem [23] e outros.

A maioria dos modelos iniciais após o modelo PRAM ignora a possibilidade deimpacto da topologia de rede, ou seja, da estrutura de comunicação de rede. Contudo, arede de interconexão dos computadores paralelos é o mecanismo que permite todos osprocessadores comunicarem-se com um aos outros e aos módulos de memória, comodescrito na Seção 2.2. Sendo assim, o tipo de topologia é um fator essencial para odesempenho dos computadores paralelos [35]. Visto isso, alguns modelos computacionaisparalelos começaram a priorizar o arranjo da topologia de rede de interconexão e, comisso, se destacaram, como é o caso do Modelo de Rede.

Geralmente, o modelo de rede atribui algumas quantidades de memórias locaispara cada processador e o custo de acesso a uma memória remota torna-se dependentetanto da topologia quanto do padrão de acesso. Assim, o modelo de rede incentiva proje-tos que sejam eficientes no mapeamento de dados quanto no roteamento da comunicaçãoentre os processadores. Existem, pelo menos, tantos modelos quanto propostas de topolo-gias de rede [25].

Os modelos de rede geralmente são representados por grafos. O desempenhodesses modelos envolve três características que são: diâmetro, largura de bisseção eescalabilidade. O diâmetro de uma rede de interconexão é a maior distância entre um parde elementos de processamento. Essa característica tem uma grande influência na latênciada rede, pois indica o tempo mais longo necessário para se comunicar entre qualquerpar de nós. A largura de bisseção é o menor número de ligações a serem removidaspara decompor o grafo em duas partes iguais. Se a largura de bisseção for grande, maisinformações podem ser trocadas entre duas metades do grafo de processadores e, assim,os problemas podem ser resolvidos mais rapidamente. A escalabilidade está na facilidadede adicionar mais processadores em um modelo de rede [19] [25] [31] [34] [35].

2.3.3 Bulk Synchronous Parallel (BSP)

Bulk Synchronous Parallel (BSP), proposto por Valiant em 1989, é um modeloque propõe alcançar todo potencial da programação paralela de forma natural e seme-lhante à programação sequencial. A máquina abstrata do BSP consiste em uma coleção

Page 37: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.3 Modelos Computacionais Paralelos 35

de processadores abstratos, cada qual com uma memória local, conectados por uma redede interconexão. A Figura 2.14 representa uma abstração desse modelo.

Figura 2.14: Modelo Bulk Synchronous Parallel (BSP)

O modelo BSP define propriedades da rede de interconexão por alguns parâ-metros arquiteturais. Essas propriedades são: o custo de invocar a sincronização, repre-sentado pela letra L, e o custo necessário para enviar um dado para dentro da rede decomunicação, representado pela letra g. O parâmetro L implica na latência da memóriae o g enfatiza a limitação da largura de banda [25]. Esses parâmetros são determinadosexperimentalmente por cada computador paralelo. Com esse método de abstrair as arqui-teturas paralelas, esse modelo torna-se altamente escalável e facilita a programação emmáquinas que implementam esse modelo [35].

Os algoritmos BSP consistem de p threads e de uma sequência de super-etapas,onde p é o número de processadores. Cada superetapa contém: um número de operaçõesde computação, um número de operações de comunicação e, por último, uma barreirade sincronização. No final de cada superetapa, os resultados de comunicações globaistornam-se visíveis na memória local de cada processador logo após a sincronização. AFigura 2.15 ilustra essa superetapa do BSP. Se o tempo de computação é w e o númeromáximo de mensagens enviadas ou recebidas por um processador é h, então o tempo deuma superetapa é dado por: t =w+hg+L. Com essas características, o BSP é consideradouma generalização do modelo PRAM. Quando a arquitetura BSP tem o valor de g muitopequeno (g = 1), ela torna-se uma PRAM [25] [35].

Um modelo semelhante ao BSP, é o modelo CGM (Coarse Grained Multicom-

puter) proposto por Dehne et al [10]. Uma máquina CGM consiste de um conjunto dep processadores, cada qual com uma memória de tamanho O(N

p ). O algoritmo em CGMconsiste em uma sequencia de rodadas, onde fases bem definidas de computação local e

Page 38: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.4 Medidas de Desempenho 36

Figura 2.15: Super-etapa do BSP: computação, comunicação esincronização.

comunicação global são realizadas. Normalmente, o tamanho da entrada nos algoritmosCGM é consideravelmente maior do que a quantidade de processadores, por isso o nomecoarse grained. O modelo considera somente dois parâmetros: o número de processadoresp e o tamanho da entrada N. Tanto a máquina quanto o algoritmo CGM são consideradoscasos especiais do modelo BSP [10].

2.4 Medidas de Desempenho

A principal razão de implementar um algoritmo paralelo está na obtenção deum desempenho superior ao do algoritmo sequencial. Esse ganho de desempenho (spee-

dup) é medido, normalmente, pelo tempo que leva para completar uma tarefa num únicoprocessador sobre o tempo necessário para completar a mesma tarefa em N processa-dores paralelos. Assim, o Speedup S(N) é definido usando N processadores paraleloscomo [30] [31]:

S(N) =Tp(1)Tp(N)

(2-1)

Onde Tp(1) é o tempo do algoritmo sobre um único processador e Tp(N)

é o tempo de processamento em processadores paralelos. Uma situação ideal seria aparalelização total do algoritmo. Com isso, teria-se Tp(N) = Tp(1)/N e a equação 2-1seria transformada em:

S(N) = N (2-2)

Page 39: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.4 Medidas de Desempenho 37

Essa situação ideal é díficil de acontecer, pois há vários fatores que influenciamno tempo de execução dos algoritmos paralelos, por exemplo, a sobrecarga de comuni-cação que inclui problemas na rede de intercomunicação, largura de banda, colisões namemória, gargalo da memória e entre outros.

Além disso, a lei de Amdahl proposta por Gene Amdahl (1967) [2] dita que todoproblema possui uma fração inerentemente sequencial, ou seja, uma parte que não podeser paralelizada. Com isso, o tempo do algoritmo paralelo depende do tempo da execuçãodessa fração sequencial. De acordo com a lei de Amdahl [2], o speedup é definido em:

S(N) =1

s+ pN

(2-3)

Sendo s o tempo da fração sequencial, p é o tempo da fração do algoritmoque pode ser paralelizada. A Figura 2.16 ilustra um problema para obter o speedup deacordo com Amdahl, equação 2-3. Partindo do pressuposto que o tempo do algoritmoexecutado em um processador é constante, sendo ele composto por frações sequencial(s) e paralela (p), e a fração paralelizável é fixa; o tempo do algoritmo sendo executadoem processador paralelo é a soma do tempo da fração sequencial s somado ao tempo dafração paralelizável p executada em N processadores. Com isso, duas conclusões podemser tiradas a partir da equação 2-3. Primeiro, quando a fração sequencial s é grande, o usode processadores paralelos tem pouco efeito. Segundo, quando N se aproxima do infinito,o speedup fica limitado ao inverso da fração sequencial s [2] [15].

Figura 2.16: Lei de Amdahl [15]

Enquanto a lei de Amdahl prediz que o tamanho da fração paralelizável é fixa enão depende do tamanho do problema, a lei de Gustafson (1988) [15] faz a observaçãode que o paralelismo aumenta em uma aplicação de acordo com o tamanho do problema.Segundo Gustafson, o tempo do algoritmo paralelo é constante e, em contrapartida, otempo do algoritmo sequencial é variável. Sendo assim, o speedup baseado na lei deGustafson é definido como:

Page 40: Implementações Paralelas para os Problemas do Fecho Transitivo e

2.4 Medidas de Desempenho 38

S(N) = s+N p (2-4)

A Figura 2.17 mostra a divisão de um problema de acordo com Gustafson [15]para obter o speedup. Nela, o tempo do algoritmo em paralelo é constante, considerandoisso, quanto maior é o problema, maior poderá ser a quantidade de processadores emparalelo para alcançar um speedup maior. Isso, implica que o speedup não fica limitadopelo tempo de execução da fração sequencial, como dito na lei de Amdahl.

Figura 2.17: Lei de Gustafson [15]

Neste capítulo, foram apresentados os principais conceitos e fundamentos dacomputação paralela que este trabalho utilizou para alcançar os objetivos: implementa-ções paralelas eficientes para o problema do fecho transitivo e do caminho mínimo entretodos os pares de vértices. No capítulo seguinte, uma explanação sobre a programaçãoparalela nas Unidades de Processamento Gráficas será mostrada.

Page 41: Implementações Paralelas para os Problemas do Fecho Transitivo e

CAPÍTULO 3Unidade de Processamento Gráfico paraPropósito Geral

A GPU (Unidade de Processamento Gráfico, do inglês Graphics Processing

Unit) é um coprocessador com uma arquitetura massivamente paralela especializado emcomputação gráfica. Normalmente, localiza-se em placas gráficas ou, em formas maissimples, integradas no chipsets da placa-mãe.

Inicialmente, a GPU foi desenvolvida com objetivo de processar tarefas gráficas.Embora, nos últimos anos, houve uma grande evolução na computação de aplicaçõesde propósitos gerais. A partir de então, ela ficou conhecida como GPGPU. Isso ocorreudevido ao crescimento computacional do hardware, aliado à flexibilidade de programaçãoe ao suporte de pontos flutuantes 32-bits IEEE adicionados na arquitetura da GPU.Com isso, houve a possibilidade de programar nas GPUs sem conhecimento prévio delinguagens complexas de shaders ou primitivas gráficas.

No presente capítulo, é descrita a estrutura tanto do hardware quanto do software

das GPUs da fabricante NVidia. Na seção 3.1, são apresentados conceitos iniciais deparalelismo e Unidades de Processamento Gráfico (GPU). Em seguida na seção 3.2,a arquitetura CUDA é detalhada. Por fim na seção 3.3, o modelo de programaçãoCUDA e suas otimizações são apresentados. Assim, objetiva-se a compreensão detalhadado modelo de programação oferecido por CUDA visando entender as implementaçõespropostas neste trabalho.

3.1 Conceitos Preliminares

A programação paralela baseia-se em processamento simultâneo de várias ta-refas e dados, é nesse ponto onde entra o papel das threads. Uma thread consiste numcódigo de um programa que está sendo executado com valores de variáveis e estrutu-ras de dados. Tanto na CPU quanto na GPU, o processamento da thread é sequencial.Contudo, a CPU é projetada para executar uma pequena quantidade de tarefas bastantecomplexas. Já a GPU é projetada para o processamento de uma grande quantidade de ta-

Page 42: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.2 Hardware CUDA 40

refas simples. Consequentemente, o suporte à thread é muito diferente entre CPU e GPU.Por exemplo, o número de registradores por núcleo é diferente para o suporte de troca decontexto das threads, sendo que a CPU possui um único banco de registradores e a GPU,muitos [8] [40] [21].

A GPU trabalha em conjunto com a CPU, sendo que, aquela muito dependentedesta. Pois, os dados inicialmente estão localizados na memória principal do computador,ou seja, na memória RAM da CPU e o recebimento de dados para ser armazenados namemória da GPU é realizado pela CPU. A Figura 3.1 ilustra, de maneira geral, a conexãoentre a GPU e a CPU, qual cada tem ligação direta ou alta proximidade com a suasrespectivas memórias. Porém, observa-se que a conexão entre elas é realizada por meio dobarramento PCI Express. Esse barramento oferece 16 GBytes/s de taxa de transferênciade dados, isto na versão 2.0. Já na versão 3.0, este valor chega a ser 32 Gbytes/s. Porsimplicidade, a Figura 3.1 omite o chipset que conecta a CPU com outros periféricos, taiscomo placas de vídeo [40].

Figura 3.1: Arquitetura CPU/GPU simplificada [40].

Entre as soluções de vídeo da NVidia, destaca-se a arquitetura de GPU conhecidacomo CUDA (Compute Unified Device Architecture). A arquitetura CUDA pode serdivida em duas partes: hardware e so f tware. Quanto à hardware, a arquitetura baseia-seem hierarquia tanto de processadores quanto de memória. Quanto à parte de so f tware,um modelo de programação é oferecido para o programador com intuito de abstrair osdetalhes do hardware. Para isso, uma hierarquia de hierarquia de threads é disponibiliza.Nas seções 3.2 e 3.3 é detalhada a arquitetura CUDA tanto à hardware e ao so f tware,respectivamente.

3.2 Hardware CUDA

A arquitetura CUDA contém três características chaves: Processadores de Fluxo(SP, do inglês Streaming Processors), Multiprocessadores de Fluxo (SM, do inglêsStreaming Multiprocessors) e uma hierarquia de memória (global, compartilhada eregistradores) [8] [40].

Page 43: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.2 Hardware CUDA 41

Os Processadores de Fluxo (SP), conhecidos também por CUDA cores, são de-finidos pela NVidia como núcleos de processamentos. Os SPs são constituídos, princi-palmente, por unidades lógica e aritmética (ULA) e unidade de ponto flutuante (FPU).As operações de ponto flutuante são executadas de acordo com o padrão IEEE 754-2008,que fornece suporte à instrução fused multiply-add (FMA) tanto para precisão simples (32bits) quanto precisão dupla (64 bits). Essa função melhora a instrução MAD (multiplicar-somar) e, é mais precisa do que as operações de adição e multiplicação executadas sepa-radamente. Tais instruções, FMA e MAD, são frequentemente utilizadas em computaçãográfica, álgebra linear e em aplicações cientificas. A Figura 3.2 ilustra, de forma simplifi-cada, a arquitetura do SP contendo uma ULA, FPU e registradores.

Figura 3.2: Estrutura do Processador de Fluxo (SP).

Os SPs são agrupados em estruturas que compartilham vários recursos dohardware. Essas organizações são chamadas de Multiprocessadores de Fluxo (SM),onde cada uma suporta uma grande quantidade de threads. Embora todos os SMs nãotrabalhem sincronizadamente, os SPs contidos em único SM executam no estilo dacategoria SIMD, onde processam de maneira síncrona. Por isso que o conjunto de SMs éclassificado na categoria MIMD, sendo assim a arquitetura CUDA também o é.

A quantidade de unidades de processamento em todos os SMs em uma GPU va-ria de geração em geração. Conforme Cook [8], essa quantidade é um aspecto chave daarquitetura, pois, dentre outros fatores, permite escalabilidade da GPU. Com essa esca-labilidade, o desempenho do hardware pode ser aumentado simplesmente combinandoo número de SMs e o número de CUDA Cores dentro de cada SM. A Figura 3.3 ilustraduas estruturas distintas de SMs. Observa-se a grande diferença de número de SPs entreelas. Em (a), está o SM da arquitetura mais atual, nomeado de SMX, da geração Kepler

de CUDA com 192 unidades de processamento; e em (b) está o SM da geração Fermi deCUDA com 32 unidades de processamento. Além dos SPs, os SMs contêm centenas de re-gistradores, memórias internas, escalonadores de warps, Unidades de Funções Especiais(SFU), e entre outros; como ilustrado na Figura 3.3.

Page 44: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.2 Hardware CUDA 42

Figura 3.3: Estruturas dos SMs de CUDA, sendo que (a) é a gera-ção Kerpler com 192 SPs e (b) é a geração Fermi com32 SPs

O motivo da existência de centenas de registradores dentro dos SMs é parapermitir trocas eficientes de contextos entre as threads, com o objetivo de maximizaro desempenho de processamento dos SMs. Isso leva o projeto da GPU a ter solução eficazsobre a latência contida no acesso à memoria, no qual gasta-se centenas de ciclos declock para buscar e armazenar dados na memória [40]. Quando um grupo de 32 threadsexecutando em um SM fica ocioso por acessar a memória ou por alcançar uma barreirade sincronização, outro grupo de threads é escalonado instantaneamente para o SM. Issoé realizado pelos escalonadores de warps contidos nos SMs.

Em relação à memória dentro de todos os SM, há uma divisão entre comparti-lhada e cache. Sendo que esta é utilizada pelo compilador e aquela é manuseada manual-mente pelo desenvolvedor.

Já as estruturas nos SMs chamadas de Unidades de Funções Especiais (SFU) sãoresponsáveis por executar instruções especiais a nível de hardware, como operações seno,cosseno, raiz quadrada e expoente.

A arquitetura CUDA tem alta vazão, pois, dentre outros fatores, há uma hierar-quia de memória disponível para todos os CUDA Cores. Essa hierarquia faz com que

Page 45: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.2 Hardware CUDA 43

as unidades de processamento acessem com menor frequência as memórias com maiorlatência. Quanto mais alto é o nível da memória nessa hierarquia, maior é a velocidadede acesso. No topo, encontra-se a memória privada dos SPs que é composta por um con-junto de milhares de registradores de tamanho 32-bits localizados dentro dos SMs. Essesregistradores, semelhantes aos das CPUs, estão próximos aos SPs e possuem o tempo deacesso muito semelhante ao de processamento dos processadores de fluxo, sendo a maisrápida entre as demais memórias. Isso justifica a sua posição na hierarquia. Além des-sas características, a memória privada pode conter um valor inteiro ou ponto flutuante;e a quantidade varia de acordo com a geração da arquitetura. Por exemplo, a arquiteturaKepler (SM 3.0) contém 65.536 registradores, enquanto a arquitetura Fermi (SM 2.1)possui 32.768 registradores.

Logo abaixo da memória privada, encontra-se a memória compartilhada que,também, localiza-se dentro dos SMs. Porém, diferente da memória privada, ela é aces-sível a todos os SPs contidos num mesmo SM. Cada memória compartilhada é visívelsomente a um único SM. Embora usada normalmente para trocar dados entre os SPs domultiprocessador de fluxo, ela pode ser utilizada como memória cache que, diferente daCPU, é inteiramente controlada pelo programador. Com relação à velocidade, a memó-ria compartilhada é 10 vezes menor do que a dos registradores (memória privada) [8]. Aquantidade de memória compartilhada disponível por SM não é muito, podendo chegar a64 KBytes na arquitetura Kepler (SM 3.0) e 32 KBytes na Fermi (SM 2.1).

Na base da hierarquia de memória, localiza-se a memória do device que éacessível por todos os SMs e se divide em: global, constante e textura. A global é aprincipal memória da GPU e aceita operações de escrita e leitura. A memória constantesomente aceita operações de leitura. Sendo assim, ela é otimizada para multibroadcast

envolvendo múltiplos SPs. Mesmo residindo na memória do device, o acesso à constanteé feito por diferentes funções que permitem usar uma cache especial [8]. A memóriade textura, por sua vez, é utilizada em dados onde exista interpolação, por exemplo, em2D ou em 3D com tabelas de consulta. Ela possui uma característica especial que é ainterpolação baseada em hardware. Todo SM tem um barramento separado para cada tipode memória do device, como pode ser visualizado na Figura 3.4. Essa figura mostra umaabstração da estrutura do SM que está conectada com todos os níveis da hierarquia dememória disponibilizados na arquitetura CUDA.

Na próxima seção, é explicado o modelo de programação CUDA disponibilizadopara o desenvolvedor. Esse modelo tem como objetivo abstrair os detalhes do hardware,facilitando o desenvolvimento sobre a arquitetura CUDA.

Page 46: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.3 Modelo de Programação CUDA 44

Figura 3.4: Arquitetura Geral dos SMs [8].

3.3 Modelo de Programação CUDA

O modelo de programação CUDA é uma extensão da linguagem C e C++, criadaem 2006 pela empresa NVidia. Esse projeto teve como objetivo principal permitir odesenvolvimento de aplicações massivamente paralelas e altamente escaláveis nas GPUs.A partir de sua criação, houve uma explosão no desenvolvimento de aplicações sobreplataformas GPUs, principalmente de aplicações não-gráficas. Isso ocorreu devido àfacilidade de desenvolver programas com pouco ou nenhum conhecimento em APIs(Interface de Programação de Aplicativos) gráficas . Assim, programas paralelas foramdesenvolvidas para diversas áreas, como química, matemática, físicas e atividades geraisde busca e classificação [21].

A abstração dos detalhes da arquitetura é um ponto forte desse modelo, pois tornaas aplicações sobre a plataforma da GPU altamente escaláveis e fáceis de desenvolver.Escalável porque o programa fica independente do modelo do hardware que está sendocompilado; e fácil de desenvolver por ter as detalhes do hardware CUDA encapsuladasnas características do modelo programação.

Uma aplicação desenvolvida no modelo de programação CUDA não é totalmenteparalelizada, pois requer duas fases: uma sequencial e uma paralela. Elas se alternam entre

Page 47: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.3 Modelo de Programação CUDA 45

si uma ou várias vezes em uma única execução. A execução inicia na fase sequencial,a qual ocorre na CPU, também conhecida como host. Já a fase paralela ocorre naGPU,nomeado de device. Essa fase é realizada por funções, chamadas de kernels. Elassão simplesmente funções em C/C++ marcadas com palavra reservada de CUDA eprocessadas em paralelo na GPU, explorando o paralelismo de dados. Quando umafunção kernel é chamada, ou lançada, threads são criadas na GPU, e assim, a faseparalela se inicia. Quando todas as threads completam suas execuções, o controle voltapara a CPU, ou seja, para fase sequencial [21]. Visto que o host e o device possuemmemórias separadas, pode ou não ocorrer transferências de dados entre uma fase eoutra. A Figura 3.5 mostra um exemplo de alternâncias entre as fases de um programaCUDA sendo executado. Observa-se uma única thread sendo processada pelo host nafase sequencial, enquanto nas fases paralelas várias threads são executadas na GPU.

Figura 3.5: Execução de um programa CUDA [21].

Na fase paralela, milhares de threads podem ser lançadas na GPU. A quantidadede threads são determinadas no momento do lançamento do kernel. Essa quantidade fazcom que o programador lance um número threads além das unidades de processamento.Isso faz com que o programador não tenha necessariamente o conhecimento da arquiteturaCUDA em que sua aplicação CUDA irá ser executada. Assim, torna-se o modelo deprogramação escalável e aceitável, pois, evita-se detalhes de uma arquitetura específica.

O modelo de programação CUDA pode ser resumido em três característicasprincipais: hierarquia de threads, memória compartilhada e barreira de sincronização.

As threads são organizadas em grades e blocos de threads. Cada bloco dethreads da grade possui identificadores em relação à sua posição nos eixos X e Y na gradee toda thread de um bloco também possui um identificador que o identifica sua posiçãonos eixos X, Y e Z em relação ao bloco. A Figura 3.6 ilustra essa hierarquia de threads,onde cada thread e bloco possuem seus identificadores. Ela mostra 2 kernels lançados nohost e, consequentemente, 2 grades de threads no device, cada qual com uma quantidadede blocos de threads.

Page 48: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.3 Modelo de Programação CUDA 46

Figura 3.6: Hierarquia de threads no modelo de programaçãoCUDA.

Uma grade possui um único kernel e, consequentemente, os blocos de threads

da grade contêm o mesmo código. Porém, não garante que todos os blocos processama mesma instrução em um mesmo instante. Por esse motivo que, em um nível deprogramação, o modelo de programação CUDA é classificado como SPMD (Single-

Program Multiple-Data), pois cada bloco de threads é uma cópia das instruções doprograma.

Como mostrado na Figura 3.6 e descrito antes, as threads que estão dentro deuma grade são organizadas em uma lista de blocos de threads. Essa lista pode ter umaou duas dimensões. Todos os blocos de uma mesma grade possuem o mesmo tamanho.Esse tamanho é definido pela quantidade de threads, sendo que o máximo pode variarde acordo com as gerações das arquiteturas. Por exemplo, nas arquiteturas Fermi eKepler a quantidade máxima é de 1024 threads. Todo o bloco de threads é processadopor um único SM, contudo, um SM pode processar concorrentemente vários blocos dethreads. O limite de processamento simultâneo do SM também é definido de acordo coma arquitetura da GPU. Visto que o bloco é processado por um SM, um único fluxo deinstruções é dado para todas as threads dentro do mesmo. A Figura 3.7 ilustra uma gradede threads com N blocos executando um mesmo código kernel, porém cada bloco comum fluxo diferente de instruções e dados.

Os blocos de threads são compostos por conjuntos de 32 threads, conhecidos porwarp. O SM particiona os blocos em warps de forma idêntica em todas as vezes, ou seja,cada warp possui threads consecutivas, aumentando simplesmente os identificadores. Elecria, gerencia e executa threads de uma warp em uma única vez. As threads da warp

iniciam juntas o mesmo endereço do programa, embora cada uma possua seus próprioscontadores de programa e registradores de estado [8]. A warp executa uma mesma

Page 49: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.3 Modelo de Programação CUDA 47

Figura 3.7: Blocos de threads da grade executando o mesmo có-digo, mas com fluxos diferentes.

instrução a cada momento. Isso implica que se algumas threads da warp se divergiremem uma instrução condicional, a warp serializará os ramos de execução, ou seja, partesdas threads que não entraram no ramo em questão e ficarão ociosas. Vale ressaltar queo tratamento de divergências em instruções de condições acontecem somente dentro deuma warp; em diferentes warps, a execução é independente.

Em nível de programação, os SMs são considerados arquiteturas SIMT (Únicofluxo de instrução, Múltiplas Threads). Essa arquitetura especifica o comportamento deuma única thread, habilitando os programadores a desenvolverem códigos paralelos comthreads independentes. Isso faz com que eles não sejam mais obrigados a escrever códigospara cada thread que segue o mesmo caminho de execução. Threads podem divergir e, emseguida, convergir em algum ponto mais tarde. Porém, cada caminho deve ser executadoem série até que o fluxo de controle se junte novamente. O modelo de programaçãonão obriga os programadores ter conhecimento dos detalhes desse fluxo, porém, torna-se importante para obter bons desempenhos em aplicações paralelas [8] [21] [27].

As threads fazem uso da hierarquia de memória. Cada thread tem acesso àmemória privada, constituída por registradores. As threads de um bloco enxergam omesmo espaço de endereçamento da memória compartilhada. Por sua vez as threads deuma grade, conjunto de todas as threads de um kernel, alcançam toda memória global,cuja a latência é a maior entre todas as memórias da GPU. A Figura 3.8 ilustra essahierarquia de memória.

Como a linguagem CUDA é uma extensão da linguagem C/C++, CUDA possuialgumas palavras reservadas próprias. Por exemplo, __device__, __const__. __shared__

que servem para identificar as localidades de variáveis, que neste caso indicam a memóriaglobal, constante e compartilhada, respectivamente.

A Figura 3.9 representa um algoritmo de soma de dois vetores em CUDA, ilus-trando sua fase paralela (kernel). Esse mesmo algoritmo é descrito para o modelo PRAMno Algoritmo 2.1. A palavra reservada __global__ indica que a função SomaDeVetores éum kernel, sendo que toda função kernel tem o retorno void. A palavra reservada threa-

dIdx é uma variável de três dimensões, que identifica a thread dentro do bloco de threads.

Page 50: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.4 Otimizações em CUDA 48

Figura 3.8: Hierarquia de Memória do Modelo de ProgramaçãoCUDA [9].

De forma semelhante, a variável blockIdx indica a identificação do bloco de threads e avariável blockDim contém a dimensão em números de threads do bloco. Assim, a variá-vel id recebe a identificação global da thread, ou seja, da thread na grade (kernel). Cadathread acessa os valores nos vetores correspondentes à sua identificação, executa a somae a armazena no vetor C que se localiza na memória global.

3.4 Otimizações em CUDA

Como explicado anteriormente, o modelo de programação CUDA oferece umalto nível de abstração do hardware, trazendo facilidade no desenvolvimento de aplica-ções para a platarfoma GPU. Porém, a fim de aproveitar ao máximo o desempenho parauma determinada aplicação, é necessário entender e utilizar algumas características intrín-secas tanto do modelo de programação quanto do hardware CUDA. Esta seção detalhaalgumas dessas características, pois as mesmas foram utilizadas nas implementações pa-ralelas propostas no presente trabalho.

A estrutura de um SM, descrito na seção 3.2, tem a arquitetura SIMD/SIMT.Consequentemente, as threads de um bloco executam o mesmo fluxo de instruções. Po-

Page 51: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.4 Otimizações em CUDA 49

Figura 3.9: Algoritmo de soma de vetores em CUDA.

rém, somente as threads de uma warp executam a mesma instrução ao mesmo tempo.Quando uma warp é pausada ou parada, visando esperar por algum acesso à memória,outras warps continuam a ser executadas. Dessa forma, o hardware é mantido ocupado.Essa técnica é conhecida como latência escondida (do inglês hide latency). Algumas mé-tricas relacionadas ao número de warps ativas em um multiprocessador são importantespara determinar quão eficientemente o hardware é mantido ocupado. Tal métrica é cha-mada de ocupação. Ocupação é a taxa da capacidade do hardware para processar warps

que está ativamente em uso. A alta taxa de ocupação não implica em um alto desempenho.Contudo, a baixa taxa de ocupação influência na latência escondida, resultando um baixodesempenho [27].

Vários fatores afetam a taxa de ocupação dos SMs, por exemplo, a quantidadede registradores utilizados, a quantidade de memória compartilhada utilizada e o númerode threads por bloco. Os registradores são alocados para todo bloco de threads. Seum bloco de threads utilizar grandes quantidades, o número de blocos de threads queresidem em um SM é reduzido, assim, a taxa de ocupação também é reduzida. Porisso, é necessário verificar qual a quantidade ideal, para uma determinada aplicação, dosrecursos compartilhados no SMs. Isso é feito na prática testando a aplicação. A NVidiadisponibiliza ferramentas que auxiliam nesta verificação, por exemplo, o NVidia CUDAProfiling.

Uma outra técnica relacionada com a ocupação é a sustentação total dos SMspor blocos de threads, ou seja, manter os SMs totalmente ocupados. Isso implica no usode duas estratégias. A primeira está relacionada com a quantidade de threads lançadas nagrade. Essa quantidade é limitada de tal forma que não haverá blocos de threads na fila deescalonamento. A segunda estratégia, consequência da primeira, é a alteração no tempode vida das threads, mantendo-as até o final do processamento do kernel. Dessa forma,há necessidade de conhecimento prévio da quantidade de SMs em que a aplicação irá serexecutada.

Observe que no modelo tradicional de programação CUDA, descrito na Se-ção 3.3, várias threads são lançadas independente do hardware a serem executadas. As-sim, de certa forma, o uso dessa técnica altera o modelo tradicional de CUDA. Essa téc-nica pode ser chamada de ocupação intensiva dos SMs.

A utilização da ocupação intensiva dos SMs evita que qualquer SM fique ocioso

Page 52: Implementações Paralelas para os Problemas do Fecho Transitivo e

3.4 Otimizações em CUDA 50

devido a qualquer dependência de dados do algoritmo paralelo no momento da compu-tação. Ao processar uma dependência de dados, ocupa-se um SM com o processamentoe os outros ficam ociosos. Para atingir a ocupação intensiva, a dependência é replicadapara todos SMs. Um bloco de threads é executado em um único SM, que processa umou mais blocos de threads simultaneamente. Consequentemente, cada bloco de threads

poderá operar em mais de uma parte dos dados de entrada do problema, quando esta formaior que a grade de blocos de threads do kernel.

Page 53: Implementações Paralelas para os Problemas do Fecho Transitivo e

CAPÍTULO 4Algoritmos em Grafos

O estudo de grafos começa por volta do século XVIII, quando Euler definiu oproblema da ponte de Königsberg. A partir disso, grandes interesses de pesquisadores sur-giram para explorar as propriedades dos grafos. Essa exploração cominou na descobertada poderosa abstração de relacionamentos entre objetos e grafos que pode ser aplicado emproblemas de diversas áreas. Por exemplo, sistemas de transportes, interações humanas,rede de comunicação, robótica, projetos VLSI, compiladores, circuitos eletrônicos, bio-logia computacional e engenharia de software. Dos vários motivos do grande sucesso douso de grafos, destaca-se o formalismo oferecido por eles para modelar diferentes estrutu-ras e relacionamentos. Muitos autores afirmam que a chave para solucionar um problemaestá em pensar em termos de grafos [33] [22] [3].

Inicialmente, neste capítulo, são explanados conceitos essenciais e representa-ções de grafos. Em seguida, dois problemas relacionados a grafos que demandam umasérie de etapas sequenciais são expostos: o problema de computar o fecho transitivo e oproblema de encontrar o caminho mínimo entre todos os pares de vértices. Para cada pro-blema, dois algoritmos sequenciais que o soluciona são analisados e outras abordagensrelacionadas são apresentadas. Assim, esse capítulo tem a finalidade de obter um entendi-mento dos problemas abordados neste trabalho e das propostas de soluções já existentespara o fecho transitivo e para o caminho mínimo entre todos os pares de vértices.

4.1 Conceitos Preliminares

Um grafo G = (V,E) é definido pelo conjunto de V vértices e o conjunto de E

arestas, onde E ⊆ V × V . Cada elemento (v,w) ∈ E é uma aresta, onde v e w ∈ V e w éadjacente ao vétice v. Os grafos podem ser direcionados (também chamados de dígrafos)ou não-direcionados. Eles são direcionados se arestas (u,v) e (v,u) ∈ E e (u,v) 6= (v,u),onde v e u ∈ V . As arestas são não-direcionadas se (u,v) e (v,u) ∈ E e (u,v) = (v,u). AFigura 4.1 mostra dois grafos, sendo que o grafo em (a) é não-direcionado e o grafo em(b) é direcionado.

Page 54: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.1 Conceitos Preliminares 52

O número de arestas incidentes a um vértice é chamado de grau do vértice, sendoesse classificado como entrada ou saída, caso o grafo seja direcionado. O grau de um grafoé o maior grau entre todos os seus vértices. Para cada aresta, um valor de inteiro ou deponto-flutuante pode ser atribuído à mesma, sendo considerado como peso da aresta. NaFigura 4.1, o grafo (a) possui grau 4, enquanto o grafo (b) possui grau de entrada 3 e graude saída 2.

Um caminho p = {v0,v1, ...,vk} de v0 a vk é uma sequência de vértices, onde(vi,vi+1) ∈ E para 0 ≤ i < k. Um ciclo é um caminho onde v0 = vk, sendo v0 o vérticeinicial e vk, o vértice final. Grafos cíclicos contém pelo menos um ciclo. Quando grafossão acíclicos, eles são conhecidos por DAGs. Em relação à quantidade de arestas, osgrafos podem ser esparsos ou denso.

Figura 4.1: Exemplos de dois grafos, sendo o grafo (a) não-direcionado e o grafo (b) direcionado

Estruturas de dados que representam grafos têm sido estudadas em profundidade.As mais comuns são: matriz de adjacência e lista de adjacência. A matriz de adjacênciaassume que os vértices são enumerados de alguma forma arbitrária e exige a comple-xidade de espaço Θ(V 2). Ela pode ser aplicada tanto para grafos direcionados quantonão-direcionados. A lista de adjacência consiste por um conjunto de |V | listas. Para cadavértice u, existe uma lista contendo todos os vértices v tal que (u,v) ∈ E e os vértices u

e v ∈ V . Em grafos direcionados, a soma do tamanho de todas as listas é dado por |E|.Enquanto em grafos não-direcionados, a soma é 2|E|, isso porque uma aresta aparece emduas listas. Para qualquer grafo, a complexidade de espaço requerida por essa segunda re-presentação é Θ(V +E). A Figura 4.2 ilustra as duas formas de representação de grafos,sendo em (a) a matriz de adjacência e em (b) a lista de adjacência.

Ambas podem representar grafos com pesos nas arestas. Embora a lista deadjacência exija assintoticamente menos espaço do que a matriz de adjacência, estademostra mais simplicidade do que aquela. Pois, a matriz apresenta uma estrutura regular,sendo ótima para grafos pequenos. Quando se trata de grandes grafos esparsos, a matrizde adjacência não se mostra eficiente e em problemas que precisam percorrer todo o grafo,visto que demanda muito esforço para verificar a existência de arestas.

Page 55: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 53

Figura 4.2: Representações de Grafos: (a) Matriz de Adjacência e(b) Lista de Adjacência

O termo regular se refere a quando o controle de fluxo e referências à memórianão dependem dos dados de entrada, ou seja, o comportamento do programa na execuçãoé previsível ao verificar o código do programa, o tamanho da estrutura e inicializaçãodos endereços. Isso, sem ter conhecimento prévio dos valores contidos nas estruturas dedados. Em contrapartida, o termo estrutura irregular está relacionado a casos nos quaisos valores de entrada determinam o comportamento em tempo de execução do programa,de modo que, esse comportamento não pode ser previsível antes da execução.

Aplicações baseadas em grafos tendem a ser irregulares, pois, os acessos à me-mória são dependentes do tipo da conectividade do grafo. As aplicações que utilizamárvores e filas de prioridade, assim como os grafos, apresentam dependências nas carac-terísticas da estrutura de dados. Tais características tornam-se um desafio para mantere processar essas estruturas na memória das GPUs, porque os acessos à memória e ofluxo de instruções nos SMs seguem um padrão e uma linearidade ditado pela arquiteturaCUDA.

Os grafos são aqui representados, em algumas soluções propostas, por uma estru-tura de lista de adjacência compactada [16]. Nessa representação, as listas de adjacênciassão unificadas em uma única lista de tamanho O(E) e os vértices são representados poruma segunda lista de tamanho O(V). Cada elemento da lista de vértices aponta para o iní-cio de sua lista de arestas na lista de adjacência unificada. Caso a representação seja de umgrafo ponderado, uma terceira lista de mesmo tamanho da lista de adjacência unificada éadicionada na representação. A Figura 4.3 ilustra a representação em lista compactada deum grafo não-ponderado.

4.2 Fecho Transitivo

O problema do fecho transitivo está relacionado com a propriedade transitiva emconjuntos. Esse relacionamento encontra-se na extensão da propriedade transitiva em uma

Page 56: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 54

Figura 4.3: Representação de grafo usando a lista compactada.

relação binária p. Quando tal extensão ocorre, uma nova relação p′ é criada. A relaçãop′ contém todos os elementos que p contém, além dos pares ordenados necessários paraque a propriedade transitiva seja verificada na relação binária p. Visto isso, p⊆ p′, sendoque p′ é o menor conjunto para que a propriedade transitiva seja verificada. Se a relaçãojá realiza a propriedade de transitividade, então ela é o seu próprio fecho transitivo [13].No problema do fecho transitivo aplicado a grafos, o mesmo conceito de alcançabilidadeentre vértices é utilizado. Um vértice v é alcançável a partir do vértice u se existir umcaminho de u até v [13].

Seja um grafo G = (V,E), onde V é o conjunto de vértices e E ⊆ V ×V é oconjunto de arestas. O fecho transitivo do grafo G é definido como um grafo G′ = (V,E ′).O conjunto E ′ é o fecho da relação binária V ×V relacionado a propriedade transitiva epode ser definido como: E ′ = {(i, j) ∈ V 2 | se somente, se existe um caminho do nó i parao nó j em G}. A Figura 4.4 ilustra um grafo G com seu respectivo fecho transitivo, sendoas arestas adicionais as pontilhadas.

Figura 4.4: Grafo original em (a) e seu respectivo fecho transitivoem (b).

Uma propriedade que se destaca no problema do fecho transitivo é a existência deuma sequência de etapas necessária para que o problema seja solucionado. Por exemplo,suponha o conjunto T = {1, 2, 3, 4} e a relação R ⊆ T ×T = {(1,2), (2,3), (3,4)} ilustrada

Page 57: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 55

na Figura 4.5 (a). Aos pares ordenados (1,2) e (2,3), é adicionada a relação (1,3). Alémdisso, é incluída a relação (2,4) devido aos pares (2,3) e (3,4), resultando no seguinteconjunto (Figura 4.5 em (b)):

{(1,2), (1,3), (2,3), (2,4), (3,4)}

Entretanto, a relação acima ainda não é a solução do fecho transitivo, pois, aoverificar o novo par (2,4) e o par original (1,2), faz-se necessário incluir o par (1,4).Com isso, chega-se na menor relação R’ transitiva que contém R, ou seja, no fechotransitivo [13]. A Figura 4.5 em (c) ilustra a relação R’.

R’ = {(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)}

Figura 4.5: Sequência de etapas para resolver o problema do Fe-cho Transitivo

Essa propriedade mostra que várias etapas sequenciais são necessárias paraencontrar o fecho transitivo de uma relação, pois pode haver dependências entre elas. Isso,de certa forma, dificulta a total paralelização de uma possível solução para o problema.

Uma variedade de algoritmos sequenciais tem sido proposta para achar o fechotransitivo de um grafo. Os primeiros algoritmos sequenciais solucionavam o problemautilizando multiplicação de matrizes e possuíam uma complexidade de tempo de O(|V |4).Em seguida, veio a proposta algorítmica de Stephen Warshall (algoritmo de Warshall)com uma complexidade de tempo de O(|V |3), sendo V o conjunto de vértices. Alémdos algoritmos que utilizam multiplicações de matrizes, há algoritmos que encontramo fecho transitivo percorrendo todo o grafo. Esses algoritmos são conhecidos comoalgoritmos de buscas. Normalmente, eles utilizam a lista de adjacência para representaros grafos. Exemplos desses tipos de algoritmos são: busca em largura (BFS, do inglêsBreadth− First Search) e o busca em profundidade (DFS, do inglês Depth− First

Search).

Page 58: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 56

Todos os métodos referenciados acima compartilham uma característica emcomum: um esforço computacional que varia de acordo com a estrutura do grafo. Issose torna um problema para tais algoritmos. Por exemplo, para algoritmos que utilizam amatriz de adjacência como representação, a resolução se torna impraticável para grafosmuito grandes. Para algoritmos que utilizam listas de adjacência, a densidade do grafo jáinfluência no tempo de execução. Adicionalmente, o tamanho do diâmetro do grafo tem acapacidade de exercer influência sobre os algoritmos que percorrem o grafo.

A fim de alcançar o objetivo deste trabalho, são explanados nas próximas seçõesdois algoritmos sequenciais que solucionam ao problema do fecho transitivo: Warshalle BFS. O primeiro soluciona o problema do fecho transitivo utilizando multiplicaçãode matrizes e o segundo percorrendo o grafo. Após as explanações dos algoritmossequenciais, uma revisão de outras abordagens sequenciais e paralelas é feita.

4.2.1 Algoritmo Sequencial de Warshall

Considere um dígrafo G = (V,E), onde V é o conjunto de vértices e E é oconjunto de arestas. Sejam os vértices numerados de 1,2, ...,n. A entrada do algoritmoWarshall é uma matriz de adjacência binária M com tamanho n× n. Todo Mi j é 1 se háuma aresta direcionada do vértice i para o j, caso contrário, o valor é 0. O algoritmo deWarshall consiste em três laços, como pode ser visto no Algoritmo 4.1. A complexidadeé O(|n|3), como dito anteriormente.

Algoritmo 4.1: Algoritmo Sequencial do Warshall

Entrada: Grafo G representado pela Matriz de Adjacência Binária Mn×n;Saída: Fecho Transitivo do grafo G representado pela Matriz de Adjacência

Binária Mn×n;

1 para k = 1 até n faça2 para i = 1 até n faça3 para j = 1 até n faça4 se Mik = 1 E Mk j = 1 então5 Mi j← 16 fim7 fim8 fim9 fim

Uma característica importante do algoritmo Warshall (Algoritmo 4.1) é a inde-pendência das iterações dos laços mais internos (linhas 2 e 3) do Algoritmo 4.1. Isso

Page 59: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 57

permite que as iterações passam a ser alternadas sem afetar o resultado, diferente das ite-rações do primeiro laço (linha 1) que possuam dependência entre si. Essa dependênciaestá na utilização das arestas criadas nas iterações anteriores. Sendo assim, pode-se con-cluir que a sequência de etapas mostrada na seção 4.2 do problema do fecho transitivoestá contido no laço da linha 1.

4.2.2 Algoritmo Sequencial de Busca em Largura

Busca em Largura (BFS) é um dos algoritmos mais simples e iterativo relaci-onados a grafos, sendo a base para outros importantes algoritmos de grafos. Uma ideiasimilar é apresentada no algoritmo de Dijkstra [3] [22].

Dado um grafo G = (V,E) e um vértice origem s ∈ V , o algoritmo explora asarestas de G a partir de s para encontrar cada vértice que é alcançável por s. À medida queos vértices são alcançados, eles são adicionados à fila Q e processados em ordem first-in

first out (FIFO). A computação das distâncias em número de arestas do vértice de origempara cada vértice alcançavel s é realizado pelo algoritmo. Além disso, o algoritmo produzuma árvore chamada breadth-first que mantém a ordem de vértices visitados, ou seja, alista de predecessores.

Inicialmente, todos os vértices são marcados como não-visitados e a fila consistesomente no vértice origem s. A cada iteração, o primeiro vértice da fila é removido etodos os seus vizinhos são visitados. Qualquer vértice vizinho não-visitado é adicionadono final da fila. Esse processo é repetido até que a fila fique vazia.

De acordo com Leiserson et al [22], o algoritmo Busca em Largura recebe essenome pois expande fronteiras, também chamadas de níveis, entre os vértices visitados enão-visitados de maneira uniforme. Ele percorre todos os vértices de uma fronteira antesde prosseguir para a próxima. Em outras palavras, o algoritmo visita todos os vérticescom a distância k de s antes de visitar qualquer vértice com distância k+1.

O Algoritmo 4.2 detalha o algoritmo Busca em Largura sequencial que recebecomo entrada um grafo G e um vértice s. A variável d[v] mantém a distância de cadavértice v para o vértice origem s. A árvore breadth-first é armazenada na variável p[v] aqual indica o predecessor de cada vértice v. Duas operações são realizadas sobre a fila Q,enqueue e dequeue.

Page 60: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 58

Algoritmo 4.2: Algoritmo Sequencial BFS

Entrada: Grafo G = (V,E) e um vértice origem s;Saída: Árvore breadth-first p;

1 para cada vértice v ∈V faça2 visitado[v]← f alse

3 d[v]← ∞

4 p[v]← null

5 fim6 visitado[s]← true

7 d[s]← 08 ENFILEIRAR(Q,s)

9 enquanto não VAZIO(Q) faça10 v← DESENFILEIRAR(Q)

11 para cada vértice w em ad j[G] faça12 se não visitado[w] então13 visitado[w]← true

14 p[w]← v

15 d[w]← d[v]+116 ENFILEIRAR(Q,w)

17 fim18 fim19 fim

As operações de enfileirar (enqueue) e desenfileirar (dequeue) gastam O(1)de tempo, mas são feitos, no pior caso, para cada vértice, tendo o tempo de O(|V |).Considerando que cada vértice é enfileirado somente uma única vez na fila que a listade adjacência é totalmente visitada, que a soma de todas as listas de adjacência é igual aE, então o tempo total de execução do algoritmo BFS é O(|V |+ |E|).

A Figura 4.6 ilustra o resultado da execução do algoritmo BFS em um grafodirecionado, sendo que o vértice 1 é vértice de origem. Quando o vértice 1 é processado,somente o vértice 2 é enfileirado na fila. Quando o 2 é processado, os vértices 3, 6, 5 sãoadicionados. Por fim, ao processar o 3, o vértice 4 é colocado na fila.

O problema do fecho está relacionado com a alcançabilidade entre todos osvértices; e o algoritmo BFS atinge todos esses vértices alcançáveis a partir de uma únicafonte. Por isso, há a necessidade de aplicar o algoritmo do BFS para cada vértice do grafo,com o intuito de que a solução encontrada pelo mesmo seja satisfatória para o problemado fecho transitivo. O Algoritmo 4.3 utiliza o BFS para o problema do fecho repetindo-

Page 61: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 59

Figura 4.6: Exemplo de execução do BFS em um grafo

o O(|V |) vezes, cada qual com um vértice origem distinto. Com isso, o Algoritmo 4.3possuí a complexidade de |V |.O(|V |+ |E|).

Algoritmo 4.3: Algoritmo Sequencial BFS para o Problema do Fecho Tran-sitivo

Entrada: Grafo G = (V,E);Saída: Fecho Transitivo do grafo G′ = (V,E ′);

1 para cada vértice v ∈V faça2 BFS Sequencial (G,v)

3 fim

4.2.3 Outras Abordagens

Muitos trabalhos na literatura são esforços de paralelização do algoritmo BFS.Porém, poucos voltados para implementação do problema do fecho transitivo. Uma vezque, como explicado na Seção 4.2.2, o algoritmo BFS precisa ser aplicado para cadavértice do grafo, limitando, de certa forma, o tamanho do grafo em máquinas reais.

Em 2007, Harish e Narayanan [16] implementaram um BFS paralelo utilizandoCUDA e a estrutura compactada de grafos ilustrada na Figura 4.3 da Seção 4.1. Oalgoritmo proposto é dividido em duas fases, uma sequencial e outra paralela, sendoque a sequencial é executada na CPU e a paralela na GPU. Na fase sequencial, há acriação e a inicialização das estruturas que serão utilizadas na GPU, além de chamadassequenciais ao kernel CUDA que representa a fase paralela do algoritmo. O Algoritmo 4.4descreve a fase sequencial do algoritmo de Harish e Narayanan [16]. As estruturas Fa eXa são arrays booleanos de tamanho |V |, sendo que este representa os vértices visitadose aquele os vértices da fronteira. O array de inteiros Ca mantém as distâncias do vérticede origem para cada vértice. Na fase paralela na GPU, uma thread é atribuído para cada

Page 62: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.2 Fecho Transitivo 60

vértice e os vértices da próxima fronteira são adicionados em paralelo no array Fa. NoAlgoritmo 4.4, observa-se que a verificação de vértices na próxima fronteira é feita naCPU sequencialmente. Isso, de certo modo, dificulta o uso do algoritmo proposto noproblema do fecho transitivo, pois o algoritmo seria repetido |V| vezes sequencialmente.

Algoritmo 4.4: Código CPU - Implementação do Algoritmo BFS propostapor Harish e Narayanan [16]

Cria estrutura compactada do grafo G(V,E) representada por Va e Ea

Cria e inicializa os arrays Fa, Xa e Ca de tamanho |V |enquanto não EMPTY (Fa) faça

para cada vértice v façaInvoca o Kernel BFS(Va,Ea,Fa,Xa,Ca)

fimfim

Hong et al [17], em 2010, propuseram um algoritmo BFS paralelo em CUDA.A fim de obter melhor desempenho, foi utilizado um modelo de programação centradoem warps. Ao invés de atribuir um vértice para cada thread, um conjunto de tarefas foiatribuída para cada warp. Isso diminuiu o impacto da estrutura irregular do grafo na GPU.Pois, os acessos à memória das threads na warp são aglutinados na medida do possível.O algoritmo proposto [17] não é adequado para o problema do fecho transitivo. Uma vezque o objetivo apresentado pelos autores é a paralelização do algoritmo BFS que recebecomo entrada grafos com certas características. Por exemplo, uma grande quantidade devértices e arestas, e grau reduzido a 6 ou diâmetro a 10. Eles relataram 0,9 GTEPS para 32milhões de vértices e 240 milhões de arestas na arquitetura Fermi Tesla 2050 executandosomente uma única instância do algoritmo a partir de uma única origem.

Cáceres et al [7] apresentaram um algoritmo paralelo baseado no algoritmo deWarshall utilizando o modelo BPS/CGM para computar o fecho transitivo. O algoritmoutiliza logp+1 rodadas de comunicação, O(nm

p ) de computação local e O(nmp ) de espaço

de em cada processador, para um grafo de entrada acíclico e direcionado, e consideradop processadores, n vértices e m arestas. Os passos do algoritmo proposto por Cáceres et

al [7] são apresentados no Algoritmo 4.5.

Page 63: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 61

Algoritmo 4.5: Algoritmo em BSP/CGM para Computar o Fecho Transitivoproposto por Cáceres et al [7]

Entrada: (1) Um grafo dirigido D = (V,E), |V |= n e vértices e |E|= m

arestas; (2) p processadores;Saída: Dt , o fecho transitivo de D;

1 Encontre uma extensão linear L de D

2 Seja S0, ...,Sp−1 uma partição de V (D), cujas partes tenha cardinalidades tãoiguais quanto possível, e cada S j seja formado por vértices consecutivos emL Para j = 0, ...,(p−1), atribua os vértices de S j ao processador p j

3 para todo processador p j em paralelo faça4 constrói o grafo dirigido D(S j) de D

5 computa o fecho transitivo Dt(S j) de D(S j)

6 inclui em D as arestas Dt(S j) D(S j)

7 fim8 Após todos os processadores terem completado o passo 3, verifique se

Dt(S j) = D(S j), para todos os que processa p j. Se verdadeiro, o algoritmo éfinalizado e D é o fecho transitivo do grafo dirigido de entrada. Casocontrário, vá para o passo (3).

4.3 Caminho Mínimo entre Todos os Pares de Vértices

O problema do Caminho Mínimo entre Todos os Pares (APSP, All-Pair Shortest-

Path) é bem conhecido na teoria dos grafos. Muitos dos problemas de redes do mundoreal implicam em encontrar o menor caminho entre dois pontos quaisquer [28]. Porexemplo, problemas relacionados a banco de dados, rede de relacionamento, tráfego aéreoe terrestre, roteamento de tráfego de redes, sistemas distribuídos, ou qualquer problemaque possa ser representado por um grafo no qual as arestas sejam ponderadas e cujosvalores sejam linearmente acumulados à medida que a rede é percorrida [11].

Considere um grafo G=(V,E) direcionado e ponderado, o problema do caminhomínimo entre cada par de vértice consiste em identificar para cada par de vértice u,v ∈V ,o menor caminho de u até v, cujo o peso é a soma de todas as arestas do caminho.

O problema do caminho mínimo possui três variantes, além do APSP, quesão [22]:

• Caminho Mínimo com uma Única Origem (SSSP, Single-Source Shortest-Path): oobjetivo é computar a menor distância de um vértice dado como origem s para cadavértice v ∈V .

Page 64: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 62

• Caminho Mínimo para um Único Destino (SDSP, Single-Destination Shortest-

Path): consiste em identificar o menor caminho dado um vértice de destino t paracada vértice v ∈V .• Caminho Mínimo entre um Único Par de Vértice (SPSP, Single-Pair Shortest-Path):

consiste em identificar o menor caminho entre um único par de vértices.

O problema APSP é considerado uma generalização do problema SSSP [28][22].Assim sendo, há duas formas de solucionar o problema APSP. A primeira é aplicandoalgoritmos que o resolvem usando a programação dinâmica, como os algoritmos deMultiplicação de Matrizes, Floyd-Warshall e Johnson1. A segunda é executando |V| vezes(algoritmos) que solucionam o problema SSSP, onde V é o conjunto de vértices. Porexemplo, os algoritmos de Dijkstra e Bellman-Ford seguem essa proposta.

Nesta seção, são mostrados em detalhes dois algoritmos sequenciais que resol-vem o problema APSP, cada qual utiliza uma técnica diferente. O primeiro é o algoritmoFloyd-Warshall e o segundo, o algoritmo de Dijkstra. Logo após, uma relação de algunstrabalhos relacionados ao problema APSP é apresentada.

4.3.1 Algoritmo Sequencial de Floyd-Warshall

O algoritmo Floyd-Warshall utiliza uma técnica conhecida como programaçãodinâmica. Possui complexidade de tempo de O(|V |3) e complexidade de espaço deO(|V |2). Trabalha sobre um grafo conexo e direcionado com arestas de pesos positivos.Arestas com pesos negativos podem estar presentes, porém ciclos negativos não sãoaceitos para solução deste algoritmo.

O Algoritmo 4.6 corresponde à solução de Floyd-Warshall sequencial. Ele recebecomo entrada uma matriz de distâncias, sobre a qual o algoritmo opera para encontraro menor caminho entre todos os pares de vértices do grafo por ela representado. Talalgoritmo baseia-se numa estrutura de repetição com três laços aninhados. Os dois laçosmais internos podem ter a ordem alterada sem afetar o resultado da computação, sempremantendo o laço mais externo inalterado, seguindo uma forma semelhante ao algoritmode Warshall.

1O algoritmo de Johnson é o mais rápido assintoticamente que os outros dois para grafos esparsos.

Page 65: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 63

Algoritmo 4.6: Algoritmo Sequencial do Floyd-Warshall

Entrada: Grafo conexo ponderado G, representado pela matriz dedistâncias An×n;

Saída: Grafo conexo ponderado G com caminho mínimo entre todos ospares de vértices, representado pela matriz de distância;

1 para k = 1 até n faça2 para i = 1 até n faça3 para j = 1 até n faça4 se Mik = 1 E Mk j = 1 então5 Mi j← 16 fim7 fim8 fim9 fim

4.3.2 Algoritmo Sequencial de Dijkstra

O algoritmo de Dijkstra [12] fornece uma solução eficiente para o problema docaminho mínimo tanto para o SSSP quanto para o APSP, embora, para solucionar este,tenha que ser aplicado para cada vértice do grafo. O algoritmo trabalha sobre um grafono qual todos os pesos das arestas são positivos. Assim, constrói um caminho mínimoa partir de um único vértice origem s para todos outros vértices, utilizando uma técnicaconhecida como relaxamento de arestas.

O processo de relaxamento de uma aresta (u,v) consiste em testar se é possívelmelhorar o menor caminho s encontrado até o momento passando por u. Se for possível,são atualizados a distância e o predecessor de v. O relaxamento pode simplesmente reduziro valor do menor caminho. Quando o relaxamento de uma aresta (u,v) ocorre, é dito queo vértice v foi alcançado.

O Algoritmo 4.7 representa a solução sequencial de Dijsktra. As distâncias daorigem para cada vértice v são mantidas em d[v] e os predecessores estão contidos emp[v]. Inicialmente, cada distância d[v] possui valor infinito, exceto a do vértice de origemque contém o valor zero. Um conjunto de vértices S é mantido, onde v ∈ S e d[v] é o valorda menor distância da origem até o vértice v. O algoritmo seleciona repetidamente umvértice u∈V −S com o menor caminho estimado, adiciona-o a S e, somente então, relaxatodas as arestas de u. Uma fila de prioridades é utilizada para selecionar o vértice commenor caminho [13] [3] [22].

Page 66: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 64

Algoritmo 4.7: Algoritmo Sequencial de Dijsktra

Entrada: Grafo G = (V,E) e um vértice origem s;Saída: Lista de predecessores p e o caminho mínimo d;

1 para cada vértice v ∈V faça2 d[v]← ∞

3 p[v]← null

4 fim5 d[s]← 06 S← /0

7 Q←V

8 enquanto não EMPTY (Q) faça9 u← EXT RACT −MIN(Q)

10 S = S∪{u}11 para cada vértice v em ad j[u] faça12 se v /∈ S e d[v]> d[u]+w(u,v) então13 p[v]← u

14 d[v]← d[v]+w(u,v)

15 DECREASE_KEY (Q,v,w)

16 fim17 fim18 fim

O relaxamento das arestas ocorre nas linhas 12 a 16 no Algoritmo 4.7. A cadaiteração do laço da linha 8, um vértice é obtido pela chamada ao método EXTRACT-MIN.Esse método extrai o elemento com menor valor na fila de prioridade. O algoritmo finalizaquando S =V , ou seja, Q = /0. Uma característica importante do algoritmo é a invariânciado laço da linha 8, que tem o objetivo de manter o conteúdo da fila de prioridade, ondeQ = V − S. Isso faz com que o algoritmo escolha o vértice mais próximo em V − S paraadicionar ao conjunto S. Assim, confirma-se a presença da estratégia gulosa no algoritmode Dijkstra [3].

A complexidade de tempo depende da estrutura de dados implementada na filade prioridade. Pois, as operações EXTRACT-MIN, DECREASE-KEY e INSERT (implícitona linha 7) pertencentes à fila de prioridade são executadas para cada vértice. Com aestrutura heap, o algoritmo executa em tempo de O(|E|log|V |). Se o heap de Fibonacci éusado, a complexidade do algoritmo passa a ser O(|E|+ |V |log|V |). Caso seja utilizada aestrutura lista, a complexidade aumenta para O(V 2) [13] [3] [22] [29].

Semelhante ao Algoritmo 4.3, o algoritmo de Dijkstra, para solucionar o pro-blema APSP, requer sua invocação repetida para cada vértice v ∈ V . O Algoritmo 4.8

Page 67: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 65

descreve o algoritmo Dijkstra para o problema APSP. A complexidade de tempo do Al-goritmo 4.8 para o melhor caso citado anteriormente é O(|V ||E|log|V |) e, para o piorcaso, O(|V |3). Portanto, embora seja eficiente, o algoritmo de Dijkstra requer um grandeesforço computacional para solucionar o problema APSP.

Algoritmo 4.8: Algoritmo Sequencial de Dijkstra para o Problema APSP

Entrada: Grafo G(V,E);Saída: União de listas de predecessores p e o caminho mínimo d;

1 para cada vértice v ∈V faça2 Dijkstra Sequencial (G,v)

3 fim

4.3.3 Outras Abordagens

A solução sequencial proposta por Venkataraman et al. [39] faz com que oalgoritmo Floyd-Warshall utilize a memória cache da CPU de forma eficiente. O métodoparticiona a matriz de distâncias em ladrilhos. A Figura 4.7 ilustra uma matriz dividida em4×4 ladrilhos. Dentro de cada ladrilho, múltiplas iterações são executadas. Os ladrilhossão executados em uma certa ordem, onde o ladrilho primário, situado ao eixo diagonalda iteração correspondente, define a ordem da execução dos ladrilhos. Isso facilita, decerta forma, a localidade na memória cache, diminuindo valores constantes do tempocomputacional. Entretanto, ele não altera a complexidade do algoritmo Floyd-Warshall.O ganho de speedup situa-se entre 1,6x e 2x [39]. A Figura 4.8 representa, de formageral, a ordem de execução dos ladrilhos de uma matriz de distâncias em cada iteração. Oprimeiro ladrilho calculado é o de cor vermelha (cinza escuro) e, em seguida, os ladrilhoscinzas claro e, por último, os ladrilhos brancos.

Figura 4.7: Divisão de uma matriz de distâncias em ladrilhos.

Já à abordagem paralela utilizada por Harish e Narayanan [16] divide o algoritmoFloyd-Warshall em duas partes. Uma parte do algoritmo é executada na CPU, onde umafunção kernel é chamada várias vezes, como mostrado no Algoritmo 4.9. A segunda parte

Page 68: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 66

Figura 4.8: Solução sequencial proposta por Venkataraman etal. [39].

é executada na GPU, onde uma única função kernel é executada por O(|V 2|) threads.Cada thread é mapeada para um único elemento da matriz, consequentemente, cadabloco de threads trabalha sobre uma única parte da matriz. A Figura 4.9 ilustra essemapeamento de threads sobre a matriz de distância feito pelo algoritmo de Harish eNarayanan [16]. A função kernel é chamada O(|V |) vezes sequencialmente pela CPU,logo, várias comunicações são realizadas. Contudo, uma única transferência de dadossignificativa é feita, pois os dados são mantidos na memória da GPU até o final doprocessamento. De acordo com Xiao e Feng [41], uma grande parte do tempo de execuçãode uma aplicação CUDA é gasto no lançamento do kernel e na sincronização. Issoocorre quando há um uso intensivo de comunicação entre CPU e GPU. Entretanto,apesar desta limitação, essa solução oferece um ganho de speedup sobre a implementaçãosequencial [16].

Figura 4.9: Solução paralela do algoritmo Floyd-Warshall pro-posta por Harish e Narayanan [16].

Page 69: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 67

Algoritmo 4.9: Código CPU - Implementação Paralela do Algoritmo doFloyd-Warshall proposta por Harish et al. [16].

Entrada: Grafo G representado pela matriz de distâncias Anxn.;Saída: Caminho mínimo do grafo G representado pela matriz de distâncias

Anxn;

1 bloco_size← número máximo de threads por bloco2 Aloca memória para a matriz Anxn na GPU3 Copia a matriz Anxn para GPU4 tam_bloco← bloco_size × bloco_size

5 tam_grade← d( nbloco_size)e × d(

nbloco_size)e

6 para estagio = 0 até n−1 faça7 Invoca o kernel <tam_grade, tam_bloco >(Anxn, estagio) na GPU8 fim9 Copia a matriz Anxn da GPU para CPU

10 Libera o espaço na memória da GPU ocupado pela matriz Anxn

O algoritmo paralelo proposto por Katz e Kider [20] é baseado no algoritmosequencial implementado por Venkataraman et al. [39]. O algoritmo particiona a matrizde distâncias em blocos de tamanhos iguais. Cada bloco fica com 16×16 vértices detamanho. Dessa forma, o bloco pode ser facilmente mapeado para os blocos de threads.O algoritmo procede em estágios, cada um constituído por três fases. As fases sãoexecutadas por kernels. Em todos os estágios, um bloco primário é definido. Esse blocoprimário fica situado ao longo do eixo diagonal da matriz de distâncias, que se inicializana localização (0, 0), e o último bloco fica em (|V|-16, |V|-16), onde V é o conjunto devértices. Assim, o algoritmo consiste em |V |

16 iterações.A Fase 1 é a mais simples. Somente o bloco primário atual executa o algoritmo,

e um único SM da GPU permanece ativo. O bloco é obtido somente uma vez na memóriaglobal e levado para a memória compartilhada, de modo a acelerar a fase tanto quantopossível.

A Fase 2 manipula os blocos cujos valores são dependentes do bloco primário.Esses blocos estão localizados na mesma linha (eixo x) e coluna (eixo y) do bloco primáriocomputado. Tais blocos são chamados blocos simplesmente-dependentes [20]. Para arealização dessa fase, o bloco primário da matriz de distâncias é carregado para a memóriacompartilhada. Por exemplo, para uma matriz com n blocos por eixo, 2×(n - 1) blocos damatriz são computados e, consequentemente, o mesmo número de blocos de threads sãoorganizados no grid.

A Fase 3 computa os blocos restantes da matriz, conhecidos por blocosduplamente-dependentes, por dependerem da computação da linha e coluna do bloco pri-

Page 70: Implementações Paralelas para os Problemas do Fecho Transitivo e

4.3 Caminho Mínimo entre Todos os Pares de Vértices 68

mário. A Figura 4.10 fornece uma visualização de alto nível da execução das três fases.Vale ressaltar que cada fase é executada sequencialmente, resultando numa comunicaçãoconstante entre a CPU e a GPU.

Figura 4.10: Visão geral do algoritmo proposto por Katz e Ki-der [20].

Page 71: Implementações Paralelas para os Problemas do Fecho Transitivo e

CAPÍTULO 5Implementações Propostas

As características das arquiteturas gerais e da GPU foram apresentadas no Capí-tulo 2 e as particularidades paralelas da arquitura CUDA das GPGPUs foram descritas noCapítulo 3. No presente capítulo, são propostas soluções paralelas que fazem o uso dascaracterísticas apresentadas da arquitetura CUDA para resolver os problemas abordadosno Capítulo 4.

Na Seção 5.1, soluções paralelas baseadas no algoritmo sequencial de Warshalle Floyd-Warshall, Algoritmo 4.1 e 4.6, são detalhadas. Uma implementação paralelabaseada no algoritmo sequencial BFS (Algoritmo 4.2) para resolver o fecho transitivoé descrito na Seção 5.2. Em seguida, a Seção 5.3 destina-se à apresentação de umaabordagem baseada no algoritmo de Dijkstra (Algoritmo 4.7).

5.1 Soluções Paralelas baseadas nos Algoritmos deWarshall e Floyd-Warshall

O cálculo tanto do fecho transitivo quanto do menor caminho entre todos osvértices de um grafo necessita passar por uma série de iterações, sendo que uma iteraçãodepende do resultado da iteração anterior para que possa ser efetivada. Devido a essadependência de dados, algoritmos como Warshall, Floyd-Warshall e outros com a mesmacaracterística não podem ser executados totalmente em paralelo. Em contrapartida, essesalgoritmos apresentam a vantagem de utilizar uma única estrutura de dados para obter oresultado esperado.

Observado esses detalhes dos algoritmos sequenciais, nesta seção são apresen-tadas duas soluções paralelas na GPU baseadas nos algoritmos sequencias de Warshall eFloyd-Warshall. Ambas possuem soluções semelhantes, diferenciando somente no núcleodos algoritmos. Portanto, as soluções, de maneira geral, valem para os dois problemas, fe-cho transitivo e menor caminho entre todos os pares de vértices.

De acordo com o Capítulo 4, os algoritmo Warshall (Algoritmo 4.1) e Floyd-Warshall (Algoritmo 4.6) possuem três laços, dois dos quais são independentes (linhas

Page 72: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 70

2 e 3), ou seja, podem ser executados em qualquer ordem sem afetar a solução final. Àvista disso, esses laços podem ser simplesmente representados por blocos de threads comduas dimensões, x e y, nos algoritmos paralelos para GPU e, assim, a lógica do algoritmosequencial é mantida. Dessa forma, as soluções paralelas já reduzem a complexidade detempo comparado com o sequencial. Porém, o primeiro laço,representado pela linha 1dos Algoritmos Sequenciais 4.1 e 4.6, apresenta dependência entre iterações, na qualimpossibilita a retirada deste laço nas soluções paralelas propostas.

A primeira solução proposta apresenta essa dependência de dados do laçoda linha 1 dos Algoritmos 4.1 e 4.6 no código da GPU, com o objetivo de evitarcomunicações excessivas entre a GPU e a CPU através do barramento PCI Express. Paraisso, é necessário o uso da ocupação intensiva nos SMs, descrita melhor na Seção 3.4.De acordo com Xiao e Feng [41], grande parte do tempo de execução de uma aplicaçãoCUDA é gasto no lançamento de kernels e sincronizações. Isso ocorre principalmentequando há um uso excessivo de comunicação entre os dispositivos. A ocupação intensivados SMs evita que qualquer SM fique ocioso devido à dependência de dados no momentoda computação (Seção 3.4).

O uso da ocupação intensiva na primeira versão da solução faz a implementaçãoconter barreiras de sincronização entre blocos de threads. Essas barreiras estão localizadasde modo que os blocos de threads sejam sincronizados antes de passar para a próximaiteração. Isso garante a integridade de dados devido às dependências contidas na linha 1dos Algoritmos 4.1 e 4.6.

A Figura 5.1 ilustra uma abstração da solução paralela utilizando a técnicade ocupação intensiva sobre uma matriz de adjacência. Os quadrados enumerados (0a 3) representam os blocos de threads, enquanto os quadrados menores apresentamos elementos da matriz e os retângulos enumerados ilustram a área de processamentopertencente ao bloco de threads de sua enumeração. Considerando que o algoritmo éprocessado sobre uma plataforma GPU com quatro SMs, onde cada qual suporta um únicobloco de threads em uma unidade de tempo. Então, lança-se o kernel, que representao algoritmo de Warshall ou Floyd-Warshall, somente com quatro blocos de threads,conforme ilustrado, de modo que a GPU fique totalmente ocupada sem que haja threads

para serem escalonadas. Dessa forma, cada bloco de threads terá que processar diversaspartes da matriz, ao invés de uma única parte, como das soluções paralelas apresentadasnas Seções 4.2.3 e 4.3.3.

O Algoritmo 5.1 descreve a implementação da fase sequencial do algoritmoparalelo que será executado no host. Observa-se que o algoritmo recebe de entrada umamatriz de adjacência Mn×n, a quantidade de SMs da GPU que o código será executado,o tamanho do bloco de threads e uma constante k. A quantidade de SMs e a constantek definem a quantidade de blocos de threads na grade (grid). Os valores do tamanho do

Page 73: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 71

Figura 5.1: Exemplo de ocupação intensiva dos SM’s: (a) Númerode blocos de thread; (b) Divisão das partes da matrizpor bloco de threads.

bloco de threads e da constante k ditam a quantidade de blocos que cada SM da GPU iráprocessar simultaneamente. Esses valores são obtidos por base de testes que variam deacordo com o modelo da GPU. Esse Algoritmo 5.1 necessita de uma única chamada aokernel CUDA, ou seja, uma única chamada à parte paralela do algoritmo, como é mostradona linha 6. Isso demonstra que toda lógica dos algoritmos sequenciais, Algoritmos 4.1e 4.6, é implementada na fase paralela do modelo CUDA. Consequentemente, uma únicacomunicação é realizada entre os dispositivos envolvidos, deixando a maior parte dotempo gasto na computação do algoritmo.

Page 74: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 72

Algoritmo 5.1: Código da CPU - Implementação Paralela Proposta dosAlgoritmos do Warshall e Floyd-Warshall em Blocos

Entrada: Matriz de Adjacência Mv×v do grafo G=(E,V), número de SMsnumero_SM, tamanho do bloco de threads bloco_size, constantek;

Saída: Matriz de Adjacência Mv×v com a solução do problema sobre ografo G=(E,V);

1 numero_Blocos← numero_SM * k2 Aloca memória para a matriz Mv×nv na GPU3 Copia a matriz Mv×v para GPU4 dimBloco← bloco_size × bloco_size

5 dimGrid← numero_Blocos

6 Invoca o kernel_Imp_Paralela_Em_Blocos <tam_grade,tam_bloco >(Mv×v)na GPU

7 Copia a matriz Mv×v da GPU para CPU8 Libera o espaço na memória da GPU ocupado pela matriz Mv×v

O kernel lançado na linha 6 do Algoritmo 5.1 é detalhado no Algoritmo 5.2. Elerepresenta a fase paralela do modelo de programação CUDA e contém toda lógica dasolução paralela, como descrito no Algoritmo 5.2. Esse algoritmo, inicialmente, obtém osidentificadores das threads no eixo X e Y. Em seguida, as proporções entre quantidade devértices e as de blocos de threads tanto no eixo X quanto no eixo Y são obtidas (linhas4 e 5) com objetivo de armazenar as quantidades de divisões da matriz que cada blocoirá trabalhar em cada eixo. O laço da linha 7 representa o laço da linha 1 nos algoritmossequenciais de Warshall e Floyd-Warshall (Algoritmo 4.1 e 4.6, respectivamente), ondelocaliza-se a dependência de dados dos respectivos algoritmos. Os laços das linhas 9 e 11direcionam os blocos de threads para as divisões da matriz que correspondem a eles. Issoé realizado através das variáveis idY e idX que indicam quais partes dos eixos da matrizque cada bloco irá trabalhar. Antes de cada laço, os índices i e j, que correspondem a linhae a coluna da matriz respectivamente, são inicializados. O cálculo do fecho transitivo oucaminho mínimo entre todos vértices encontra-se na chamada da função Calculo na linha12. A função Calculo é descrita pelos algoritmos 5.3 e 5.4, sendo este para o problema docaminho mínimo e aquele para o fecho transitivo.

No final de cada iteração do laço da linha 7, uma barreira sincronização érealizada entre todos os blocos de threads da GPU. Essa barreira garante a sequência deiterações necessárias para resolução do problema do fecho transitivo e APSP. Como vistono Capítulo 3, não existe uma sincronização da API CUDA que sincronize todos os blocosde threads de uma grade. Por isso, houve a necessidade de implementar uma barreira que

Page 75: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 73

atendesse esse caso. Desse modo, foi utilizado o método de sincronização proposto porXiao e Feng [41] que não utiliza operações atômicas para obter a sincronização global.

Algoritmo 5.2: Código da GPU - Implementação Paralela Proposta dosAlgoritmos do Warshall e Floyd-Warshall em Blocos

Entrada: Matriz de adjacência Mv×v do grafo G=(E,V);Saída: Matriz de adjacência Mv×v com a solução do problema sobre o grafo

G=(E,V);

1 para todo id em paralelo faça2 Obtém os identificadores em X e Y das threads na grade3 Obtém o tamanho da grade de threads em Tam_X e Tam_Y

4 proporcaoX← |V |Tam_X

5 proporcaoY ← |V |Tam_Y

6 n← |V |7 para v← 0; v < n;v++ faça8 i← Identificador da thread em Y

9 para idY ← 0; idY < proporcaoY ; idY ++ faça10 j← Identificador da thread em X

11 para idX ← 0; idX < proporcaoX; idX ++ faça12 Calculo(M, v, i, j)13 j← j+Tam_X

14 fim15 i← i+Tam_Y

16 fim17 Barreira_Sincronização_Entre_Blocos

18 fim19 fim

Algoritmo 5.3: Função - Cálculo do Fecho Transitivo de um Grafo

Entrada: (1) Matriz de adjacência binária Mv×v do grafo G=(E,V), (2)vértice v e (3) índices i e j;

1 se M[v][ j] = 1 E M[i][v] = 1 então2 M[i][ j]← 13 fim

Page 76: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 74

Algoritmo 5.4: Função - Cálculo do Caminho Mínimo Entre Todos os Paresde Vértices de um Grafo (APSP)

Entrada: (1) Matriz de distância Mv×v do grafo G=(E,V), (2) vértice v e (3)índices i e j;

1 se M[v][ j]+M[i][v]< M[i][ j] então2 M[i][ j]←M[v][ j]+M[i][v]

3 fim

A Figura 5.2 ilustra, de forma semelhante à Figura 5.1, as iterações do laço 7do Algoritmo 5.2 sobre a matriz de adjacência com as divisões destacadas de cada blocode threads irá trabalhar. Os retângulos em amarelo, horizontal e vertical, representam alocalização dos elementos da matriz (linha 12) que são necessários para computação dafunção Calculo em uma dada iteração. A primeira, segunda e a terceira iterações sãorepresentadas em (a), (b) e (c), respectivamente. A N-essíma iteração é mostrada em (d).Observa-se que a matriz de adjacência é dividida em blocos. Por isso, o termo “em blocos”está presente no nome das implementações dos algoritmos 5.1 e 5.2.

Essa primeira solução apresentada reduz a comunicação entre a CPU e a GPU e,por sua vez, não utiliza de forma eficiente a hieraquia de memória da GPU nas iteraçõesdo laço 7 dos algoritmos 5.1 e 5.2.

Com o objetivo de obter um melhor desempenho computacional em relação aoestado da arte das implementações paralelas dos algoritmos Warshall e Floyd-Warshall,propõe-se uma segunda abordagem cuja solução faz uso de dois aspectos principais daGPU: localidade de dados e ocupação intensiva dos SMs.

A localidade de dados relaciona-se com a hierarquia de memória disponívelna arquitetura CUDA, onde o acesso à memória global possui uma latência muitoalta em relação à memória compartilhada. Para uma melhor localidade dos dados, aimplementação proposta baseia-se naquela sequencial concebida por Venkataraman et

al. [39]. A matriz de distâncias é dividida em blocos de tamanhos iguais. Esses blocossão levados inteiramente para a memória compartilhada e, somente então, é realizadoo cálculo do caminho mínimo ou do fecho transitivo. O algoritmo segue em estágios,semelhante à implementação de Katz e Kider [20]. Em cada estágio, que é subdivididoem duas fases, um bloco primário é definido.

A Fase 1 do algoritmo realiza o cálculo do caminho mínimo ou fecho transitivono bloco primário e nos seus respectivos eixos x e y, ou seja, nos blocos que pertencemà linha e à coluna do bloco primário. Para tal, aplica-se uma ocupação intensiva dos SMse uma divisão em etapas dessa fase, onde todas as etapas são executadas em um únicokernel.

Page 77: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 75

Figura 5.2: Iterações da implementação paralela descrita no Al-goritmo 5.2

A primeira etapa consiste no armazenamento do bloco primário na memóriacompartilhada e, em seguida, na execução do algoritmo neste bloco primário, em cadabloco de threads. Como o tempo de vida das threads é alterado para persistir (ocupaçãointensiva) até o final da execução do kernel, cada bloco de threads busca e armazena umaúnica vez o bloco primário atual, onde encontra-se a dependência de dados. Portanto,o bloco primário é replicado conforme a quantidade de blocos de threads lançados nokernel. A Figura 5.3 exemplifica essa primeira etapa. Como pode ser observado, há 3SMs na GPU, e cada um executa um único bloco de threads.

Na segunda etapa, os blocos de threads executam o algoritmo na linha e colunado atual bloco primário. Entretanto, um bloco de threads executa em mais de um bloco damatriz de adjacências, sendo definida a sequência de tarefas antes da execução. Ressalta-se que um SM diferente a cada estágio é responsável por armazenar o atual bloco primário.A Figura 5.4 ilusta essa segunda etapa, onde em (a) a linha está sendo executada; e em (b)a coluna está sendo executada.mostra a proposta de implementação do

Na Fase 2 do algoritmo, os blocos restantes da matriz de adjacências são

Page 78: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 76

Figura 5.3: Primeira etapa da Fase 1 da implementação do Floyd-Warshall.

calculados de forma convencional proporcionado pelo modelo de programação CUDA.O tempo de vida das threads não é persistente até o fim do kernel. Caso a matriz sejamaior que a capacidade de threads em execução, haverá threads esperando para seremescalonadas. Esta fase é realizada somente em um único kernel. Portanto, os blocos dalinha e da coluna correspondentes ao bloco de threads a ser computado são armazenadosna memória compartilhada.

O Algoritmo 5.5 mostra a estratégia da implementação no código da CPU,onde a variável qtdPartes indica a proporção entre o tamanho da matriz de distâncias(tamanho do grafo) com o número de SMs da GPU e tamanho do bloco de threads.Na GPU, ela indicará quantas partes da matriz de distâncias que cada bloco de threads

irá executar. então indica também a quantidade de partes da matriz com que cada SMirá trabalhar. A quantidade exata de blocos de threads para cada kernel é ditado pelasvariáveis dimGrid_ f ase1 e dimGrid_ f ase2.

Page 79: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.1 Soluções Paralelas baseadas nos Algoritmos de Warshall e Floyd-Warshall 77

Figura 5.4: Segunda etapa da Fase 1 da implementação do Floyd-Warshall.

Page 80: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 78

Algoritmo 5.5: Código da CPU - Implementação Proposta dos Algoritmos Warshalle Floyd-Warshall

Entrada: Grafo G = (V,E) representado pela matriz An×n, bloco_size, numeroSM;Saída: Caminho Mínimo/Fecho Transitivo do grafo G representado pela matriz

An×n;

1 n←| Anxn |2 numeroBlocos_SM← numeroSM

3 numeroBlocos← d( nbloco_size)e

4 qtdPartes← ( n(bloco_size × numeroBlocos_SM))

5 Aloca memória para a matriz An×n na GPU6 Copia a matriz An×n para GPU7 dimBloco← bloco_size × bloco_size

8 dimGrid_ f ase1← numeroBlocos_SM

9 dimGrid_ f ase2← numeroBlocos×numeroBlocos

10 enquanto estagio = 0 até n faça11 Invoca o kernel_fase_1 <dimGrid_ f ase1, dimBloco >(An×n, n,

numeroBlocos_SM, qtdPartes)12 Invoca o kernel_fase_2 <dimGrid_ f ase2, dimBloco >(An×n, n, estagio)13 estagio← estagio+bloco_size

14 fim15 Copia a matriz Av×v da GPU para CPU16 Libera o espaço na memória da GPU ocupado pela matriz An×n

5.2 Solução Paralela baseada no Algoritmo BFS para oProblema do Fecho Transitivo

O algoritmo sequencial BFS, descrito no Capítulo 4 (Algoritmo 4.2), percorre ografo em fronteiras ou níveis. Primeiro, ele visita todos os vértices de uma única fronteiraantes de prosseguir para a próxima. Essa visita vai ocorrendo de forma irregular, ou seja,não contém uma direção predefinida no grafo.

As soluções para o problema do fecho transitivo, como mostrado na Seção 4.2do Capítulo 4, necessitam de etapas sequenciais. O algoritmo sequencial BFS realizaessas etapas sequenciais através de visitas ordenadas nas fronteiras. Para que a soluçãodo algoritmo sequencial BFS seja válida para o problema do fecho transitivo, o algoritmoprecisa ser executado |V| vezes, cada um com vértice de origem diferente.

Visto isso, a implementação paralela proposta do BFS nesta seção envolveparalelizar os acessos a vértices de uma mesma fronteira e executar concorrentemente

Page 81: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 79

vários algoritmos BFS, cada qual com um vértice de origem distinto. Essa implementaçãotrabalha sobre a representação compactada do grafo de entrada, apresentada na Seção 4.1do Capítulo 4, e a representação da matriz de adjacência. Sendo a primeira usada paraarmazenar (escrever) o fecho transitivo do grafo e aquela utilizada somente para leituracom objetivo de aglutinar os acessos à memória global, de alta latência, e minimizaracessos desnecessários à memória quando não houver aresta.

A invocação de várias instâncias do algoritmo do BFS na solução aqui propostaocorre na execução dos blocos de threads. Pois, cada bloco computará um algoritmoparalelo BFS com um vértice de origem distinto. Dessa forma, cada Multiprocessador(SM) fica responsável por calcular uma quantidade predefinida de BFS concorrentemente,ou seja, a quantidade de blocos de threads que cada SM suporta ao mesmo tempo seráigual ao número de algoritmos BFSs sendo executados em paralelo. Sendo que estaquantidade é definida no lançamento do kernel, onde o número de threads por bloco éinformado, em tempo de compilação, onde é dado o limite de registradores por thread.Uma consequência de implementar os algoritmos BFS por bloco é que o hardware daGPU é mantido totalmente ocupado durante a execução. Torna-se desnecessário qualquercomunicação ou sincronização entre blocos de threads, pois cada instância do algoritmoBFS é independente de outra. Além disso, o uso de uma GPU com número maior de SMs,implica num maior número de algoritmos paralelos BFS executando simultaneamente,tornando o algoritmo totalmente escalável. A ilustração dessa paralelização de váriosalgoritmos BFS na GPU é mostrado na Figura 5.5.

Figura 5.5: Paralelização de vários algoritmos BFS na GPU

A solução paralela proposta faz o uso de três listas de tamanho O(|V |). Sendoduas para representação das fronteiras e uma para os vértices já visitados. Todas sãomarcadas com 0 ou 1, indicando se o vértice foi visitado, no caso da lista de visitados, ouse o vértice está contido nas fronteiras atual ou na próxima no caso das listas de fronteiras.

No início do algoritmo sequencial BFS, a fila contém somente o vértice deorigem. Em cada etapa, o vértice de origem é retirado e todos seus vizinhos são visitadose adicionados no final da fila. Esse processo é repetido até que todos os vértices sejamvisitados. Considerando isso, podemos dividir os vértices na fila do algoritmo BFS em

Page 82: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 80

dois tipos, os vértices contidos na fronteira atual e na fronteira posterior. Sendo assim, afila do algoritmo sequencial é representada na solução paralela proposta por duas listas defronteiras, uma para fronteira atual e a outra para a próxima fronteira. A Figura 5.6 ilustraas fronteiras de um grafo e as listas das fronteiras.

Figura 5.6: Listas das Fronteiras do algoritmo paralelo proposto

A arquitetura CUDA, como descrito no Capítulo 3, trabalha com uma hierarquiade threads leves (grades, blocos de threads e threads) sobre uma hierarquia de proces-sadores. Como a estrutura do grafo é irregular, uma implementação paralela ingênua doBFS na GPU seria mapear uma thread para cada vértice. Isso levaria a algumas threads

terem muito trabalho, enquanto outras, pouco, resultando em uma serialização de tarefas.Ao invés de atribuir uma tarefa (vértice) para cada thread, a solução aqui proposta atribuium conjunto de tarefas a um conjunto de threads para obter um resultado, que neste casoé visitar todos os vértices vizinhos a partir de um dado vértice. Esse conjunto de threads

é o mesmo conjunto atribuído à warp, que é descrito no Capítulo 3.O conjunto de threads da warp foi escolhido, pois as threads de uma mesma

warp apresentam algumas pecularidades que ajudam na comunicação e sincronizaçãoentre elas. Em relação a essas threads, podem ser citadas as seguintes características:

• São executadas em um mesmo contexto e compartilham os recursos de um únicoSM;• Os processadores de fluxo de um único SM trabalham sobre a forma de SIMD

síncrono, ou seja, trabalham sobre uma mesma instrução ao mesmo tempo. Conse-quentemente, as threads da warp são síncronas;• Uma quantidade de threads adequada para o hardware do SM, pois os acessos à

memória podem ser otimizados;• Possuem funções especiais da API CUDA que facilitam na comunicação sem

precisar de acessar memória compartilhada, por exemplo, __popc, __ballot, __ffs;

As warps contém 32 threads. Sendo assim, a lista de fronteira atual é divididaem partes iguais de tamanho 32 vértices, cada parte é mapeada para uma warp. Caso a

Page 83: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 81

quantidade de partição de 32 elementos da lista seja maior que a quantidade de warps, aswarps conterão mais de uma parte da lista da fronteira. A Figura 5.7 ilustra essa divisãode trabalho sobre a lista de fronteira atual.

Figura 5.7: Divisão de trabalhos entre as warps

Os vértices contidos em uma mesma parte da lista da fronteira atual são proces-sados sequencialmente pela warp, ou seja, todas as thread da warp visitam ao mesmotempo a lista de adjacência de um único vértice. Como um bloco contém várias warps, nomáximo 32 warps na arquitetura Fermi, e todo bloco trabalha sobre um mesmo algoritmoparalelo BFS, então listas de adjacências de vértices distintos são visitados ao mesmotempo, como ilustrado na Figura 5.8. Dessa forma, há dois níveis de paralelização. O pri-meiro ocorre na fronteira atual com a divisão de tarefas por warp e o segundo acontece nafronteira seguinte, onde várias listas de adjacências são visitadas em paralelo, cada qualcom várias threads de uma mesma warp visitando-a. Neste momento que se encontra asegunda paralelização da solução proposta, sendo a primeira, a execução de vários algo-ritmos BFS em paralelo como ilustrado na Figura 5.5. A Figura 5.8 mostra a divisão dalista da fronteira atual entre warps e as visitas que cada warp faz na lista de adjacência decada vértice.

O Algoritmo 5.6 descreve a implementação proposta. Nele, as linhas 1 a 3 criame inicializam variáveis e estruturas de dados do algoritmo. As linhas 4 a 6 adicionamos identificadores de threads da warp na lista TidW que são utilizadas para identificar asthreads de uma mesma warp. Na linha 7, o vértice de origem é adicionado na fronteiraatual. O laço da linha 10 é executado em paralelo e nas linhas 10 e 11 a comunicaçãodas threads de uma mesma warp para obter os vértices contidos na parte correspondenteda warp na fronteira atual. Isso para que a warp toda visite a mesma lista de adjacência,ocorrendo assim o paralelismo. O laço da linha 14 é, também, executado em paralelo. Olaço da linha 15 é executado sequencialmente para toda thread e indica a visita de listas deadjacências em sequência pela warp. Porém, para warps diferentes, listas de adjcências

Page 84: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 82

Figura 5.8: Paralelização de visitas a vértices contidos nas fron-teiras

são visitadas em paralelo. O algoritmo finaliza quando não há vértices na fronteira atual,ou seja, quando não existem vértices não-visitados. Essa condição de parada é apresentadapelo laço da linha 9. Observa-se que antes de verificar, há uma barreira de sincronização.Isso garante que cada fronteira do grafo será visitada somente quando todas anterioresforem. Assim, o algoritmo caminha entre fronteiras, garantindo que a complexidade doalgoritmo seja dependente da quantidade de fronteiras contidas no grafo.

Page 85: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 83

Algoritmo 5.6: Implementação Paralela Proposta do BFS

Entrada: (1) Vértice origem s, (2) matriz de adjacência binária Mv×v e (3)estruturas compactadas listaVertices e listaArestas;

Saída: Fecho Transitivo do grafo G representado pela Matriz de AdjacênciaBinária Mv×v;

1 Obtém os identificadores globais e locais na warp da thread e oidentificador da warp (id, idW e idTW , respectivamente)

2 Cria as listas da fronteira atual Fra e da próxima fronteira Frprox, devisitados Visitados de tamanhos O(V ) e as listas W e TidW de 32 elementos

3 Fra← /0; Frprox← /0; Visitados← /0

4 para todo id em paralelo faça5 TidW [idTW ]← id

6 fim7 Fra← s

8 Barreira de sincronização9 enquanto Fra 6= /0 faça

10 para todo {u} ∈ Fra em paralelo faça11 W ←{{u} ∈ Fra e id ∈ TidW |u = 1 ou u = 0}12 quantidadeVerticesWarp← |Q|, onde Q = {{u} ∈W |u = 1}13 fim14 para todo id em paralelo faça15 para i = 0; i < quantidadeVerticesWarp; i++ faça16 posicaoBit← posicao[u], onde {posicao[u] ∈ |W | e

{u} ∈W |u = 1 e u é o primeiro bit 1 da lista}17 vértice v← Obtem_Vertice(posicaoBit)

18 proporcaoWarp← d(|Ad j[v]|/TAMANHO_WARP)e19 para j = 0; j < proporcaoWarp; j++ faça20 posicao← proporcaoWarp×TAMANHO_WARP+ idTW

21 vértice u← listaArestas[posicao]

22 se NÃO Visitado[u] então23 Frprox[u]← 1; Mvu← 124 fim25 fim26 W ←W −W [posicaoBit]; Visitado[u]← 1

27 fim28 fim29 Fra[v]← 0; Fra← Frprox; Frprox← /0

30 Barreira de sincronização

31 fim

Page 86: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.2 Solução Paralela baseada no Algoritmo BFS para o Problema do Fecho Transitivo 84

Como todas as threads de uma mesma warp trabalham sobre a mesma listade adjacência, torna-se necessário uma comunicação eficiente, pois elas executam demaneira síncrona. A comunicação entre as threads de uma mesma warp ocorre daseguinte forma. Primeiro, cada thread da warp verifica se há um vértice na posiçãocorrespondente a sua posição na warp, obtendo o valor 0 ou 1. Em seguida, uma avaliaçãosobre todos esses valores é feita e, assim, retorna para todas elas um valor inteiro cujo N-

éssimo bit está definido igual a 1, se e somente se o valor avaliado é 1 para a N-éssimo

thread da warp, como descrito na linha 1 do Algoritmo 5.6. A Figura 5.9 ilustra esseprocesso. Nela, as 32 threads de uma warp obtêm os valores contidos na parte listada fronteira atual e, então, todos os valores são combinados formando um inteiro de 4bytes. Esse inteiro é passado por broadcast para todas as threads ativas participantes doprocesso.

Figura 5.9: Comunicação entre as threads ativas de uma warpsobre uma parte da lista da fronteira atual

Após obtido esse valor inteiro, cada thread irá descobrir qual a sequência devértices a ser visitada. Sendo essa sequência única para toda a warp, assim, todas asthreads visitam a lista de adjacência de um mesmo vértice. Essa visita é feita por basedos valores de bits contidos nos 4 bytes retornados. Cada bit com valor 1 nos 4 bytes,indica um vértice na parte da lista da fronteira atual, ou seja, indica uma lista de vizinhospara ser visitada. Assim, evita-se qualquer outra comunicação ou sincronização entre elas.

Ao finalizar a comunicação, cada thread irá trabalhar sobre os vértices na lista debits em sequência, obtendo o próximo vértice a partir da posição dos bits com valor 1. Paraisso, a função Obtem_Vertice é chamada (linha 17). Após obtido o vértice, a quantidadede visitas por thread que será feita na lista de adjacência é obtida na linha 18. Com isso,as visitas são feitas em sequência, verificando se o vértice foi visitado (linha 22), caso nãoseja, ele é adicionado na lista da próxima fronteira e marcado como visitado (linha 23).

Page 87: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 85

5.3 Solução Paralela baseada no Algoritmo Dijkstrapara o Problema APSP

O problema APSP pode ser resolvido por soluções que resolvam o problemaSSSP, entretanto, é necessário que elas sejam aplicadas para todos os vértices do grafo.Nesta seção, é proposta uma solução paralela do algoritmo sequencial de Dijkstra [12](Algoritmo 4.7) para o problema APSP que não utiliza a fila de prioridades.

A solução aqui proposta trabalha sobre uma matriz de adjacência e uma estruturacompactada, ambas explicadas na Seção 4.1 do Capítulo 4. Sendo a matriz utilizadasomente para leitura e estrutura compactada somente para escrita. A estrutura compactadareduz acessos não-aglutinados, ou seja, acessos em localidades não-próximas à memóriaglobal e, consequentemente, facilita a verificação de existência de arestas. Já a matriz deadjacência é usada para armazenar os caminhos mínimos entre todos os pares de vértices.Com isso, a complexidade de espaço da implementação proposta é de ordem O(|V |2).

Essa solução paralela do algoritmo de Dijkstra envolve dois níveis de parale-lização. O primeiro nível localiza-se na execução de vários algoritmos de Dijkstra emparalelo; e o segundo acontece dentro de cada instância do algoritmo.

A paralelização no nível de execução simultânea de diversos algoritmos deDijkstra ocorre devido à concorrência de blocos na GPU, pois cada algoritmo é executadopor um bloco de threads distinto, sendo este executado por um único Multiprocessador deFluxo (SM). Essa forma de mapeamento de um algoritmo para um único bloco é adequadoao modelo de programação CUDA, por causa que os blocos de threads são processadosindependentemente. Com isso, ao simples aumento do número de SMs na GPU leva aimplementação a ter ganho de desempenho sem qualquer alteração no código, tornando,o algoritmo escalável em relação ao número de SMs. A execução dessa paralelizaçãoocorre como ilustrado na Figura 5.10, onde cada SM recebe um fluxo de instruções que,neste caso, são os algoritmos de Dijkstra, cada qual com vértice de origem distinto.

Figura 5.10: Execução simultânea de vários algoritmos de Dijks-tra.

Page 88: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 86

O segundo nível de paralelização encontra-se dentro de cada instância do algo-ritmo proposto. A concorrência acontece quando várias threads de um bloco visitam listasde adjacências de diversos vértices distintos de um mesma fronteira. Essa paralelizaçãocomporta-se de forma semelhante à implementação paralela proposta do algoritmo BFS(Seção 5.2). No algoritmo BFS tanto no sequencial (Algoritmo 4.2) quanto no paralelo(Algoritmo 5.6), caminha-se em fronteiras do grafo, ou seja, uma fronteira é alcançada so-mente quando todos os vértices da fronteira anterior tiverem sido visitados. Além disso,um vértice é visitado somente uma única vez. No algoritmo paralelo proposto baseado node Dijkstra, caminha-se, também, em fronteiras, mesmo que vértices possam ser visitadosvárias vezes. O número de visitas de um vértice depende das fronteiras ou níveis que omesmo pertence. A Figura 5.11 ilustra as visitas que ocorrem nas fronteiras do grafo di-rigido e ponderado, representado em (a), por uma única instância do algoritmo propostocom vértice de origem em 1. Observa-se que os vértices 2 e 5 foram visitados duas vezes,isso porque o vértice 2 pertence aos 1o e 3o níveis e o vértice 5, aos 2o e 3o níveis. Dessaforma, os valores dos caminhos são verificados em cada visita.

Figura 5.11: Atualização do menor caminho entre fronteiras.

As fronteiras são representadas por duas listas, a atual e a próxima. Essas listassão binárias com tamanho |V | e têm o objetivo de marcar a existência de vértices nasfronteiras. Uma terceira lista de mesmo tamanho é utilizada para armazenar o caminhomínimo de todos os vértices a partir do vértice de origem. A Figura 5.6 representa asfronteiras do grafo e as duas listas das fronteiras utilizadas nesta solução.

Page 89: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 87

O mapeamento das threads de um bloco é feito de forma idêntica ao utilizadona solução paralela do BFS (Seção 5.2), ou seja, uma quantidade de tarefas (vértices) émapeada para um conjunto de threads (warp). A Figura 5.7 ilustra esse mapeamento dewarps na lista da fronteira atual. O processamento de cada parte da lista da fronteira atualé feito em paralelo pelas warps, embora dentro de cada uma (contendo 32 vértices) aexecução ocorre de maneira sequencial. As etapas desse processamento nas fronteiras dografo podem ser visualizadas na Figura 5.9. Nessa forma de executar, várias listas de ad-jacências são visitadas simultaneamente e, consequentemente, distâncias são calculadasao mesmo tempo. Além disso, não é necessário qualquer comunicação entre as warps,dispensando, assim, sincronizações entre elas. Embora, exista a necessidade de comuni-cação entre threads de uma mesma warp, elas não precisam de sincronização explícita,pois as threads da warp são síncronas. Essa comunicação entre threads de uma mesmawarp ocorre de forma semelhante ao algoritmo paralelo proposto do BFS que pode servisualizada na Figura 5.9.

O Algoritmo 5.7 representa a implementação paralela proposta do algoritmode Dijkstra e, por sua vez, representa o kernel CUDA. Nesse algoritmo, a linha 1cria e inicializa variáveis e estruturas de dados do algoritmo. A linha 2 adiciona osidentificadores de threads da warp na lista TidW que é utilizada para identificar as threads

de uma mesma warp. A linha 5 atribui o valor infinito nos caminhos mínimos de todosos vértices na lista listaCaminhoMinimo. Na linha 7, o vértice de origem é adicionado nafronteira atual e o valor do caminho mínimo é atualizado como 0. O laço da linha 10 éexecutado em paralelo, sendo que nas linhas 11 e 12 ocorre a comunicação das threads

de uma mesma warp para obter os vértices contidos na parte correspondente da warp

na fronteira atual. Isso permite com que toda a warp visita a mesma lista de adjacência,explorando paralelismo. O laço da linha 14 também é executado em paralelo. O laço dalinha 15 é executado sequencialmente por toda thread e indica visitas sequenciais naslistas de adjacências pela warp. Porém, para warps diferentes, listas de adjcências sãovisitadas em paralelo.

A função de relaxamento das arestas é representado pelo Algoritmo 5.8, o qualé chamado pelo Algoritmo 5.7 na linha 22. Essa função é executada de forma atômica,ou seja, as threads que tentam acessar a distância de um mesmo vértice são serializadas.Essa ação de serialização é chamada de lock ou operação atômica. Isso ocorre quando namesma fronteira, há uma lista de vértices v0,v1, ...,vk que possuem a aresta (vi,u), sendo0≤ i≤ k e vi,u∈V . Na linha 28, o vértice visitado é removido da lista da fronteira se nãohouver uma atualização por uma thread no valor do caminho mínimo. Isso evita que seuse uma distância que não seja a menor. Na linha 30, há uma troca de listas das fronteiras,onde a fronteira atual recebe os vértices da próxima fronteira e a lista da próxima fronteiraé inicializada.

Page 90: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 88

O algoritmo finaliza quando não há vértices na fronteira atual, ou seja, quandonão existem vértices não-visitados. Essa condição de parada é apresentada pelo laço dalinha 9. Observa-se que antes de verificar a condição, uma barreira de sincronização (linha31). Isso garante que cada fronteira do grafo será visitada somente quando todas anterioresforem. Assim, o algoritmo caminha entre fronteiras, garantindo que a complexidade doalgoritmo seja dependente da quantidade de fronteiras contidas no grafo.

Page 91: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 89

Algoritmo 5.7: Implementação Paralela Proposta do Dijkstra

Entrada: (1) Vértice origem s, (2) matriz de distâncias Dv×v e (3) estruturascompactadas listaVertices. listaArestas e listaDistancias;

Saída: Caminho mínimo do grafo G representado pela Matriz de Distância Dv×v;

1 Obtém os identificadores globais e locais na warp da thread e o identificador dawarp (id, idW e idTW , respectivamente)

2 Cria as listas da fronteira atual Fra e da próxima fronteira Frprox, de visitadosVisitados de tamanhos O(V ) e as listas W e TidW de 32 elementos

3 Fra← /0; Frprox← /0

4 para todo id em paralelo faça5 TidW [idTW ]← id; CaminhoMinimo[id]← ∞

6 fim7 Fra←{s}; CaminhoMinimo[s]← 08 Barreira de sincronização9 enquanto Fra 6= /0 faça

10 para todo {u} ∈ Fra em paralelo faça11 W ←{u ∈ Fra e id ∈ TidW |u = 1 ou u = 0}12 quantidadeVerticesWarp← |Q|, onde Q = {u ∈W |u = 1}13 fim14 para todo id em paralelo faça15 para i = 0; i < quantidadeVerticesWarp; i++ faça16 posicaoBit← posicao[u], onde {posicao[u] ∈ |W | e u ∈W |u = 1 e u é

o primeiro bit 1 da lista}17 vértice v← Obtem_Vertice(posicaoBit)

18 proporcaoWarp← d(|Ad j[v]|/TAMANHO_WARP)e19 para j = 0; j < proporcaoWarp; j++ faça20 posicao← proporcaoWarp×TAMANHO_WARP+ idTW

21 posicao← j×TAMANHO_WARP+ idTW

22 vértice u← listaArestas[posicao]

23 RELAXAMENTO_ARESTAS(Frprox, listaCaminhoMinimo,v,u)

24 fim25 W ←W −W [posicaoBit]

26 fim27 fim28 se NÃO houve atualização do valor listaCaminhoMinimo[v] então29 Fra[v]← 030 fim31 Fra← Frprox; Frprox← /0

32 Barreira de sincronização

33 fim34 Dv×id ← listaCaminhoMinimo[id];

Page 92: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 90

Algoritmo 5.8: Relaxamento das Arestas

Entrada: (1) Listas Frprox e listaCaminhoMinimo e (2) vértices v e u

1 se listaDistancias[u]+ listaCaminhoMinimo[v]< listaCaminhoMinimo[u]

então2 Frprox[u]← 13 listaCaminhoMinimo[u]← listaDistancias[u]+ listaCaminhoMinimo[v]

4 fim

O Algoritmo 5.7 pecorre o grafo em fronteiras. Isso é proposto com o objetivode obter desempenho para não necessitar da estrutura da fila de prioridades encontradano algoritmo sequencial de Dijkstra (Algoritmo 4.7). Essa estrutura é intrisicamente se-quencial e, assim, dificulta uma modelagem paralela eficiente. Contudo, a necessidade deescolher o vértice com menor peso na fila de prioridade no algoritmo sequencial, é man-tida no código paralelo apresentado, mas na forma de operações atômicas. Considerandoque o grafo não possui arestas paralelas, as operações atômicas ocorrem somente entre aswarps, pois todas as threads da warp (conjunto de 32 threads) visitam uma mesma lista deadjacência. Dessa forma, a quantidade de operações atômicas em uma única instância doalgoritmo paralelo está limitada pelo número de warps em um bloco de threads, sabendoque um bloco de threads é executado somente por um SM. Por exemplo, na arquiteturaFermi da Nvidia, um bloco de threads possui no máximo 1024 threads, tendo um total de32 warps. Assim, executanto nessa arquitetura, uma instância do algoritmo paralelo emum SM terá, no pior caso, 32 operações atômicas ao mesmo tempo.

Além de pecorrer o grafo em fronteiras com objetivo de melhorar o desempenho,o Algoritmo 5.7 utiliza da melhor forma a hierarquia de memória disponibilizado pelomodelo CUDA. A matriz de distância Dv×v, utilizada por todas as instâncias do algoritmoque armazenam o menor caminho, é localizada na memória global da GPU, acessível portodos SMs. Por isso, se fez necessário uma estrutura intermediária de representação degrafos que reduzisse o impacto da matriz localizada na memória global. Pois, essa me-mória possui baixa latência (Seção 3.2) e a estrutura da própria matriz não é eficiente narepresentação de grafos quando estes são esparsos (Seção 4.1). Essa estrutura intermediá-ria é a representação compacta de grafos apresentada na Seção 4.1. Ela facilita a leituradas listas de adjacência, pois mantém todas as listas de adjacências juntas em uma únicalista. Por ser usada somente para leitura, essa estrutura é armazenada na memória cons-tante, tipo especial de memória global, quecontém otimizações para operações de leitura,descrito na Seção 3.2.

Já em relação as listas que representam as fronteiras e o caminho mínimo SSSP,elas são únicas para cada instância do algoritmo. Sendo assim, cada bloco de threads, fazvárias operações sobre os dados contidos nelas, fazendo acessos tanto de leitura quanto de

Page 93: Implementações Paralelas para os Problemas do Fecho Transitivo e

5.3 Solução Paralela baseada no Algoritmo Dijkstra para o Problema APSP 91

escrita, além de operações atômicas. Por isso, o melhor local dessas listas é na memóriacompartilhada, ainda que elas apresentam algumas limitações. Por exemplo, o tamanholimitado (na arquitetura Fermi da Nvidia, a memória chega a ter no máximo 32 KBytes) eo conflito de bancos. Porém, a latência desse memória é baixa, por isso se faz necessárioum esforço para mantê-las na memória compartilhada.

Essas características do algoritmo aliado às de grafos e às da GPU, permite fazerafirmações sobre as possíveis limitações do algoritmo. A irregularidade da estrutura dografo pode influênciar no desempenho do algoritmo, pois degradam os acessos à memóriatanto global quanto compartilhada. A memória global da GPU possui um padrão deacessos relacionado a threads das warps. Além disso, quando conflitos à bancos dememória ocorrem, os acessos são serializados. O diâmetro do grafo influência diretamenteno tempo de execução do algoritmo, pois a complexidade do Algoritmo 5.7 é dependenteda quantidade de fronteiras. Por fim, outro fator, que não pode ser desconsiderado, é o graude entrada de cada vértice. Quanto maior esse grau de cada vértice, maior a possibilidadede operações atômicas ocorrerem em cada fronteira.

Page 94: Implementações Paralelas para os Problemas do Fecho Transitivo e

CAPÍTULO 6Resultados Experimentais

6.1 Materiais e Métodos

As soluções paralelas propostas neste trabalho foram testadas em uma máquinacom 6 GB de memória RAM, sistema operacional Linux (Ubuntu 12.04 x86), processadorIntel Core i5 2300 (2.80GHz) e uma placa gráfica NV IDIA R© Tesla C2075, com 4 GB dememória e 448 núcleos de processamento oferecendo até 515 Gflops de desempenho emponto flutuante de precisão dupla.

Os algoritmos sequenciais foram implementados na linguagem C e executadosem um único núcleo de processamento da CPU. Tais algoritmos foram: Warshall (Al-goritmo 4.1), BFS (Algoritmo 4.2), Floyd-Warshall (Algoritmo 4.6) e Dijkstra (Algo-ritmo 4.7). Sendo utilizado a estrutura heap mínimo no algoritmo de Dijkstra, pois essealgoritmo com essa estrutura apresenta a complexidade de O(|E|log|V |).

Para fim de comparação, as propostas de Harish e Narayanan [16] e Katz eKider [20] foram implementadas em CUDA conforme suas descrições, tanto para oproblema do fecho transitivo quanto para o problema do caminho mínimo APSP. Naexecução do algoritmo de Katz e Kider [20], a dimensão do bloco de threads foi de32×32, onde obteve-se os melhores tempos comparados com outras dimensões. Osmelhores tempos obtidos com o algoritmo de Harish Narayanan [16] foram com adimensão 16×16. Já nas soluções propostas, a dimensão do bloco de threads que alcançoumelhores resultados foi de 32×32, obtendo 66% de ocupação da GPU.

Todos os grafos foram gerados aleatoriamente utilizando o gerador random e R-

MAT da ferramenta GTgraph [4]. Sendo este, o gerador de grafos com diâmetros menoresque 10 e, aquele, o gerador de grafos randômicos, aleatórios. Após gerados, os grafosforam gravados em arquivos com o objetivo de utilizar os mesmos grafos em todos testes.Foram realizados experimentos com grafos de diferentes números de vértices (1024,2048, 3072, 4096, 5120, 6144, 7168, 8192 e 16384), arestas (6|V |, 600|V | e 6000|V |)e diâmetros (randômicos e diâmetros menores que 10). Nos casos de 6000|V | arestas, osgrafos que possuem quantidade máxima de arestas menor do que a quantidade proposta,é usado 60% de desindade de arestas.

Page 95: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.2 Resultados dos Algoritmos de Fecho Transitivo 93

Os algoritmos de Harish e Narayanan [16] e Katz e Kider [20] não apresentaramgrandes diferenças de tempo entre os grafos de diferentes arestas ou diâmetros. Alémdeles, os algoritmos paralelos aqui propostos do Warshall e Floyd-Warshall também nãoapresentaram grandes mudanças. Assim, a média aritmética foi realizada nesses casos.Contudo, todos algoritmos foram executados 10 vezes e a média dos tempos computada.

Os resultados mostrados neste capítulo consideram somente o tempo de compu-tação paralela, ou seja, da fase paralela do código CUDA. O tempo de transferência degrandes dados e o tempo de execução do código host foram desconsiderados.

6.2 Resultados dos Algoritmos de Fecho Transitivo

6.2.1 Resultados das Implementações Paralelas do Warshall

A tabela 6.1 mostra os resultados dos algoritmos paralelos de Warshall emCUDA em segundos (s). Sendo o Algoritmo 5.1 nomeado de implementação proposta emblocos; e o Algoritmo 5.5 nomeado de implementação proposta com ocupação intensiva(Ocu. Int.).

Analisando os resultados da implementação proposta em blocos, observa-se quehouve ganhos de desempenho sobre o algoritmo de Harish e Nayaraman [16]. Emboranão faça grandes transferências de dados entre comunicações CPU e GPU, boa parte dotempo do algoritmo de Harish e Nayaraman [16] é gasto em comunicações. De acordocom Xiao e Feng [41], uma grande parte do tempo de uma aplicação CUDA é gasto nolançamento do kernel e na sincronização, quando é feito o uso intensivo de comunicaçãoentre CPU e GPU.

Contudo, o algoritmo proposto em blocos não ganha em tempo do algoritmoproposto por Katz e Kider [20], pois este utiliza de maneira eficiente o uso da hierarquiade memória com a técnica apresentada por Venkataraman et al. [39] em seu algoritmosequencial. Além disso, o algoritmo proposto em blocos não faz bom uso da memóriacompartilhada ao processar as suas iterações.

Em relação à implementação paralela proposta com ocupação intensiva, faz-semelhor uso da memória compartilhada utilizando-se a técnica apresentada por Venkata-raman et al. [39] e a utilização da otimização da ocupação intensiva em CUDA, a mesmausada na solução proposta em blocos. A combinação dessas duas técnicas no algoritmocom ocupação intensiva resultou em ganho desempenho frente às implementações deta-lhadas na tabela 6.1.

A Figura 6.1 mostra os ganhos obtidos pela implementação proposta com ocu-pação intensiva utilizando grafos com vários números de vértices. Por exemplo, para ocálculo do fecho transitivo em um grafo com 8192 vértices, o algoritmo proposto com

Page 96: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.2 Resultados dos Algoritmos de Fecho Transitivo 94

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192

Proposta em Blocos 0,040 0,370 1,388 3,543 7,422 12,148 20,994 31,774Proposta Ocu. Int. 0,021 0,181 0,681 1,585 2,575 4,440 7,587 10,760Katz e Kider 0,039 0,300 1,005 2,370 4,625 7,941 12,643 18,834Harish e Nayaraman 0,076 0,533 1,747 4,098 7,947 13,695 21,720 32,520

Tabela 6.1: Tempo computacional (s) das implementações parale-las do algoritmo do Warshall

ocupação intensiva se mostrou 3x mais rápido que o algoritmo de Harish et al. [16]. AFigura 6.2 ilustra o ganho das implementações paralelas aqui propostas do algoritmo deWarshall sobre o algoritmo sequencial (Algoritmo 4.1).

0,0

0,5

1,0

1,5

2,0

2,5

3,0

3,5

4,0

1024 2048 3072 4096 5120 6144 7168 8192

Gan

ho

co

mp

uta

cio

nal

Número de vértices

Katz e Kider

Harish e Nayaraman

Figura 6.1: Ganho computacional da implementação propostacom ocupação intensiva em relação às implementa-ções de Harish e Narayanan [16]; e Katz e Kider [20].

Os ganhos computacionais das implementações paralelas aqui propostas doalgoritmo de Warshall sobre a implementação sequencial são mostradas na Figura 6.2.A implementação paralela ocupação intensiva obteve um speedup médio de ∼= 192,1,enquanto a implementação em blocos teve o speedup médio de ∼= 80,3.

6.2.2 Resultados da Implementação Paralela do BFS

O algoritmo paralelo BFS proposto (Algoritmo 5.6) foi testado com grafosvariando-se o número de vértices, arestas e diâmetros. Isso porque o algoritmo percorre ografo em fronteiras através das listas de adjacências. Assim, quanto maior o diâmetro,

Page 97: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.2 Resultados dos Algoritmos de Fecho Transitivo 95

0

50

100

150

200

250

1024 2048 3072 4096 5120 6144 7168 8192

Gan

ho

co

mp

uta

cio

nal

Número de vértices

Proposta paralela emBlocos

Proposta paralela Ocup.Int.

Figura 6.2: Ganhos computacionais das implementações parale-las do Warshall em relação ao sequencial.

Número de VérticesImplementações 1024 2048 3072 4096Prop. Paralela BFS 0,0003 0,0008 0,0015 0,0023Sequencial BFS 0,0672 0,2638 0,5936 1,0628Speedup 192,09 313,37 395,77 457,12

Tabela 6.2: Tempo computacional (s) e ganho computacional paragrafos randômicos com 1024, 2048, 3072, 4096 vérti-ces e 6|V | arestas.

maior será o número de fronteiras. Além disso, a complexidade do algoritmo BFS,tanto o sequencial quanto o paralelo, depende da quantidade de arestas. Portanto, essasvariações das características dos grafos são realizada com o intuito de conseguir os piorese melhores casos do algoritmo paralelo proposto do BFS.

Dessa forma, as tabelas 6.2, 6.3, 6.4 e 6.5 apresentam resultados em segundos(s) da implementação paralela proposta do BFS e a implementação sequencial. Sendo osgrafos de entrada gerados aleatoriamente, ou seja, sem limitação no tamanho do diâmetro.Além do tempo computacional, o speedup é mostrado para cada instância do grafo. AFigura 6.4 ilustra em forma gráfica os resultados mostrados nessas tabelas. Observa-seque com grafos de 6|V| arestas (tabelas 6.2 e 6.3) o speedup médio obtido foi de ∼= 449.Já em relação a grafos de 600|V | e 6000|V | arestas (tabelas 6.4 e 6.5), o speedup médiochegou a ∼= 19. A Figura 6.3 mostra os speedup obtidos com grafos de 600|V | e 6000|V |arestas e sem limitação no diâmetro.

Page 98: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.2 Resultados dos Algoritmos de Fecho Transitivo 96

0

5

10

15

20

25

1024 2048 3072 4096 5120 6144 7168 8192

Gan

ho

Co

mp

uta

cio

nal

Número de vértices

600|V| arestas

6000|V| arestas

Figura 6.3: Ganho computacional da implementação paralela doBFS em relação ao algoritmo sequencial com grafosrandômicos de 600|V | e 6000|V | arestas.

Número de VérticesImplementações 5120 6144 7168 8192Prop. Paralela BFS 0,0033 0,0044 0,0057 0,0072Sequencial BFS 1,6780 2,4259 3,3378 4,4526Speedup 506,98 543,69 578,18 613,99

Tabela 6.3: Tempo computacional (s) e ganho computacional paragrafos randômicos com 5120, 6144, 7168, 8192 vérti-ces e 6|V | arestas.

Os testes com grafos de 6|V |, 600|V | e 6000|V | arestas e com diâmetros reduzi-dos (< 10) são mostrados nas tabelas 6.6, 6.7, 6.8 e 6.9. O speedup médio para grafos de6|V | com diâmetros reduzidos é de ∼= 283, uma redução em relação aos grafos aleatórios.Porém, nota-se no tempo, tanto sequencial quanto paralelo, que não houve um aumentosignificativo entre os resultados. Nos grafos de arestas 600|V | e 6000|V |, o speedup mé-dio reduziu de ∼= 19 para ∼= 17. Isso se deve ao fato da redução do tempo do algoritmosequencial em alguns tipos de grafos, como mostrado na Figura 6.5 para os grafos de6000|V | arestas.

Page 99: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.2 Resultados dos Algoritmos de Fecho Transitivo 97

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192Prop. Paralela BFS 0,07 0,31 0,73 1,32 2,09 3,02 4,15 5,47Sequencial BFS 1,36 6,27 14,96 27,59 44,18 64,46 88,77 117,49Speedup 17,99 19,79 20,40 20,86 21,11 21,27 21,35 21,44

Tabela 6.4: Tempo computacional (s) e ganho computacional comgrafos randômicos de 600|V | arestas.

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192Prop. Paralela BFS 0,142 1,20 3,63 7,57 13,07 20,17 28,85 37,47Sequencial BFS 3,11 23,15 70,75 150,2 264,4 411,8 595,9 717,3Speedup 21,88 19,29 19,48 19,84 20,23 20,41 20,65 19,14

Tabela 6.5: Tempo computacional (s) e ganho computacional comgrafos randômicos de 6000|V | arestas.

Número de VérticesImplementações 1024 2048 3072 4096Prop. Paralela BFS 0,0003 0,0008 0,0017 0,0024Sequencial BFS 0,0653 0,2461 0,3142 1,0092Speedup 183,94 282,55 180,18 417,37

Tabela 6.6: Tempo computacional (s) e ganho computacional paragrafos com 1024, 2048, 3072, 4096 vértices, 6|V | ares-tas e com diâmetro < 10.

Número de VérticesImplementações 5120 6144 7168 8192Prop. Paralela BFS 0,0037 0,0057 0,0081 0,0080Sequencial BFS 1,1423 1,2737 1,4036 4,1120Speedup 308,09 219,87 172,46 507,91

Tabela 6.7: Tempo computacional (s) e ganho computacional paragrafos com 5120, 6144, 7168, 8192 vértices, 6|V | ares-tas e com diâmetro < 10.

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8196Prop. Paralela BFS 0,06 0,33 0,32 1,68 1,31 1,62 1,63 8,13Sequencial BFS 0,90 4,54 5,79 20,97 24,89 28,48 31,84 96,30Speedup 14,99 13,71 17,79 12,48 18,91 17,56 19,43 11,83

Tabela 6.8: Tempo computacional (s) e ganho computacional comgrafos de |600V | arestas e com diâmetro < 10.

Page 100: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.2 Resultados dos Algoritmos de Fecho Transitivo 98

0

5

10

15

20

25

30

35

40

45

50

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

Co

mp

uta

cio

nal

(s)

Número de vértices

6|V| arestas

600|V| arestas

6000|V| arestas

Figura 6.4: Tempo computacional (s) da implementação paralelado BFS com grafos randômicos de 6|V |, 600|V | e6000|V | arestas.

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192Prop. Paralela BFS 0,14 0,96 0,89 5,78 4,81 5,44 5,42 33,54Sequencial BFS 2,31 14,82 16,88 86,42 95,49 103,1 109,7 468,9Speedup 15,85 15,42 18,91 14,94 19,85 18,94 20,20 14,41

Tabela 6.9: Tempo computacional (s) e ganho computacional comgrafos de |6000V | arestas e com diâmetro < 10.

6.2.3 Análise Comparativa dos Resultados das Implementações Pa-ralelas Propostas

Os gráficos das figuras 6.6 e 6.7 mostram os tempos apresentados pelos algo-ritmos paralelos do Warshall e do BFS aqui propostos com grafos randômicos recebidoscomo entrada.

O algoritmo paralelo do BFS apresenta os melhores tempos em relação aosparalelos do Warshall com grafos esparsos, ou seja, com a quantidade de arestas entre6|V | e 600|V |, onde V é a conjunto de vértices. Porém, grafos com a quantidade dearestas maior ou igual a 6000|V | fazem o algoritmo BFS ter desempenho inferior aosdos algoritmos paralelos do Warshall, como pode ser visto nos gráficos 6.6 e 6.7. Issoacontece porque o algoritmo paralelo BFS é influenciado principalmente pela quantidadede arestas e pela estrutura do grafo. Por outro lado, a implementação paralela do Warshallapresenta somente influência sobre a quantidade de vértices.

Page 101: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.2 Resultados dos Algoritmos de Fecho Transitivo 99

0

100

200

300

400

500

600

700

800

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

Grafos aleatórios (Random) Grafos com diâmetros < 10 (R-MAT)

Figura 6.5: Tempo computacional (s) do algoritmo sequencial doBFS com grafos randômicos de 6000|V | arestas.

Resultados com entrada de grafos de diâmetros ≤ 10 nos algoritmos paralelospropostos do Warshall e BFS podem ser visualizados nos gráficos 6.8 e 6.9. Observa-seque o algoritmo paralelo do BFS mantém os melhores tempos em relação aos paralelosdo Warshall com grafos esparsos. Contudo, o algoritmo paralelo do BFS possui desempe-nho inferior em alguns casos em relação aos paralelos do Warshall com grafos de 6000V

arestas. Em relação ao algoritmo paralelo do Warshall em Blocos, o algoritmo paralelodo BFS obtém melhor tempo em todos os casos exceto nos grafos com 4096 vértices. Jácomparado com o algoritmo paralelo do Warshall com Ocupação Intensiva, o algoritmoparalelo do BFS apresenta perdas em todos os grafos exceto com grafos de 7168 vértices.Mesmo com desempenho inferior, os tempos do BFS em paralelo se aproximam mais doque nos casos com grafos randômicos, com exceção dos grafos de 8192 vértices. Issoacontece devido à grande quantidade de vértices, pois o armazenamento das estruturas dedados é realizado na memória global da GPU devido ao fato do tamanho da memória com-partilhada ser limitada. Em consequência, aumenta-se a latência dos acessos a memóriae o número de acessos não-aglutinados, prejudicando diretamente no tempo de execuçãodo algoritmo.

Page 102: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 100

0

10

20

30

40

50

60

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

Warshall Paralelo EmBlocos

BFS Paralelo - grafos de6|V| arestas

BFS Paralelo - grafos de600|V| arestas

BFS Paralelo - grafos de6000|V| arestas

Figura 6.6: Comparação de tempo entre os algoritmos paralelospropostos do Warshall em Blocos e do BFS com grafosrandômicos (gerador Random).

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192

Proposta em Blocos 0,072 0,530 1,431 3,672 7,922 13,548 22,744 35,784Proposta Ocu. Int. 0,026 0,193 0,6373 1,509 2,920 5,025 7,941 11,914Katz e Kider 0,044 0,330 1,097 2,599 5,051 8,715 13,816 20,702Harish e Nayaraman 0,110 0,770 2,485 5,832 11,355 19,618 31,295 47,885

Tabela 6.10: Tempo computacional (s) das implementações para-lelas do algoritmo do Floyd-Warshall

6.3 Resultados dos Algoritmos do Caminho MínimoAPSP

6.3.1 Resultados das Implementações Paralelas do Floyd-Warshall

A tabela 6.10 apresenta os resultados em segundos (s) dos algoritmos paralelosde Floyd-Warshall em CUDA. Seguindo a mesma nomeclatura na seção 6.2.1, o Algo-ritmo 5.1 é nomeado de implementação proposta em blocos; e o Algoritmo 5.5 é nomeadode implementação proposta com ocupação intensiva (Ocu. Int.).

Analisando a tabela 6.10 e os gráficos nas figuras 6.10 e 6.11, observa-se quehouve, de fato, ganhos de eficiência computacional utilizando o algoritmo proposto comocupação intensiva. O ganho obtido foi por meio da estratégia aqui utilizada, que diminuia comunicação entre a CPU e a GPU, fazendo com que as threads permaneçam ativas

Page 103: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 101

0

10

20

30

40

50

60

70

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

BFS Paralelo - grafos de6000|V| arestas

Warshall Paralelo Ocup.Int.

BFS Paralelo - grafos de600|V| arestas

BFS Paralelo - grafos de6|V| arestas

Figura 6.7: Comparação de tempo entre os algoritmos paralelospropostos do Warshall com Ocupação Intensiva e doBFS com grafos randômicos (gerador Random).

por um tempo maior nos núcleos de processamento. Vale ressaltar que a utilização dessaestratégia altera somente constantes do tempo do algoritmo e não sua complexidade.

Os algoritmos apresentados por Harish et al. [16] e Katz e Kider [20] apresenta-ram um tempo maior de execução para grafos com um número maior que 2048 vértices,devido ao uso intensivo de comunicação entre os dispositivos (CPU e GPU). As Figu-ras 6.12 e 6.13 mostram os ganhos obtidos utilizando grafos com vários números de vér-tices. Por exemplo, para o cálculo do caminho mínimo em um grafo com 16384 vértices,o algoritmo proposto se mostrou 4x mais rápido que o algoritmo de Harish et al. [16] eaté 195x mais rápido que a implementação sequencial do algoritmo Floyd-Warshall (Al-goritmo 5.1).

6.3.2 Resultados das Implementações Paralelas de Dijkstra

O algoritmo paralelo proposto de Dijkstra (Algoritmo 5.7) e o sequencial deDijkstra (Algoritmo 4.7) foram testados com grafos variando entre vértices, arestas ediâmetro, semelhante aos testes verificados no algoritmo paralelo BFS. É realizado dessaforma por causa que o algoritmo paralelo proposto de Dijkstra caminha sobre o grafo emfronteiras através das listas de adjacências. Dessa forma, a variação do diâmetro e arestasinfluenciam diretamente no tempo de execução do algoritmo paralelo. Essas variaçõesde classes de grafos é realizada com o intuito de obter os melhores e piores casos doalgoritmo paralelo proposto de Dijkstra.

Page 104: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 102

0

5

10

15

20

25

30

35

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

BFS Paralelo - grafos de6|V| arestas

BFS Paralelo - grafos de600|V| arestas

BFS Paralelo - grafos de6000|V| arestas

Warshall Paralelo EmBlocos

Figura 6.8: Comparação de tempo entre os algoritmos paralelospropostos do Warshall em Blocos e do BFS com grafosde diâmetro ≤ 10 (R-MAT).

O algoritmo sequencial de Dijkstra (Algoritmo 4.7) não mostrou variações notempo de execução de acordo com tamanho do diâmetro do grafo. O gráfico da Figura 6.14apresenta o tempo computacional do algoritmo sequencial de Dijkstra com grafos deentrada de 6000|V | para grafos randômicos (gerador Random) e diâmetro limitado ≤ 10(R-MAT).

As tabelas 6.11, 6.12, 6.13 e 6.14 apresentam resultados em segundos (s) daexecução da implementação paralela proposta de Dijkstra e da implementação sequencial.Sendo os grafos de entrada gerados aleatoriamente, ou seja, sem limite no tamanho dodiâmetro do grafo. Além do tempo computacional, o speedup é mostrado para cadainstância de grafos. O Algoritmo 5.7 obteve o tempo de 0,25 segundos para grafos de1024 vértices; e 96,73 segundos para grafos de 8192 vértices. Ambos com grafos de6000|V | arestas.

O gráfico da Figura 6.16 ilustra em forma gráfica os resultados mostrados nessastabelas. Observa-se o speedup médio alcançado com grafos de 6|V| arestas (tabelas 6.11e 6.12) é ∼= 3,58. A justificativa para tal speedup está nas operações atômicas que sãoexecutadas, fazendo as operações serem serializadas. Contudo, o algoritmo obtém altosspeedups médios com grafos de 600|V | e 6000|V | arestas (tabelas 6.13 e 6.14), onde ospeedup médio para 600 arestas é ∼= 23,63 e para 6000 arestas foi ∼= 24,35. Os speedups

podem ser vistos no gráfico da Figura 6.15, onde mostra os speedup obtidos com grafosde 6|V |, 600|V | e 6000|V | arestas sem limite no tamanho do diâmetro.

Page 105: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 103

Número de VérticesImplementações 1024 2048 3072 4096Prop. Paralela Dijkstra 0,0223 0,0898 0,2068 0,4446Sequencial Dijkstra 0,0892 0,3597 0,8227 1,4586Speedup 4,00 4,00 3,97 3,28

Tabela 6.11: Tempo computacional (s) e ganho computacionalpara grafos randômicos com 1024, 2048, 3072, 4096vértices e 6|V | arestas.

Número de VérticesImplementações 5120 6144 7168 8192Prop. Paralela Dijkstra 0,5683 1,0916 1,1337 2,568Sequencial Dijkstra 2,2891 3,3246 4,5321 5,9306Speedup 4,02 3,04 3,99 2,31

Tabela 6.12: Tempo computacional (s) e ganho computacional doalgoritmo paralelo de Dijkstra para grafos randômi-cos com 5120, 6144, 7168, 8192 vértices e 6|V | ares-tas.

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192Prop. Paralela Dijkstra 0,12 0,56 1,26 2,50 4,46 5,67 8,51 14,92Sequencial Dijkstra 2,94 14,63 32,71 59,70 103,7 152,3 187,2 248,6Speedup 24,55 26,09 25,88 23,83 23,20 26,82 21,99 16,66

Tabela 6.13: Tempo computacional (s) e ganho computacional doalgoritmo paralelo de Dijkstra com grafos randômi-cos de 600|V | arestas.

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192Prop. Paralela Dijkstra 0,25 1,85 5,55 11,08 23,23 34,09 56,98 96,73Sequencial Dijkstra 6,48 48,14 144,7 308,37 546,1 861,9 1270 1748Speedup 25,75 25,98 26,07 27,83 23,51 25,27 22,29 18,07

Tabela 6.14: Tempo computacional (s) e ganho computacional doalgoritmo paralelo de Dijkstra com grafos randômi-cos de 6000|V | arestas.

Page 106: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 104

0

5

10

15

20

25

30

35

1024 2048 3072 4096 5120 6144 7168 8192

Te

mp

o c

om

pu

taci

on

al (s

)

Número de vértices

BFS Paralelo - grafos de6|V| arestas

BFS Paralelo - grafos de600|V| arestas

BFS Paralelo - grafos de6000|V| arestas

Warshall Paralelo Ocup.Int.

Figura 6.9: Comparação de tempo entre os algoritmos paralelospropostos do Warshall com Ocupação Intensiva e doBFS com grafos de diâmetro ≤ 10 (R-MAT).

Os testes com grafos de 6|V |, 600|V | e 6000|V | arestas e com diâmetros redu-zidos (< 10) são apresentados nas tabelas 6.15, 6.16, 6.17 e 6.18 em segundos. O Algo-ritmo 5.7 apresentou 0,648 segundos para grafos de 1024 vértices e 6000|V | arestas; e39,02 segundo para grafos de 8192 vértices e 6000|V | arestas.

O speedup médio obtido com grafos de 6|V | arestas foi ∼= 7,19. Com grafosde 600|V | arestas chegou a ser ∼= 82,38; e para grafos de 6000|V | foi obtido ∼= 70,56de speedup. Com isso mostra que houve aumento nos speedup médios do algoritmo emrelação ao de entrada de grafos de randômicos. Por exemplo, para grafos de 6000|V |houve um aumento de ∼= 46,26.

Analisando as tabelas e os gráficos em geral, observa-se que houve, de fato,ganhos de eficiência computacional utilizando o algoritmo proposto. A estratégia aquiutilizada em fronteiras para implementação do algoritmo de Dijkstra mostra que a classede grafos que melhor obtém desempenho são grafos com diâmetro de tamanho limitado,ou seja, diâmetro pequeno (∼= 10).

6.3.3 Análise Comparativa dos Resultados das Implementações Pa-ralelas Propostas

Dois tipos de algoritmos que resolvem diferentemente o problema do caminhomínimo APSP foram implementados em paralelo na arquitetura CUDA: Floyd-Warshall e

Page 107: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 105

Número de VérticesImplementações 1024 2048 3072 4096Prop. Paralela Dijkstra 0,0203 0,0810 0,1063 0,3365Sequencial Dijkstra 0,0920 0,3576 0,7948 1,4471Speedup 4,53 4,41 7,47 4,30

Tabela 6.15: Tempo computacional (s) e ganho computacional doalgoritmo paralelo de Dijkstra para grafos com 1024,2048, 3072, 4096 vértices, 6|V | arestas e com diâme-tro < 10.

Número de VérticesImplementações 5120 6144 7168 8192Prop. Paralela Dijkstra 0,3910 0,3743 0,3737 0,5628Sequencial Dijkstra 2,3001 3,2449 4,4205 5,8875Speedup 5,88 8,66 11,82 10,46

Tabela 6.16: Tempo computacional (s) e ganho computacional doalgoritmo paralelo de Dijkstra para grafos com 5120,6144, 7168, 8192 vértices, 6|V | arestas e com diâme-tro < 10.

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8196Prop. Paralela Dijkstra 0,107 0,521 0,532 2,917 2,451 2,626 2,793 29,28Sequencial Dijkstra 4,31 28,17 72,81 177,2 300,2 451,8 117,4 899,7Speedup 40,1 54,0 136,8 60,7 122,4 172,0 42,0 30,7

Tabela 6.17: Tempo computacional (s) e ganho computacional doalgoritmo paralelo de Dijkstra para grafos de |600V |arestas e com diâmetro < 10.

Número de VérticesImplementações 1024 2048 3072 4096 5120 6144 7168 8192Prop. Paralela Dijkstra 0,648 1,305 1,403 7,431 7,346 7,699 8,686 39,02Sequencial Dijkstra 6,512 48,42 145,3 309,1 546,7 862,8 1.223 1.749Speedup 10,03 37,10 103,5 41,60 74,42 112,0 140,8 44,83

Tabela 6.18: Tempo computacional (s) e ganho computacional doalgoritmo paralelo de Dijkstra com grafos de |6000V |arestas e com diâmetro < 10.

Page 108: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 106

0

2

4

6

8

10

12

1024 2048 3072 4096 5120

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

Katz e Kider

Harish

Implementação proposta

Figura 6.10: Tempo de execução dos algoritmos para grafos com1024, 2048, 3072, 4096 e 5120 vértices.

0

50

100

150

200

250

300

350

400

450

6144 7168 8192 16384

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

Katz e Kider

Harish

Implementação proposta

Figura 6.11: Tempo de execução dos algoritmos para grafos com6144, 7168, 8192 e 16384 vértices.

Dijkstra. Sendo duas soluções paralelas para o algoritmo de Floyd-Warshall e uma para oalgoritmo de Dijkstra. Junto a essas implementações, duas classes de grafos foram geradasem relação ao tamanho do diâmetro a fim de obter os melhores e piores casos de cadaalgoritmo.

Dessa forma, os gráficos das figuras 6.17 e 6.18 apresentam os resultadosdessas implementações com grafos gerados randomicamente e limitados pelo tamanhodo diâmetro, respectivamente. O algoritmo paralelo de Floyd-Warshall com OcupaçãoIntensiva apresentou o melhor desempenho frente à implementação em Blocos nos doiscasos apresentados.

Analisando o gráfico com grafos randômicos (Figura 6.17), nota-se que o al-goritmo de Dijkstra obteve o melhor desempenho frente a todas as outras com grafos

Page 109: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 107

00,5

11,5

22,5

33,5

44,5

10

24

20

48

30

72

40

96

51

20

61

44

71

68

81

92

16

38

4

Gan

ho

co

mp

uta

cio

nal

Número de vértices

Katz e Kider

Harish

Figura 6.12: Ganhos de speedup em relação às implementaçõesde Harish et al. [16] e Katz e Kider [20].

0

50

100

150

200

250

10

24

20

48

30

72

40

96

51

20

61

44

71

68

81

92

16

38

4

Gan

ho

co

mp

uta

cio

nal

Número de vértices

Sequencial

Figura 6.13: Ganhos de speedup em relação à implementaçãosequencial do algoritmo Floyd-Warshall.

com 6|V | arestas. Em seguida, houve aproximação entre o Floyd-Warshall paralelo comOcupação Intensiva e o algoritmo paralelo de Dijkstra com entrada de grafos de 600|V |arestas. Por último, com o pior desempenho apresenta o algoritmo de Dijkstra paralelocom grafos de 6000|V |, tendo um tempo de execução superior ao Floyd-Warshall paraleloem Blocos. Com isso, conclui-se que o algoritmo paralelo de Dijkstra apresenta a me-lhor solução para problema do caminho mínimo APSP sobre grafos esparsos (entre 6|V |a 600|V | arestas). Logo, com grafos densos (≤ 6000|V | arestas) ou em geral, o algoritmode Floyd-Warshall com Ocupação Intensiva mostrou melhor para solução do problema docaminho mínimo APSP.

No cenário com grafos de tamanho do diâmetro limitado (Figura 6.18), oalgoritmo de Dijkstra paralelo com grafos de 6|V | arestas obteve novamente o melhordesempenho. Com grafos de 600|V | arestas, o Dijkstra paralelo obteve desempenhos

Page 110: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 108

0

200

400

600

800

1000

1200

1400

1600

1800

2000

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

Grafos aleatórios (Random) Grafos com diâmetros < 10 (R-MAT)

Figura 6.14: Tempo computacional (s) do algoritmo sequencial doDijkstra com grafos de 6000|V | arestas.

superiores em todas as instâncias em relação ao Floyd-Warshall paralelo com OcupaçãoIntensiva, exceto com grafos de 5120 e 8192 vértices. O mesmo ocorre com grafos de6000|V | arestas, em algumas instâncias obteve melhores desempenhos em relação aoFloyd-Warshall paralelo. Vários motivos podem ser a causa dessas diferenças de temposentre as instâncias de grafos no algoritmo de Dijkstra paralelo. Podem ser a quantidadede operações atômicas em cada execução do algoritmo, a irregularidade em cada fronteirado grafo, geração de acessos não-aglutinados, conflito de banco e o tamanho da memóriacompartilhada, e entre outros. Neste cenário apresentado pela Figura 6.18, o pior casopara a solução do problema do caminho mínimo APSP ficou para o algoritmo de Floyd-Warshall em Blocos. Pode-se concluir que o melhor algoritmo para resolver o caminhomínimo APSP com grafos esparsos de diâmetro de tamanho limitado é o algoritmo deDijkstra paralelo.

Page 111: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 109

0

5

10

15

20

25

30

1024 2048 3072 4096 5120 6144 7168 8192

Gan

ho

co

mp

uta

cio

nal

Número de vértices

6|V| arestas

600|V| arestas

6000|V| arestas

Figura 6.15: Ganho computacional da implementação paralela doDijkstra com grafos randômicos de 6|V |, 600|V | e6000|V | arestas.

0

20

40

60

80

100

120

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

Co

mp

uta

cio

nal

(s)

Número de vértices

6|V| arestas

600|V| arestas

6000|V| arestas

Figura 6.16: Tempo computacional (s) da implementação paralelado Dijkstra com grafos randômicos de 6|V |, 600|V | e6000|V | arestas.

Page 112: Implementações Paralelas para os Problemas do Fecho Transitivo e

6.3 Resultados dos Algoritmos do Caminho Mínimo APSP 110

0

20

40

60

80

100

120

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

Dijkstra Paralelo - 6|V|arestas

Dijkstra Paralelo - 600|V|arestas

Dijkstra Paralelo - 6000|V|arestas

Floyd-Warshall Ocup. Int.

Floyd-Warshall Paralelo EmBlocos

Figura 6.17: Comparação de tempo entre os algoritmos paralelospropostos do Floyd-Warshall com Dijkstra paralelocom grafos randômicos (Random).

0

5

10

15

20

25

30

35

40

45

1024 2048 3072 4096 5120 6144 7168 8192

Tem

po

co

mp

uta

cio

nal

(s)

Número de vértices

Floyd-Warshall Paralelo EmBlocos

Floyd-Warshall Ocup. Int.

Dijkstra Paralelo - 6|V|arestas

Dijkstra Paralelo - 600|V|arestas

Dijkstra Paralelo - 6000|V|arestas

Figura 6.18: Comparação de tempo entre os algoritmos paralelospropostos do Floyd-Warshall com Dijkstra paralelocom grafos de diâmetro ≤ 10 (R-MAT).

Page 113: Implementações Paralelas para os Problemas do Fecho Transitivo e

CAPÍTULO 7Conclusão

Este trabalho teve o objetivo de investigar o uso de paralelismo em arquiteturasda GPU para solução de problemas envolvendo grafos, cuja paralelização eficiente édifícil de alcançar. Isso foi realizado através de novas soluções paralelas desenvolvidas eanalisadas no modelo de programação disponibilizado pela arquitetura da GPU. Soluçõesbaseadas em algoritmos sequenciais que têm a finalidade reduzir o tempo de resolução dosproblemas do fecho transitivo e do caminho mínimo entre todos os pares de vértices. Paraisso, essas novas abordagens precisam fazer o uso de propriedades intrínsecas da naturezaparalela da arquitetura para obter um desempenho escalável em relação ao tamanho doproblema solucionado e independência do modelo do hardware da GPU. Essas soluçõessão codificadas na linguagem CUDA, e um ambiente de experimentação com diferentesclasses de grafos gerados é criado para obtenção de resultados comparativos com osmelhores resultados atualmente disponíveis.

Apresentou-se neste trabalho implementações paralelas escaláveis para os pro-blemas do fecho transitivo e do caminho mínimo entre todos os pares de vértices. Osalgoritmos aqui apresentados mostraram ganhos de eficiência computacional (speedup)em relação ao algoritmo sequencial. Além disso, as análises comparativas mostraram queas soluções propostas rivalizam-se e, em certos cenários, sobrepõe-se aos melhores re-sultados atualmente disponíveis na literatura dedicada ao tema tanto para o problema dofecho transitivo quanto do caminho mínimo entre todos os pares de vértices. Por exem-plo, quando comparado com as implementações de Harish e Nayaraman [16] e Katz eKider [20], a implementação proposta de Floyd-Warshall com ocupação intensiva teve amédia de speedup de ≈ 4x e ≈ 1,8x, respectivamente.

As análises comparativas também mostraram que as implementações propostasdo BFS e Dijkstra são sensíveis à estrutura dos grafos em relação ao número de arestas eao tamanho do diâmetro. O melhor caso apresentado dos dois algoritmos propostos estána entrada de grafos esparsos, onde a quantidade de arestas varia entre 6|V | a 600|V |. Opior caso apresentado por eles encontra-se em grafos densos, cujo a quantidade de arestasestá acima de 6000|V |. Variações no ganho computacional não foram apresentadas nassoluções propostas baseadas nos algoritmos de Warshall e Floyd-Warshall. Contudo, nos

Page 114: Implementações Paralelas para os Problemas do Fecho Transitivo e

112

dois problemas abordados, os melhores casos dos algoritmos propostos BFS e Dijkstraapresentaram desempenhos superiores aos Warshall e Floyd-Warshall, respectivamente.

Foi possível concluir que este speedup pôde ser obtido por meio da utilizaçãode todos os recursos da tecnologia CUDA, aliado com melhores práticas arquiteturaisdurante o desenvolvimento. Além disso, concluiu-se que CUDA colabora, de maneirasignificativa, para a obtenção de versões paralelas eficientes por meio da exploraçãode características paralelizáveis intrínsecas ao problema e adaptáveis às especificaçõese modo de funcionamento desta arquitetura.

Trabalhos seguintes nessa linha de pesquisa poderão solucionar os problemasabordados em grafos com uma maior quantidade de vértices e arestas; e em ambientescom várias GPUs.

Algumas possibilidades de melhoria nas soluções paralelas propostas dos algorit-mos do BFS e de Dijkstra podem ser realizadas, através de uma cooperação mais eficienteentre as threads de uma mesma warp. Isso em versões mais atuais da arquitetura CUDA.

Além disso, outras técnicas podem ser aplicadas para as propostas paralelas dosalgoritmos de Warshall e Floyd-Warshall de maneira que reduz o número de chamadas akernels CUDA, mantendo a utilização eficiente da hierarquia de memória da GPU.

Outros trabalhos também poderão elaborar estruturas adequadas, que mantêmuma matriz triangular na GPU de maneira eficiente, para uma versão do algoritmo deCáceres et al [7] em CUDA. Desse forma, poder-se-á obter bons desempenhos na GPUfrente aos melhores resultados atualmente do problema do Fecho Transitivo.

Adicionalmente, pretende-se utilizar as estratégias descritas nas soluções parale-las do Floyd-Warshall e de Dijkstra no software PET-GYN [18].

Page 115: Implementações Paralelas para os Problemas do Fecho Transitivo e

Referências Bibliográficas

[1] AINI, A.; SALEHIPOUR, A. Speeding up the floyd-warshall algorithm for the

cycled shortest path problem. Applied Mathematics Letters, 25(1):1–5, 2012.

[2] AMDAHL, G. M. Validity of the single processor approach to achieving large

scale computing capabilities. In: Proceedings of the April 18-20, 1967, Spring

Joint Computer Conference, p. 483–485. ACM, 1967.

[3] ATALLAH, M. J. Algorithms and Theory of Computation Handbook, volume 2.

CRC press, 2010.

[4] BADER, D. A.; MADDURI, K. Gtgraph: A synthetic graph generator suite. Atlanta,

GA, February, 2006.

[5] BAR-NOY, A.; KIPNIS, S. Designing broadcasting algorithms in the postal model

for message-passing systems. Mathematical Systems Theory, 27(5):431–452,

1994.

[6] BORGWARDT, K. M.; KRIEGEL, H.-P. Shortest-path kernels on graphs. In: Data

Mining, Fifth IEEE International Conference on, p. 8–pp. IEEE, 2005.

[7] CÁCERES, E. N.; SONG, S. W.; SZWARCFITER, J. L. A parallel algorithm for

transitive closure. In: IASTED PDCS, p. 114–116. Citeseer, 2002.

[8] COOK, S. CUDA Programming: A Developer’s Guide to Parallel Computing with

GPUs. Morgan Kaufmann, 2012.

[9] CUDA, C. Programming guide. NVIDIA Corporation, July, 2012.

[10] DEHNE, F.; FABRI, A.; RAU-CHAPLIN, A. Scalable parallel geometric algorithms

for coarse grained multicomputers. In: Proceedings of the Ninth Annual Sympo-

sium on Computational Geometry, p. 298–307. ACM, 1993.

[11] DEHNE, F.; YOGARATNAM, K. Exploring the limits of gpus with parallel graph

algorithms. CoRR, abs/1002.4482, 2010.

[12] DIJKSTRA, E. Dijkstra’s algorithm. 1959.

Page 116: Implementações Paralelas para os Problemas do Fecho Transitivo e

Referências Bibliográficas 114

[13] GERSTING, J. L. Fundamentos Matemáticos para a Ciência da Computação: um

Tratamento Moderno de Matemática Discreta. Livros Técnicos e Científicos, 2004.

[14] GOODMAN, J. E.; O’ROURKE, J. Handbook of Discrete and Computational

Geometry. Chapman and Hall/CRC, 2010.

[15] GUSTAFSON, J. L. Reevaluating amdahl’s law. Communications of the ACM,

31(5):532–533, 1988.

[16] HARISH, P.; NARAYANAN, P. J. Accelerating large graph algorithms on the gpu

using cuda. High Performance Computing–HiPC, p. 197–208, 2007.

[17] HONG, S.; KIM, S. K.; OGUNTEBI, T.; OLUKOTUN, K. Accelerating cuda graph

algorithms at maximum warp. In: Proceedings of the 16th ACM symposium on

Principles and Practice of Parallel Programming, p. 267–276. ACM, 2011.

[18] JRADI, W.; DO NASCIMENTO, H.; LONGO, H.; HALL, B. Simulation and analysis

of urban traffic the architecture of a web-based interactive decision support

system. In: Intelligent Transportation Systems, 2009. ITSC ’09. 12th International

IEEE Conference on, p. 1–6, 2009.

[19] KARP, R. M.; RAMACHANDRAN, V. Parallel algorithms for shared-memory machi-

nes, handbook of theoretical computer science (vol. a): Algorithms and com-

plexity, 1991.

[20] KATZ, G. J.; KIDER, J. T. All-pairs shortest-paths for large graphs on the

gpu. Proceedings of the 23rd ACM SIGGRAPHEUROGRAPHICS Symposium on

Graphics Hardware, p. 47–55, 2008.

[21] KIRK, D. B.; WEN-MEI, W. H. Programming Massively Parallel Processors: a

Hands-on Approach. Morgan Kaufmann, 2013.

[22] LEISERSON, C. E.; RIVEST, R. L.; STEIN, C.; CORMEN, T. H. Introduction to

algorithms, volume 3. The MIT press, 2009.

[23] LIU, P.; AIELLO, W.; BHATT, S. An atomic model for message-passing. In: Proce-

edings of the Fifth Annual ACM Symposium on Parallel Algorithms and Architectures,

p. 154–163. ACM, 1993.

[24] MA, J.; LI, K.-P.; ZHANG, L.-Y. A parallel floyd-warshall algorithm based on

tbb. In: Information Management and Engineering (ICIME), 2010 The 2nd IEEE

International Conference on, p. 429–433. IEEE, 2010.

Page 117: Implementações Paralelas para os Problemas do Fecho Transitivo e

Referências Bibliográficas 115

[25] MAGGS, B. M.; MATHESON, L. R.; TARJAN, R. E. Models of parallel computation:

A survey and synthesis. In: System Sciences, 1995. Vol. II. Proceedings of the

Twenty-Eighth Hawaii International Conference on, volume 2, p. 61–70. IEEE, 1995.

[26] MATTSON, T. G.; SANDERS, B. A.; MASSINGILL, B. L. A pattern language for

parallel programming, 2004.

[27] NVIDIA, C. C best practices guide. NVIDIA, Santa Clara, CA, 2012.

[28] ORTEGA-ARRANZ, H.; TORRES, Y.; LLANOS, D. R.; GONZALEZ-ESCRIBANO, A.

The all-pair shortest-path problem in shared-memory heterogeneous systems.

2013.

[29] ORTEGA-ARRANZ, H.; TORRES, Y.; LLANOS, D. R.; GONZALEZ-ESCRIBANO, A. A

tuned, concurrent-kernel approach to speed up the apsp problem. 2013.

[30] PACHECO, P. S. Parallel Programming with MPI. Morgan Kaufmann Pub, 1997.

[31] PARHAMI, B. Introduction to Parallel Processing: Algorithms and Architectures,

volume 1. Springer, 1999.

[32] RIDI, L.; TORRINI, J.; VICARIO, E. Developing a scheduler with difference-bound

matrices and the floyd-warshall algorithm. Software, IEEE, 29(1):76–83, 2012.

[33] SKIENA, S. The Algorithm Design Manual: Text, volume 2. Springer, 2008.

[34] SKILLICORN, D. B. Foundations of Parallel Programming. Número 6. Cambridge

University Press, 2005.

[35] SKILLICORN, D. B.; TALIA, D. Models and languages for parallel computation.

ACM Computing Surveys (CSUR), 30(2):123–169, 1998.

[36] STALLINGS, W.; DE FIGUEIREDO, C. C.; DE FIGUEIREDO, L. C. Arquitetura e

Arganização de Computadores: Projeto para o Desempenho. Prentice-Hall,

2009.

[37] TSUCHIYAMA, R.; NAKAMURA, T.; IIZUKA, T.; ASAHARA, A.; SON, J.; MIKI, S. The

OpenCL Programming Book. Fixstars, 2010.

[38] UPFAL, E. Efficient schemes for parallel communication. Journal of the ACM

(JACM), 31(3):507–517, 1984.

[39] VENKATARAMAN, G.; SAHNI, S.; MUKHOPADHYAYA, S. A blocked all-pairs shortest-

paths algorithm. Journal of Experimental Algorithmics (JEA), 8, 2003.

Page 118: Implementações Paralelas para os Problemas do Fecho Transitivo e

Referências Bibliográficas 116

[40] WILT, N. The CUDA Handbook: A Comprehensive Guide to GPU Programming.

Addison-Wesley Professional, 2013.

[41] XIAO, S.; FENG, W.-C. Inter-block gpu communication via fast barrier synch-

ronization. In: Parallel & Distributed Processing (IPDPS), 2010 IEEE International

Symposium on, p. 1–12. IEEE, 2010.