Anda di halaman 1dari 74

Universidade Federal do Rio Grande do Sul

Escola de Engenharia
Programa de Pós-Graduação em Engenharia Civil

ESTUDO DO USO DA LINGUAGEM CUDA EM


SIMULAÇÕES COMPUTACIONAIS DE PROBLEMAS DA
ENGENHARIA DO VENTO

Guilherme Wienandts Alminhana

Porto Alegre
2018
SUMÁRIO

1 INTRODUÇÃO ....................................................................................... 11

2. CUDA ...................................................................................................... 17

2.1 INTRODUÇÃO ...................................................................................... 17

2.2 PRINCIPAIS ASPECTOS ..................................................................... 19

2.2.1 Kernels ................................................................................................. 20


2.2.2 Precisão simples x precisão dupla ....................................................... 25
2.2.3 Transferência de memória entre host e device .................................... 26
2.2.4 Tipos de memória no device ................................................................ 29

2.2.5 Tópicos sobre paralelismo ................................................................... 34


2.2.6 Uso de múltiplas GPUs ....................................................................... 35
2.2.7 Funções atômicas ................................................................................ 37

2.3 APLICAÇÕES NA LITERATURA ...................................................... 37


2.3.1 Equações de Navier-Stokes para fluidos incompressíveis .................. 38
2.3.2 Simulação de problemas da Dinâmica dos Fluidos ............................. 41
2.3.2.1 Escoamento em cavidade ..................................................................................... 41
2.3.2.2 Escoamento em torno de cilindro ......................................................................... 43
2.3.3 Simulação de escoamentos compressíveis .......................................... 44
2.3.4 Simulação de escoamentos em áreas urbanas ..................................... 47
2.3.5 Direct Numerical Simulation ............................................................... 48
2.3.6 Solução das equações de N-S através de um algoritmo heterogêneo .. 50

3. ESTUDO DE APLICAÇÃO .................................................................. 52

3.1 ENSAIO DE MODELO REDUZIDO EM TÚNEL DE VENTO ......... 52


3.1.1 Parâmetros de comparação .................................................................. 52
3.1.2 Resultados da comparação .................................................................. 59
3.2 SIMULAÇÃO DE ESCOAMENTO DE FLUIDOS VISCOSOS ......... 61
3.2.1 Cavidade bidimensional ...................................................................... 61
3.2.2 Degrau ................................................................................................. 65

69
4. CONCLUSÕES ......................................................................................

REFERÊNCIAS ......................................................................................... 72

ANEXOS ..................................................................................................... 74
LISTA DE FIGURAS

Figura 1.1 – Características de CPUs ao longo dos últimos 42 anos .............................. 13

Figura 1.2 – Resultados de Lee et al. (2010) entre o desempenho de CPU x GPU para 14
rotinas usuais em códigos numéricos .........................................................................

Figura 1.3 – Evolução de CPUs e GPUs em termos de processamento de operações de 15


ponto flutuante – FLOPS ............................................................................................

Figura 2.1 – Unidades componentes do device ............................................................... 20

Figura 2.2 – Chamada de kernel construído em um sub-rotina ...................................... 20

Figura 2.3 – Criação de kernels com mais de uma dimensão ......................................... 21

Figura 2.4 – Estrutura de um kernel via sub-rotina ......................................................... 21

Figura 2.5 – Organização local e global dos threads em função do número de threads 22
blocks ..........................................................................................................................

Figura 2.6 – Instruções para um kernel loop directive .................................................... 23

Figura 2.7 – Atributos de variáveis a serem empregadas no device ............................... 24

Figura 2.8 – Especificações de dispositivos CUDA em diferentes capacidades 26


computacionais ...........................................................................................................

Figura 2.9 – Tempos de execução de parte do código de Nogueira et al. (2015) ........... 27

Figura 2.10 – Transmissão de informações por pageable memory e pela pinned 28


memory .......................................................................................................................

Figura 2.11 – Performance entre pageable memory e pinned memory em uma Tesla 28
K20 .............................................................................................................................

Figura 2.12 – Performance na transmissão de dados por pageable memory e por 29


pinned memory em um GTX 750Ti ...........................................................................

Figura 2.13 – Trânsito de dados entre os diferentes tipos de memórias do device ......... 32

Figura 2.14 – Impacto no bandwidth em função do deslocamento de acesso à 33


memória global do device, para precisão simples ......................................................

Figura 2.15 – Impacto no bandwidth em função do deslocamento de acesso à 33


memória global do device, para precisão dupla .........................................................

Figura 2.16 – Resultados para occupancy e bandwitdth em função do tamanho do 35


thread block ................................................................................................................
Figura 2.17 – Trânsito de informações no ambiente UVA ............................................. 36

Figura 2.18 – Comparação entre o uso de memória global e compartilhada para 38


diferentes kernels do algoritmo computacional de Thibault e Senocak (2012) .........

Figura 2.19 – Speedup para diferentes arranjos de hardware para a simulação do 40


problema da cavidade, em precisão simples ..............................................................

Figura 2.20 – Speedup para diferentes tipos de precisão para a simulação do problema 41
da cavidade .................................................................................................................

Figura 2.21 – Speedup para o problema da cavidade em relação ao tratamento das 42


matrizes globais e tempo de simulação total ..............................................................

Figura 2.22 – Speedup para o problema de escoamento em torno de um cilindro para o 44


tratamento das matrizes globais e tempo de simulação total ......................................

Figura 2.23 – Malha do problema de estreitamento de Phillips et al. (2009) ................. 45

Figura 2.24 – Resultados dos testes de performance do trabalho de Phillips et al. 46


(2009) .........................................................................................................................

Figura 2.25 – Malha tridimensional elaborada por Senocak et al. (2009) representando 47
parte da cidade de Oklahoma .....................................................................................

Figura 2.26 – Resultados do speedup obtido pelo uso de múltiplas placas gráficas na 48
simulação tridimensional do escoamento em ambiente urbano .................................

Figura 2.27 – Speedup em DNS comparando a performance de CPU versus GPU ....... 49

Figura 2.28 – Divisão de cargas de trabalho para um algoritmo heterogêneo para uso 50
de CPUs e múltiplas GPUs .........................................................................................

Figura 2.29 – Resultados do estudo de Lai et al. (2017) ................................................. 51

Figura 3.1 – Tempos de execução e speedups para o programa de tratamento de dados 60


do túnel de vento ........................................................................................................

Figura 3.2 – Aspecto da malha proposta por Petry (2002) e condições de contorno 61
utilizadas na simulação numérica do problema de escoamento em uma cavidade ....

Figura 3.3 – Campos médios para a componente da velocidade ao longo do eixo x 63


para o problema de escoamento em uma cavidade ....................................................

Figura 3.4 – Campos médios para a componente da velocidade ao longo do eixo y 63


para o problema de escoamento em uma cavidade ....................................................

Figura 3.5 – Campos médios de pressão para o problema de escoamento em uma 64


cavidade ......................................................................................................................

Figura 3.6 – Comparação entre os dados fornecidos por Ghia et al. (1982) e os obtidos 64
pelas diferentes versões do código de fluidos ............................................................
Figura 3.7 – Malha proposta por Petry (2002) para o problema do escoamento sobre 65
um degrau ...................................................................................................................

Figura 3.8 – Campo de velocidades, componente ao longo do eixo x, para o problema 66


do escoamento sobre um degrau – Código Serial ......................................................

Figura 3.9 – Campo de velocidades, componente ao longo do eixo x, para o problema 67


do escoamento sobre um degrau – Código OpenMP .................................................

Figura 3.10 – Campo de velocidades, componente ao longo do eixo x, para o 67


problema do escoamento sobre um degrau – Código CUDA ....................................

Figura 3.11 – Campo de pressões para as diferentes versões do código de fluidos ........ 68
LISTA DE TABELAS

Tabela 3.1 – Performance no código numérico do Túnel: precisão simples .................. 59

Tabela 3.2 – Performance no código numérico do Túnel: precisão dupla ...................... 59

Tabela 3.3 – Performance relativa (speedup) entre os códigos numéricos do Túnel ...... 60

Tabela 3.4 – Tempos de simulação e speedups do problema de escoamento em uma 62


cavidade para 5 tomadas de dados .............................................................................

Tabela 3.5 – Tempos de simulação e speedups do problema de escoamento sobre um 66


degrau bidimensional .................................................................................................
LISTA DE QUADROS

Quadro 1 – Tipos de memórias no device e suas características ..................................... 29

Quadro 2 – Penalidade em função do tipo de memória utilizada na declaração de uma 31


variável .......................................................................................................................

Quadro 3 – Funções atômicas e suas instruções ............................................................. 37

Quadro 4 – Especificações do hardware empregado por Thibault e Senocak (2012) 39


para a simulação do problema da cavidade ................................................................
LISTA DE SIGLAS

CPU – Central Process Unit

CUDA – Compute Unified Device Architecture

DRAM – Dynamic Random-Access Memory

EV – Engenharia do Vento

FLOPS – FLoating-point Operations Per Second

GPU – Graphics Processing Unit

KLD – Kernel Loop Diretive

OpenMP – Open Multi-processing

MPI – Message Passing Interface

N-S – Navier-Stokes

PGI – Portland Group Inc.

PD – Precisão dupla

PS – Precisão simples

TS – Thunder Storms

UVA – Unified Virtual Addressing


11

1 INTRODUÇÃO

A Engenharia do Vento (EV) é um campo de pesquisa que ao longo dos últimos 100 anos
consolidou-se como essencial para o avanço da sociedade, sendo capaz, através de
investigações experimentais e tomada de dados em escala real, analisar e compreender o
comportamento do vento e a interação deste com o homem e suas criações. E a medida que o
arrojo construtivo das edificações e a complexidade dos eventos investigados começaram a
aumentar, o desenvolvimento de mecanismos e métodos capazes de analisar e simular tais
situações se torna imperativo. Nesse sentido, o uso de simulações numéricas na Engenharia do
Vento mostra-se uma alternativa de grande potencialidade, haja vista que atualmente é
possível criar modelos altamente discretizados, e que quando seus resultados são cruzados
com os obtidos em estudos experimentais, obtém-se respostas completas e confiáveis sobre a
ação do vento para uma vasta gama de tipos de estudos na EV. Nesse contexto, vê-se que o
uso de uma abordagem híbrida constitui uma solução consistente e que muitos centros de
pesquisa e autores na literatura vem buscando desenvolver. Todavia, nem sempre é possível
contar com o uso combinado de tais ferramentas, e isto se dá em casos nos quais é necessário
empregar equipamentos muito específicos para a simulação experimental ou para a tomada de
dados em escala real, citando-se como exemplo, a simulação de Thunder Storms (TS) e de
tornados. Para situações tais como as apontadas, tem-se que a simulação computacional
mostrasse um meio alternativo, onde centros de pesquisa com menor capacidade de
capacitação de recursos financeiros e disponibilidade de espaço físico, podem contornar a
falta de equipamentos/recursos, de modo há desenvolver pesquisas relacionadas aos tipos de
eventos exemplificados.

O uso de simulação numérica na EV, teve início por volta da década de 1980, em que
modelos simples buscando simular escoamentos bidimensionais foram confeccionados. E
conforme foram ocorrendo os avanços tecnológicos na área da Computação, possibilitando
maiores capacidades de processamento e quantidades de memória, os modelos numéricos
começaram a se tornar mais robustos e ficando cada vez mais capazes de simular problemas
tridimensionais, apresentando alto grau de refinamento e podendo até mesmo recriar
estruturas complexas que surgem durante o escoamento do vento e a interação deste com
corpos imersos no fluido. Todavia, sempre houverem fatores limitantes no uso de modelos

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


12

numéricos, sendo os principais a capacidade de processamento e a memória disponível no


sistema do computador. E aqui, salienta-se que a qualidade do resultado das simulações
computacionais está diretamente condicionada ao grau de refinamento do domínio do fluido
(vento). Logo, percebe-se que, embora ocorram avanços cada vez mais significitivos, a
capacidade de recursos computacionais sempre configurou um fator limitante no estudo de
problemas da Engenharia do Vento utilizando simulações numéricas.

Historicamente, a simulação computacional em diversos campos da ciência, sempre buscou


utilizar Central Process Units (CPUs), cuja função é de realizar o processamento de dados do
modelo numérico confeccionado para um dado fim. Em um primeiro momento, os algoritmos
desenvolvidos se baseavam no uso de um CPU único para a realização de todas as tarefas do
código durante a simulação computacional, o que se mostrava uma abordagem pouco
eficiente e que, ao final, gerava tempos de simulação computacional demasiadamente
elevados, mostrando-se assim muita limitada. Visto a ineficiência do emprego desta
abordagem, mecanismos de multiprocessamento, tais como o MPI e OpenMP, foram sendo
desenvolvidos. Sendo assim, os algoritmos numéricos foram evoluindo a sua forma de escrita,
buscando adaptar a linguagem de programação e as diretrizes de paralelização de tarefas, de
modo a propiciar o emprego de várias unidades de processamento (com ou sem memória
compartilhada). O que acabou por levar o surgimento dos supercomputadores (clusters), que
utilizam o conceito do uso de múltiplas unidades de processamento para a simulação
numérica de diversos problemas de interesse científico.

Ao longo das décadas, a capacidade de processamento dos computadores, tinha como


propósito desenvolver a cada nova geração de CPUs, novos chips com maior frequência de
processamento e esse objetivo foi mantido até a meados de 2005. Após, viu-se que a
confecção de processadores com maior clock (frequência), ou em outros termos, cada vez
mais “rápidos” começava a se tornar mais custosa e pouco produtiva. Nesse sentido, os
fabricantes de CPUs, alteraram seu foco no desenvolvimento de chips, buscando elaborar
CPUs com múltiplos núcleos de processamento, ao invés de apenas um. Tal feito propiciou
grandes avanços na simulação numérica, permitindo a paralelização de tarefas através de
diretrizes OpenMP, entregando um maior desempenho computacional, ver Figura 1.1.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
13

Figura 1.1 – Características de CPUs ao longo dos últimos 42 anos


(múltiplas fontes)

Nesse ponto, o nível de discretização dos modelos numéricos pode sofrer grandes avanços e
estudar situações cada vez mais complexas. Inevitavelmente, com o aumento do nível de
discretização, a capacidade de processamento continuou ainda a ser um grande obstáculo,
visto que o tempo demandado para a simulação computacional ainda se mostrava
demasiadamente alto. Destacando-se neste ponto, o trabalho desenvolvido por Alminhana
(2017), que embora tenha utilizado 24 núcleos de processamento para a simulação numérica
de escoamentos de problemas de interesse da Engenharia de Vento, o tempo de
processamento alcançou patamares de 3 a 4 meses de simulação.

Acompanhando o desenvolvimento dos CPUs, as placas de vídeo, Graphics Processing Units


(GPUs), foram adquirindo a cada nova geração, maior capacidade de processamento e
memória, e passaram a contar com dezenas a várias centenas de núcleos. Sendo assim, o uso
de tais equipamentos contendo muitos núcleos de processamento de GPUs mostrou-se uma
opção atraente para a simulação computacional haja vista a capacidade de processamento
entregue e também em função da natureza dos mesmos, que se baseia na realização de tarefas
em paralelo. Vislumbrando essa possibilidade, a Nvidia, juntamente com empresas de
software, desenvolveu mecanismos que possibilitaram o uso de linguagens de programação
usuais do meio científico para a criação de códigos numéricos, facilitando o emprego da GPU
para a simulação computacional. O produto desenvolvido pela Nvidia, e parceiros, possibilita

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


14

utilizar tanto os núcleos de processamento da GPU da Nvidia (CUDA cores) quanto os


núcleos do CPU.

O uso de uma abordagem híbrida tal como GPUs + CPU possibilitou a melhora no
desempenho de processamento para diversas aplicações de interesse científico. Em Lee et al.
(2010), encontra-se um estudo cujo propósito foi investigar profundamente a diferença na
performance entre o uso de CPU x GPU+CPU. Os autores estudaram para diversas rotinas
comuns em algoritmos numéricos científicos a performance no processamento, sendo
estabelecido ao final uma relação entre o desempenho CPU x GPU+CPU. Para a investigação,
utilizou-se apenas um processador i7-960 e uma GPU Nvidia GTX 280. Os resultados,
referentes a performance relativa, determinados por Lee et al. (2012) seguem apresentados na
Figura 1.2.

Onde:
SGEMM : rotinas de algebra linear; MC: simulação de Monte Carlo;
Convol: convolução em imagens; FFT: transformada rápida de Fourier para análise de sinais
SAXPY: produtor escalar de vetores; LBM: transformações temporais;
Constraint Solver: mecânica de corpos rígidos; spMV: solver de matrizes esparsas;
GJK: detector de colusão de partículas; Sort: banco de dados;
RC: renderização de volumes; Search: procura em banco de dados;
Hist: análise de imagens; Bilat: análise de imagens.

Figura 1.2 – Resultados de Lee et al. (2010) entre o desempenho de


CPU x GPU para rotinas usuais em códigos numéricos

Analisando os resultados apresentados na Figura 1.2, percebe-se a potencialidade do uso de


paralelização via GPUs em códigos numéricos, uma vez que para diversas rotinas, o

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
15

desempenho do código com GPU obteve desempenhos superiores a 2x ou mais, em relação às


rotinas empregando apenas o uso de CPU. Ainda sobre a performance de processamento entre
CPUs e GPUs, encontra-se em Senocak et al. (2009) a Figura 1.3, que ilustra a evolução em
termos de capacidade de processamento deste tipo de equipamento.

Figura 1.3 – Evolução de CPUs e GPUs em termos de processamento


de operações de ponto flutuante – FLOPs (SENOCAK et al, 2009)

Pelos dados expostos na Figura 1.3, tem-se que a capacidade de processamento de GPUs
cresceu exponencialmente desde o início dos anos 2000 em relação ao das CPUs. Logo, o uso
desses equipamentos se mostra mais interessante no que concerne a simulação de problemas
computacionais com alta demanda de processamento.

No contexto apresentado, as possibilidades de paralelização de tarefas em códigos


computacionais, na qual pode-se contar tanto com os núcleos da GPU quanto CPU, mostrasse
o futuro no que tange a simulação numérica, e principalmente no que concerne aos problemas
de interesse da EV, que demandam, naturalmente, muitos esforços de processamento de
dados. Nesse sentido, o presente seminário buscará aprofundar-se no que toca a compreensão
e funcionamento do processamento em GPUs, das potencialidades e peculiaridades do uso da
arquitetura CUDA e suas diretrizes. Ainda se tem que será investigado na literatura
contemporânea casos de aplicação de paralelização de códigos numéricos empregando o
CUDA, de modo a avaliar as possibilidades de uso da linguagem CUDA em problemas de

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


16

interesse da Dinâmica dos Fluidos e da Engenharia do Vento. Ao final, pretende-se


demonstrar em estudos de casos simples, a performance no processamento computacional
decorrentes do uso da arquitetura CUDA para algoritmos elaborados pelo presente autor do
seminário e os comparar com os obtidos para códigos empregando apenas recursos de CPUs,
em versões seriais e paralelizados.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
17

2 CUDA

Neste item serão discutidas as peculiaridades e princípios de funcionamento da ferramenta


CUDA (Compute Unified Device Architecture), no que concerne ao seu uso, particularizando
ao final, para aplicações de interesse da Dinâmica dos Fluidos e Engenharia do Vento.

2.1 INTRODUÇÃO

A computação paralela a muitos anos vem sendo empregada em larga escala em diversos
campos da ciência dado o aumento da complexidade dos problemas investigados e da
demanda de processamento de dados. Nesse sentido, a paralelização de tarefas passou a se
tornar um pré-requisito para qualquer código numérico de simulação computacional. E desde
os primórdios das linguagens paralelas de computação, a atenção era voltada para o
processamento via CPU, e grande parte da comunidade acadêmica utilizava diretrizes de
paralelização de tarefas por sistema de memória compartilhada (OpenMP) ou não (MPI).

Em um contexto histórico, as GPUs sempre tiveram, majoritariamente, suas aplicações


voltadas para a renderização e cálculos matemáticos de jogos eletrônicos, havendo melhoras
significativas nos componentes responsáveis pelo gerenciamento e processamento de tarefas
em um ritmo mais acelerado do que os dos processadores centrais (CPUs), contando a cada
geração com maior capacidade de processamento e memória, de modo que aos poucos fora
despertado o interesse de se utilizar os recursos de processamento das placas gráficas em
problemas de interesse científico. Contudo, as poucas linguagens computacionais que
possibilitavam o uso de GPUs não se apresentavam intuitivas para uso e aplicação em
computação paralela, dada a complexidade da mesma e o nível de direcionamento das
diretrizes ser muito elevado, exigindo um conhecimento muito específico das mesmas. Nesse
ponto, teve-se empecilhos muito grandes no estímulo do uso de GPUs no processamento de
código de interesse científico, um motivado pela complexidade de programação e outro
associado à cultura de programação que era majoritariamente voltada ao uso de CPUs.

Aos poucos os empecilhos quanto ao uso de GPUs foram se esvaindo e o paradigma mudou
completamente quando a empresa fabricante de tecnologia para GPU chamada Nvidia

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


18

desenvolveu a arquitetura CUDA para seus processadores. O advento dessa nova arquitetura
foi capaz de contornar as dificuldades ligadas a forma de programação em placas de vídeo
devido a moderna engenharia de computação desenvolvida, que possibilitou a
compatibilização de instruções entre o hardware das GPUs com linguagens de programação
tal como C, usualmente empregada em diversas áreas da ciência. E aos poucos, grandes
centros de pesquisa começaram a trocar o modelo de paralelização de seus programas
computacionais para que passassem a contar com o uso de GPUs. Acrescenta-se que desde
que surgira a arquitetura CUDA, em torno do ano de 2007, teve-se na lista dos 5 maiores
supercomputadores do mundo, 3 utilizando GPUs, e no ano de 2012, o supercomputador mais
rápido do mundo utilizava GPUs para a paralelização de suas atividades. A ascensão da
linguagem CUDA ocorreu devido à sua simplicidade e as poucas alterações na forma de
programar em relação a paralelização de atividades via CPU.

Inicialmente, os pesquisadores que pretendiam contar com recursos de paralelização de


atividades via GPU, podiam fazer apenas uso da complicada linguagem OpenCL. Após o
desenvolvimento de compiladores específicos, tornou-se possível utilizar a linguagem C,
permitindo assim uma programação mais intuitiva e amigável para pesquisadores e
programadores. E em 2009, a Nvidia, juntamente com a empresa de software PGI,
desenvolveram em parceria um compilador que interpretaria a linguagem Fortran contando
com os recursos de paralelização de atividades da arquitetura CUDA em GPUs. E através
deste compilador, muitos pesquisadores passaram a utilizar a potencialidade da paralelização
de tarefas pela GPU via CUDA Fortran, já que usualmente nos campos de simulação se
emprega a linguagem Fortran para a escrita de programas computacionais devido sua
simplicidade e alta performance.

Desde de a possibilidade de utilizar linguagem intuitivas, tais como C e Fortran, o uso da


arquitetura CUDA para a paralelização de tarefas via GPU foi capaz de revolucionar alguns
campos da ciência, podendo-se citar alguns exemplos (SANDERS; KANDROT, 2010):

a) melhora no processamento de dados em exames médicos, tais como o de ultrassom,


que foram capazes de permitir diagnósticos mais rápidos e com maior precisão para
um volume maior de informações;

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
19

b) ganhos de processamento para o tratamento de problemas de Dinâmica dos Fluidos,


podendo-se simular interação fluido-estrutura sem precisar utilizar estruturas
complexas de supercomputadores (clusters) com diversos nós de processamento;

c) simulação computacional de impactos ambientais provocados por poluentes,


diminuindo a carga de trabalho experimental em laboratórios para detectar a eficiência
do uso de dados elementos químicos, ou misturas, no processo de limpeza de um
ambiente contaminado.

A passagem da carga de cálculos massivos do CPU para o GPU está vinculada a velocidade
de realização de tarefas que a placa gráfica possui. E a explicação para tal fato, está conectada
a arquitetura da GPU/CUDA, que permite que a realização de uma tarefa seja quase que
instantânea, uma vez que a GPU não armazena informações pertinentes ao estado da tarefa
realizada, o que, por exemplo, não ocorre em tarefas feitas empregando CPUs (RUESTCH;
FATICA, 2014). Dados os pontos levantados e discutidos, verifica-se que o avanço em
diversos campos da ciência, que contam com simulações computacionais, está
inevitavelmente vinculado ao uso de algoritmos que utilizem equipamentos com alto nível de
processamento de tarefas em paralelo, cuja aplicação ideal é a de GPUs, visto que as mesmas
foram desenvolvidas para tais situações, possibilitando deste modo, maiores desempenho e
eficiência.

2.2 PRINCIPAIS ASPECTOS

A linguagem de programação CUDA pode ser definida como sendo híbrida, visto o uso
combinado do CPU e GPU. Para fins de uso da potencialidade do CUDA, tanto em linguagem
C ou em Fortran, deve-se sempre ter em mente figuras identificadas como host e device.
Sendo o host composto pelo CPU e sua memória, ao passo que a GPU e sua memória
compõem o device.

Na utilização da arquitetura CUDA, o código principal sempre é controlado pelo host, e cabe
a este destinar e gerenciar as tarefas a serem realizadas por ele próprio e pelas que ficarão a
cargo do device. Em suma, tem-se que tarefas massivas de computação serão direcionadas do
CPU para a GPU, ao passo que o CPU será o responsável pelo trânsito, andamento e troca de

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


20

informações ao longo do funcionamento do programa computacional, garantido o fluxo das


atividades (SANDERS; KANDROT, 2010).

Neste ponto, cabe salientar como funciona a organização do device, na arquitetura CUDA,
para a realização de uma rotina. Inicialmente, uma tarefa (thread) a ser realizada na GPU é
feita por um core ou thread processor, sendo que um conjunto de thread processors constitui
o que se chama de multiprocessor. Um multiprocessor, por definição, é composto por
unidades de processamento e uma memória compartilhada. O conjunto composto por todos os
multiprocessors formam a GPU. Ao final, tem-se que a GPU, juntamente, com memória
DRAM, configuram o device. Abaixo segue a Figura 2.1 que evidencia cada unidade
componente do device.

Figura 2.1 – Unidades componentes do device (RUESTCH; FATICA,


2014)

2.2.1 Kernels

Para que o device realize uma tarefa específica, demandada pelo host, é necessário estruturar
o que se identifica por kernel, sendo esta parte do código entendida como um conjunto de
instruções que serão realizadas somente pela GPU. Sendo assim, toda tarefa do código que
demande processamento via GPU deve utilizar kernels, que deverão conter todas as instruções
referentes a realização das instruções almejadas. Os kernels somente são processados quando
acionados por comandos específicos e usualmente são escritos no formato de sub-rotinas.
Contudo, em alguns casos, pode-se escrevê-las ao longo das instruções do código do host.
Para que um kernel seja lançado, deve-se ao longo do corpo principal do algoritmo
computacional (código no host), inserir comandos de acionamento. Abaixo, mostra-se uma

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
21

forma de acionar um kernel em CUDA Fortran, sendo que as tarefas a serem executadas pelo
mesmo se encontram descritas em uma sub-rotina específica.

Figura 2.2 – chamada de kernel construído em uma sub-rotina

Na Figura 2.2, a segunda linha específica que a sub-rotina “incremento” será realizada pelo
device. No trecho composto por “<<<grid, tPB>>>”, tem-se que as variáveis “grid” e “tPB”
especificam o tamanho da malha de blocos de processamento (thread blocks) e o número de
unidades de processamento (threads) por thread block que a GPU deverá utilizar para
completar a tarefa lançada pelo host. Destaca-se que thread blocks podem ser uni, bi ou
tridimensionais conforme é especificado no seu acionamento. Para invocar kernels que
utilizem duas ou mais dimensões para os blocos de processamento, pode-se utilizar o
comando “dim3”, tal como é apresentado abaixo, na Figura 2.3.

Figura 2.3 – Criação de kernels com mais de uma dimensão (PGI


Compiler & Tools (2017)

Destaca-se que é necessário que a execução de thread blocks seja independente entre eles
próprios. Podendo-se entranto, dentro de um mesmo thread block a troca de informações entre
os threads locais.

Na linha 1 da Figura 2.2, tem-se que a instrução “a_d = a” é responsável pela transferência de
dados de informações que constam na variável “a”, que está alocada no host, para “a_d”, que
está situada na memória do device. Já na linha 3, tem-se a transmissão das informações entre a
variável “a_d”, que fora utilizada no device, e “a”, que foi atualizada em função dos valores
determinados para “a_d” após a execução do kernel “incremento”.

Um kernel construído como uma sub-rotina apresenta como estrutura padrão as diretrizes
expostas na Figura 2.4:

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


22

Figura 2.4 – Estrutura de um kernel via sub-rotina

Na Figura 2.4, tem-se que toda sub-rotina que lança um kernel deve fazer parte de um module,
que no caso do exemplo é chamado de “cudakernels”. Além disto, tem-se que a diretriz
attributes(global) indica que o presente kernel, intitulado “incremento” é chamado do host
para ser executado no device. Caso fosse especificada a diretriz attributes(local), teria-se que
o kernel será chamado no device e nele próprio será executado. E por fim, tem-se a
possibilidade de declarar-se um módulo como sendo attributes(host), o qual será executado
apenas pelo host. Após, tem-se a declaração das variáveis que integram o kernel, e aqui
destaca-se o atributo value, que tem como função buscar na memória do host o valor de uma
variável escalar atribuída ao longo do código. Por fim, tem-se na variável “i” a identificação
de todos os threads lançados para a realização das tarefas contidas no kernel “incremento”.
Nesse ponto, se faz necessário compreender como é feita a ordenação dos threads em função
do número de thread blocks lançados na chamada do kernel.

Tomando como ponto de partida um kernel genérico lançado com o seguinte padrão
“<<<4,4>>>”, tem-se a formação de 4 thread blocks, sendo executados 4 threads por bloco.
Logo, ao todo, serão executados 16 threads. Do ponto de vista do device, os threads são
numerados de forma local, ou seja, por thread block, sendo assim, não possuem uma
numeração global. Dessa forma, deve-se utilizar um mecanismo que ajuste a numeração dos
threads, de termos locais para globais. E para isso, recomenda-se o uso da instrução
apresentada na linha 9 da Figura 2.3. Para uma melhor compreensão do sistema de
identificação dos threads, dentro dos threads blocks e também do que efeito gerado pela
instrução da linha 9, tem-se a Figura 2.5, que demonstra a identificação local e global para os
threads lançados para a realização das tarefas do kernel genérico tomado como exemplo.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
23

Figura 2.5 – Organização local e global dos threads em função do


número de thread blocks (RUESTCH; FATICA, 2014)

Caso se deseje fazer a chamada de kernels, de uma outra forma, sem utilizar sub-rotinas,
pode-se empregar o que se chama de kernel loop directive (KLD). A abordagem KLD, além
de ser uma forma alternativa para o chamamento de kernels, é ideal para situações as quais
são exigidos o uso de loops (comandos que contam com o uso de instruções do tipo do) (PGI
Compiler & Tools, 2017). A escrita das instruções mostradas na Figura 2.3, utilizando a
abordagem KLD, pode ser realizada utilizando o seguinte padrão de instruções:

Figura 2.6 – Instruções para um kernel loop directive

Diferentemente da sequência de instruções da Figura 2.3, na Figura 2.5, as instruções são


reduzidas a apenas variáveis utilizadas no device e há possíveis escalares especificadas na
memória do host, tal como o caso da variável “b”. O emprego de KLDs ao longo do algoritmo
numérico pode ocasionar acréscimos substanciais na performance do código, sendo, uma boa
alternativa para a escrita de algoritmos em CUDA Fortran mais eficientes (PGI Compiler &
Tools, 2017). Segundo a PGI Compiler & Tools (2017), deve-se ficar atento às seguintes
restrições ao utilizar a abordagem de kernels via KLDs:

a) Se a diretiva especifica “n” dimensões, ela deve ser seguida por pelo menos
loops de “do” fechados;

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


24

b) Os loops “do” devem ter limites invariáveis: o limite inferior, limite superior, e
incremento invariável com relação a qualquer outro loop dentro do mesmo
kernel;

c) Não pode haver instruções “goto” ou “exit” dentro ou entre quaisquer loops
que tenham sido mapeados empregando valores de configuração da grid e do
bloco de processamento;

d) O corpo dos loops pode conter instruções de atribuição, instruções “if”, loops e
declarações “goto”;

e) Somente variáveis de CUDA Fortran ou CUDA C são permitidas dentro dos


loops;

f) As funções intrínsecas de CUDA são permitidas, se forem permitidas no


código do dispositivo, mas as intrínsecas específicas do dispositivo, tal como
syncthreads, funções atômicas, etc., não são;

g) Sub-rotinas e chamadas de funções para subprogramas de atributos


(dispositivos) são permitidas se estiverem no mesmo módulo que o código que
contém a diretiva;

h) Arrays usados ou atribuídos no loop devem ter o atributo device;

i) Loops implícitos e a sintaxe de array F90 não são permitidos nos KLDs;

j) Os escalares usados ou atribuídos no loop devem ter o atributo de device ou o


compilador fará uma cópia dessa variável durante a duração dos loops, um
para cada segmento. Exceto no caso de reduções; quando uma redução tem um
alvo escalar, o compilador gera uma sequência correta de operações
sincronizadas para produzir uma cópia na memória global do device ou no
host.

Outro ponto a se observar durante o lançamento de um kernel é quanto à declaração das


variáveis no corpo do algoritmo do host. No início do código, comumente, são declaradas
todas as variáveis que serão utilizadas ao longo do algoritmo, e nessa região, também deverão
ser declaradas todas as variáveis que serão utilizadas pelo device nos kernels que serão

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
25

lançados. Para isto, deve-se ao declarar as variáveis do device colocando o atributo device, tal
como é mostrado abaixo na linha 23 da Figura 2.7.

Figura 2.7 – Atributos de variáveis a serem empregadas no device

Todas as variáveis declaradas com o atributo device são alocadas na memória global do
device. As variáveis que podem ser empregadas em códigos, contando com recursos do
CUDA, podem possuir diferentes tipos de qualificações. Conforme PGI Compiler & Tools
(2017), os principais tipos são:

a) device: este tipo de qualificação para uma variável faz com que se a declaração
seja feita no código do host, que possa ser utilizada em sub-rotinas que a
contenham. Já se for alocada em um módulo que contenha sub-rotinas de
kernels, esta variável pode ser utilizada em qualquer kernel do módulo.

b) managed: este tipo de qualificação só pode ser feita a partir do CUDA 6.0.
Como principal característica, tem-se que variáveis declaradas com este
atributo podem ser utilizadas tanto no host quanto no device.

c) constant: são utilizadas apenas no device. As principais características


inerentes a uma variável deste tipo são: quando declaradas em módulos no
device, ela pode ser utilizada em todos os kernels que pertencem ao referido
módulo; possuem duração de todo funcionamento do algoritmo numérico e
podem ser modificadas no trecho do código referente ao host;

d) shared: podem ser utilizadas apenas no device. São alocatadas na memória


compartilhada do device e todos os threads dentro de um mesmo thread block
podem acessá-la e alterá-la;

e) pinned: são normalmente utilizadas em arrays e podem fazer com que a


transferência de dados entre host e device seja realizada de forma mais rápida;

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


26

f) texture: são utilizadas apenas no device, devendo ser ponteiros do tipo real ou
inteiro. Podem causar melhoras no desempenho do código.

2.2.2 Precisão simples x precisão dupla

Inicialmente, a arquitetura CUDA contava apenas com núcleos de processamento para


precisão simples (PS), e somente após o lançamento de placas com capacidade computacional
de 1.3, foi possível desenvolver algoritmos que contassem com precisão dupla (PD).
Atualmente, a capacidade de computação, das GPUs da Nvidia com arquitetura CUDA,
encontra-se em 6.1, possuindo dezenas de centenas de thread cores, que, majoritariamente,
são destinados ao cálculo utilizando variáveis declaradas como PS, mas ainda assim,
possuindo núcleos de processamento para PD. Salienta-se que a performance de algoritmos
utilizando precisão simples sempre será superior em relação aos que contam com precisão
dupla, devido a diferença entre a quantidade de núcleos disponíveis para cada cálculo. Abaixo
é demonstrado na Figura 2.7, algumas informações pertinentes aos dispositivos CUDA, em
que são apresentadas características quanto à capacidade de computação, número de unidades
de processamento por tipo de precisão e o máximo número de threads.

Figura 2.8 – Especificações de dispositivos CUDA em diferentes


capacidades computacionais (RUESTCH; FATICA, 2014)

Observando a Figura 2.8, verifica-se que mesmo em capacidades de computação altas, tais
como as 2.0 e 3.5, o número de unidades de cálculo para precisão dupla é relativamente baixo,
atingindo ao máximo 832 núcleos na GPU Tesla K20, ao passo que para a precisão simples
tem-se 2496 núcleos, o que resulta em uma performance de 3524,35 GFLOPS para PS e de
1174,78 GFLOPS para PD. Destaca-se que GFLOPS denota que a quantidade de operações de
ponto flutuante que o equipamento é capaz de realizar por segundo, sendo este parâmetro uma
forma de medir o desempenho de processamento. De forma resumida, vê-se que o melhor uso
e maior desempenho são obtidos, ao se utilizar GPUs, na declaração de variáveis empregando

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
27

PS, onde verifica-se uma performance muito superior em relação ao uso de CPUs para
atividades similares.

2.2.3 Transferência de memória entre host e device

Tem-se que a taxa de transferência, entre host e device, ocorre pela placa-mãe em seu
barramento PCI-Express, sendo limitada pela tecnologia desta. Sendo assim, caso a placa
tenha conexões de 2° ou de 3° geração, a capacidade de troca de informações será,
respectivamente, 8 GB/s e 16 GB/s. No entanto, a transmissão de informações entre a
memória do device e a GPU é capaz de atingir taxas na faixa de 200 a 400GB/s, o que indica
que a transmissão de dados entre host e device deve ser evitada sempre que possível, pois ela
sempre resultará em gargalos de desempenho no código computacional (RUESTCH;
FATICA, 2014). Neste sentido, para melhor compreender que a transmissão entre dados deve
ser evitada sempre que possível, cita-se o trabalho de Nogueira et al. (2015), que investigou
os tempos de execução de dadas tarefas para um algoritmo numérico que utilizava os recursos
de processamento da GPU. Nogueira et al. (2015) percebeu, para seu código, que o tempo de
transferência de dados foi maior do que o tempo de execução da rotina principal do código, tal
como é mostrado na Figura 2.9.

Figura 2.9 – Tempos de execução do código de Nogueira et al. (2015)

Outro ponto a se observar em algoritmos que utilizam processamento via GPU/CUDA é


quanto à declaração das variáveis, uma vez que o tipo de memória empregada para a
transmissão das informações pode gerar perdas de desempenho. Nesse ponto, destaca-se a
pageable memory e a pinned memory utilizadas para a troca de informações entre host e

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


28

device. A pageable memory trata-se de um tipo de memória que pode ser apagada e
sobreescrita ao longo da execução de um algoritmo computacional, ao passo que a pinned
memory uma vez preenchida por informações não pode ser apagada ao longo da execução do
código. Além disso, tem-se que toda variável declarada como sendo do tipo pageable
necessitará da criação de um buffer temporário de memória do tipo pinned para que assim se
faça a transmissão de informações entre host e device (RUESTCH; FATICA, 2014). A seguir
demonstra-se, na Figura 2.10, a diferença que existe na transmissão de informações entre a
memória do tipo pageable em relação a do tipo pinned.

Figura 2.10 – Transmissão de informações por pageable memory e


pela pinned memory (RUESTCH; FATICA, 2014)

Sendo assim, a alocação de variáveis empregando um dos dois tipos de memória implicará,
inevitavelmente, em diferentes níveis de performance, uma vez que em um deles a
transmissão é feita diretamente e o outro necessita de um passo intermediário para tal.
Contudo, deve-se ficar atento que a alocação de variáveis do tipo pinned pode levar ao
esgotamento da memória RAM do host rapidamente, pois uma vez alocada uma variável neste
tipo de memória, a mesma só voltará a estar disponível após a finalização do código numérico
ou através da desalocação da variável. Nesse sentido, o uso exacerbado de variáveis do tipo
pinned pode afetar tanto o desempenho do código quanto o do funcionamento do computador.
Logo, seu uso deve ser feito com ponderação (SANDERS; KANDROT, 2010).

Em Ruestch e Fatica (2014) é escrito um código que avalia a transferência de dados entre host
e device empregando para isto as memórias do tipo pageable e pinned. Os autores utilizaram
uma placa Tesla K20 para a realização dos testes e chegaram ao seguinte resultado:

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
29

Figura 2.11 – Performance entre pageable memory e pinned memory


em uma Tesla K20 (RUESTCH; FATICA, 2014)

Analisando a Figura 2.11, verifica-se que o desempenho na transmissão de dados entre host e
device é 3,46x maior utilizando a memória do tipo pinned diretamente, ao passo que a
transmissão inversa, ou seja, device para host, é 4,12x mais rápida. Utilizando o código
proposto por Ruestch e Fatica (2014) e trocando a GPU por uma Nvidia GTX 750Ti, obtêm-
se os seguintes resultados:

Figura 2.12 – Performance na transmissão de dados por pageable


memory e por pinned memory em uma GTX 750Ti (elaborado pelo
autor)

Para a GTX 750Ti já se verifica que o desempenho na transmissão de dados apresenta uma
diferença menor na performance entre a pageable memory e pinned memory. Nesse sentido,
tem-se que para esta placa de vídeo, o uso de pinned memory pode ser reduzido em relação ao
do que seria para a Tesla K20.

2.2.4 Tipos de memória no device

Para aproveitar ao máximo a potencialidade e nível de paralelização das GPUs modernas, faz-
se necessário o conhecimento dos diversos tipos de memórias presente no dispositivo.
Segundo Ruestch e Fatica (2014), tem-se que as memórias que a GPU possui são separadas
em:

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


30

Quadro 1 – Tipos de memória no device e suas características

(fonte: RUESTCH; FATICA, 2014)

Observando o Quadro 1, verifica-se diferentes tipos de memória, sendo que cada uma possui
distintas características em relação à outra. Abaixo são apresentadas as principais
peculiaridades das mesmas:

a) Global: é um tipo de memória no qual, tanto o host quanto o device, podem ler
e escrever dados. A sua duração se dá por aplicação e a informação fica
disponível para todos os threads lançados e pelo host, ela fica localizada na
DRAM;

b) Local/Register: variáveis que são declaradas apenas no device são registradas


neste tipo de memória. Ela engloba tanto os registradores, que ficam na
memória do thread core, quanto a memória local, que fica na DRAM. Sua
duração se dá pelo tempo de execução de um thread. Por fim, tem-se que este
tipo de memória permite a leitura e escrita de dados;

c) Shared: é uma memória compartilhada por todos os threads presentes em um


thread block. É uma alternativa para otimizar a velocidade de processamento
de dados na GPU, uma vez que a memória global é mais lenta;

d) Constant: é do tipo que permite a leitura e escrita de dados no host. Contudo,


no device é possível apenas a leitura dos dados;

e) Texture: pode ser entendida como sendo similar à memória constant. Constitui
uma alternativa para se evitar o uso da memória global, que é mais lenta na
ordem de 100x.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
31

Em Cardoso (2012) é apontado em função do tipo de memória, o quanto será afetada a


performance no trânsito de informações. Em outros termos, a penalidade pelo uso de um dado
tipo de variável em um kernel.

Quadro 2 – Penalidade em função do tipo de memória utilizada na


declaração de uma variável (CARDOSO, 2012)

Observando o Quadro 2, conclui-se que é preferível, sempre que possível, utilizar as


memórias do tipo Register, Shared e Constant, uma vez que as penalidades envolvidas são as
mínimas possíveis para o tráfego de informações no device. Segundo Couto (2016), a
memória do tipo Register é a única capaz de fornecer o maior desempenho possível nas
GPUs, pois proporciona grandes bandwidth1 e latency2. E neste ponto, tem-se que Ruestch e
Fatica (2014) apontam que toda modificação em um algoritmo que utilize GPU para
processamento paralelo deve ter total entendimento em como ela afetará o bandwidth do
mesmo. Caso o bandwidth observado em uma dada tarefa seja muito inferior ao máximo que
a GPU consegue atingir, deve-se prover investigações e alterar o código, de forma a obter
uma solução mais otimizada em termos de transferência de dados. Neste ponto, é válido
comentar o trabalho desenvolvido por Thibault e Senocak (2012), onde os autores avaliaram o
desempenho do código numérico ao empregar a memória do tipo global e a do tipo shared. A

1
Bandwidth: pode ser entendido como a taxa de transferência de dados.
2
Latency: tempo entre o acionamento de um processo lançado e a percepção de o efeito deste.

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


32

investigação realizada pelos presentes autores será comentada em maior detalhe mais à frente,
no item 2.3.1.

Em Couto (2016), encontra-se uma ilustração que demonstra o fluxo de informações entre as
diferentes memórias presentes na GPU.

Figura 2.13 – Transmissão de dados entre as diferentes memórias no


device (COUTO, 2016)

Sobre o uso da memória do device, Ruestch e Fatica (2014), comentam que o aspecto mais
importante a se considerar na performance de um algoritmo em CUDA seria o acesso à
memória global de forma corrida, isto é, de forma sequencial sem intervalos.

Para melhor compreender o que seria o acesso corrido a memória, faz-se necessário definir o
que se chama de warp. Segundo o manual da PGI Compiler & Tools (2017), toda instrução
executada pelo device é feita a partir de grupos de 32 threads, denominados warps. Sendo
assim, tem-se que a memória global do device é lida e escrita de forma corrida em termos de
um warp para capacidade computacionais acima de 2.0, e de meio-warp para inferiores
(RUESTCH; FATICA, 2014).

O acesso a memória global modifica diretamente a performance do algoritmo, tanto que sobre
este aspecto, Ruestch e Fatica (2014) investigaram o impacto no bandwidth de um código em
função do tipo de deslocamento (offset: pulo na sequência de dados ou strided: múltiplo de

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
33

contador de acesso) na leitura à memória global do device para diversas GPUs. Os resultados
obtidos seguem apresentados nas Figura 2.14 e 2.15.

Figura 2.14 – Impacto no bandwidth em função do deslocamento de


acesso a memória global do device, para precisão simples
(RUESTCH; FATICA, 2014)

Figura 2.15 – Impacto no bandwidth em função do deslocamento de


acesso a memória global do device, para precisão dupla (RUESTCH;
FATICA, 2014)

Analisando os resultados ilustrados nas Figuras 2.14 e 2.15, conclui-se que a melhor
performance obtida, na situação de um offset no acesso à memória global, ocorre quando não
há deslocamento, ou quando o deslocamento é de meio ou um warp completo. Ainda se tem a
possibilidade de acesso via strided, deve ser evitada sempre que possível, uma vez que afeta
em demasia o bandwidth do algoritmo.

Contudo, nem sempre é possível evitar de utilizar acessos à memória global que usem
alternativas que resultem em uma melhor performance. Para essas situações, uma forma para
amenizar o impacto na performance, é buscar empregar tipos de memória que possuam

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


34

penalidades menores, tal como as do tipo texture ou shared. Todavia, acessos do tipo strided
sempre irão resultar em baixa performance no tráfego de informações.

2.2.5 Tópicos sobre paralelismo

Como visto anteriormente, o processo de transferência de dados entre host e device é a


principal fonte de consumo de tempo no uso de GPU. Nesse sentido, tem-se que o
gerenciamento das memórias é determinante para que se obtenha bons níveis e tempos de
execução de tarefas através. Contudo, há casos em que os procedimentos de transferência de
dados estão próximos do pico esperado para a GPU e nesse aspecto, tem-se que para não se
perder performance computacional utilizar um conjunto de instruções que garanta bons níveis
de paralelismo nos kernels do algoritmo. Conforme Ruestch e Fatica (2014), é possível
alcançar bons níveis de paralelismo através de duas abordagens:

a) nível de paralelismo dos threads;

b) nível de paralelismo das instruções.

A primeira abordagem busca aumentar a performance do algoritmo através dos parâmetros de


execução dos kernels no host. Para isso, busca-se altera-se o que se chama de taxa de
occupancy3 na execução de um kernel, uma vez que este parâmetro indica o nível de latência
(latency) de uma dada tarefa, podendo-se entender que uma alta occupancy está associada a
uma baixa latência. O impacto do primeiro tipo de paralelismo pode ser visto em Ruestch e
Fatica (2014), que analisaram os efeitos do tamanho dos threads blocks e as respectivas
occupancy e bandwidth resultantes para um algoritmo em específico.

Já a segunda abordagem busca ocultar as latências inerentes ao funcionamento da GPU


através do conceito de load batching e load-use separation. Esta técnica é utilizada em
variáveis declaradas como pertencentes a memória do tipo shared. Este tipo de melhora no

3
Occupancy: taxa que relaciona o número de warps ativos e o número máximo de warps presentes na GPU.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
35

paralelismo pode ser empregado também em KLDs, para maiores detalhes ver Ruestch e
Fatica (2014).

(a) Tesla K20 (b) Tesla C2050

Figura 2.16 – Resultados para a occupancy e bandwidth em função do


tamanho do thread block (modificado de RUESTCH; FATICA, 2014)

2.2.6 Uso de múltiplas GPUs

As vantagens inerentes ao uso da arquitetura CUDA, seja em C ou em Fortran, não se limitam


a apenas o uso de uma GPU. Pode-se, entretanto, utilizar mais de uma para o processamento
paralelo massivo de um algoritmo. A utilização de múltiplas GPUs pode ser realizada através
de diversas técnicas de comunicação, citando-se por exemplo: peer-to-peer, OpenMP e MPI.

A comunicação peer-to-peer ocorre sempre que uma GPU necessita de informações que estão
contidas em uma outra. Logo, para que uma acesse a informação da outra, é necessário
empregar um mecanismo de comunicação que faça a conexão entre as memórias de ambas, e
uma vez feito tal procedimento, a transmissão de dados entre GPUs pode ser realizada tal
como:

a1_d = a0_d

Onde:

a1_d: variável que receberá as informações advindas da GPU tida como 0;

a0_d: variável com as informações necessárias para o funcionamento da GPU tida como 1.

Como coloca Ruestch e Fatica (2014), na versão CUDA 4.0 foi introduzido um mecanismo
chamado de Unified Virtual Addressing (UVA), que é responsável por agrupar as memórias
do host e das múltiplas GPUs de um sistema em uma única memória virtual. Sendo assim, o
acesso de informações nesse sistema virtual formado, facilita o trânsito de dados entre os

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


36

dispositivos, uma vez que se pode fazer acessos diretamente aos dados (direct acess) ou
também por transferências diretas (direct transfers) entre GPUs. Para melhor compreender-se
esse conceito, abaixo segue demonstrado na Figura 2.17, como são feitos ambos os
procedimentos de troca de informações.

(a) direct transfer (a) direct acess

Figura 2.17 – Trânsito de informações no ambiente UVA (RUESTCH;


FATICA, 2014)

Para problemas de maior escala, a comunicação peer-to-peer pode apresentar limitações. De


modo a contornar problemas na performance numérica devido a tal situação, pode-se utilizar
mecanismos de paralelização do tipo MPI.

Ao utilizar a abordagem MPI, será necessário, a criação de nós de processamento, sendo


possível contar com o ambiente UVA, disponibilizado a partir da versão CUDA 4.0. Ainda
sobre a paralelização contando com múltiplas GPUs, tem-se que a utilização pode ser
realizada também no contexto de diretrizes em OpenMP para a paralelização de tarefas.

O uso de MPI para a implementação do uso de múltiplas GPUs, passa, inicialmente, pela
geração de nós (ranks), que podem ser entendidos como threads a serem executados. Sendo
assim, pode-se através dos diferentes nós gerados, dividir a carga de trabalho por GPU e
assim aumentar o nível de paralelismo de execução do algoritmo numérico. Observando
alguns trabalhos utilizando o uso de MPI e CUDA, verifica-se que a performance de execução
pode atingir patamares de 2x a 3x dos algoritmos em relação ao uso de apenas uma GPU,
como por exemplo é mostrado nos trabalhos de Thibault e Senocak (2012) e Senocak et al.
(2009), maiores detalhes sobre estes estudos serão discutidos mais à frente no texto.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
37

2.2.7 Funções atômicas

Os compilares desenvolvidos para o uso da arquitetura CUDA da Nvidia permitem o uso de


funções atômicas, cujo propósito é de realizar uma tarefa de forma independente das demais
realizadas ao longo do código, de modo que não ocorra em hipótese alguma a reescrita de
uma dada variável por uma tarefa diferente da especificada no lançamento de um kernel.
Outro ponto, interessante sobre o uso de funções atômicas está relacionada a execução de
tarefas simples tais como a adição, subtração, multiplicação, e etc, de uma dada variável ou
array, sendo uma alternativa para evitar loops dentro de uma rotina do device. No manual
fornecido pela PGI Compiler & Tools (2017), encontra-se disponíveis as seguintes funções
atômicas:

Quadro 3 – Funções atômicas e suas instruções (PGI Compiler &


Tools, 2017)

A partir das funções apresentadas no Quadro 3, pode-se fazer uso de funções atômicas ao
longo do código do device. Contudo, deve-se ficar atento com o uso excessivo dessas funções,
uma vez que o desempenho do código numérico pode ser afetado substancialmente a medida
que elas sejam empregadas em demasia.

2.3 APLICAÇÕES NA LITERATURA

Após o advento da arquitetura CUDA e a criação de mecanismos que possibilitaram utilizar


linguagens tais como C ou Fortran, pesquisadores de diversos campos começaram a mostrar
interesse no uso do poder de computação paralela possibilitada pelas GPUs. Neste contexto,
encontra-se na literatura contemporânea, diversos estudos que mostram os benefícios trazidos

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


38

pela GPUs em simulações computacionais. Sendo assim, neste item serão demonstrados
alguns trabalhos cujo objetivo era elucidar o comparativo de desempenho entre o uso de
CPUs e GPUs para simulações numéricas, particularizando para problemas relacionados à
Dinâmica dos Fluidos e Engenharia do Vento.

2.3.1 Equações de Navier-Stokes para fluidos incompressíveis

Em Thibault e Senocak (2012) é encontrado um estudo que busca identificar a potencialidade


do uso de GPUs para a simulação de problemas pertinentes à Dinâmica de Fluidos,
particularizando para o caso de escoamentos incompressíveis. No referido trabalho também
são feitas comparações entre o uso de GPUs frente à de CPUs (códigos serial e paralelizado).

Dentre os principais pontos levantados por Thibault e Senocak (2012), tem-se que para a
implementação de um algoritmo numérico empregando a arquitetura CUDA, o programador
deve ter total conhecimento do gerenciamento do trânsito de informações, não somente entre
host e device, mas como também entre as diferentes memórias que o device dispõe. E neste
ponto, destaca-se os resultados obtidos pelos autores citados, no tocante a performance entre o
uso da memória global frente à memória compartilhada (shared) da GPU. Abaixo, segue a
Figura 2.18, em que são apontados os resultados obtidos pelos autores quanto ao tipo de
memória empregada durante a execução de kernels em seu código numérico para a solução
das equações de Navier-Stokes para fluidos incompressíveis.

Figura 2.18 – Comparação entre o uso de memória global e


compartilhada para diferentes kernels do algoritmo computacional de
Thibault e Senocak (2012)

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
39

A análise realizada por Thibault e Senocak (2012), cujos resultados são apresentados na
Figura 2.17, buscou-se determinar a performance relativa (speedup) entre o uso da memória
global e compartilhada, tomando como referência os resultados obtidos para o uso da
memória global. Analisando os dados apresentados, verifica-se que para dois dos três kernels
do código numérico dos autores, o uso de memória compartilhada culminou em uma melhor
performance, haja vista que ambas resultaram em speedups superiores a 1,0. Contudo, em um
dos kernels não se verificou melhora pela utilização de memória compartilhada, o que indica
que possivelmente, nesta parte do código, o trânsito de informações entre a memória global-
compartilhada-global demandou mais tempo do que apenas os cálculos a serem realizados
pela GPU. Sendo assim, pode-se dizer que o uso de memória compartilhada deve ser
investigado caso a caso, uma vez que pode afetar significativamente a performance do código
numérico. No final da investigação sobre os tipos de memória, Thibault e Senocak (2012)
apontam que o uso misto de tipos de memória entrega uma melhor performance no algoritmo
computacional, porém, a complexidade de programação é ampliada neste tipo de abordagem,
devendo o programador avaliar se deverá utilizar um código com maior ou menor
performance, dada a dificuldade de programação que irá se ter ao elaborar tal algoritmo.

Para avaliar a performance de processamento de GPUs, Thibault e Senocak (2012) analisaram


o problema do escoamento em uma cavidade, onde simularam o mesmo com as seguintes
configurações e especificações:

Quadro 4 – Especificações do hardware empregado Thibault e


Senocak (2012) para a simulação do problema da cavidade

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


40

Com o hardware descrito no Quadro 4, os pesquisadores simularam o problema da cavidade


empregando diferentes arranjos de equipamentos. Os resultados em termos de tempo obtidos
para cada arranjo foram comparados com valor de referência estabelecido como sendo o
tempo de simulação computacional para o Intel Core 2 Duo. Abaixo na Figura 2.19, são
descritos em termos de speedup, os resultados encontrados pelos pesquisadores para cálculos
empregando precisão simples.

Figura 2.19 – Speedup para diferentes arranjos de hardware para a


simulação do problema da cavidade, em precisão simples
(THIBAULT; SENOCAK, 2012)

Analisando os resultados para o speedup no problema da cavidade, pode-se notar que o uso de
GPUs para o processamento computacional do código elaborado pelos autores foi, no
mínimo, 10,8x mais rápido do que o utilizando um único CPU. Além disto, tem-se que o uso
de múltiplas de GPUs é capaz de aumentar substancialmente a velocidade de processamento
do problema. Sendo assim, é interessante utilizar, quando possível, mais de uma GPU para
estudar problemas mais complexos e com maior nível de discretização, obtendo as respostas
mais rapidamente. Acrescenta-se a análise de resultados, que em relação ao melhor speedup
entre os CPUs empregados frente ao melhor obtido pela GPUs, nota-se uma diferença de
12,2x. Portanto, o uso de múltiplos núcleos de processamento em um CPU, para o hardware
utilizado pelos pesquisadores, não se mostra suficientemente eficaz para simulação de
problemas de Dinâmica dos Fluidos, sendo preferível, a utilização de GPUs.

Para elucidar a diferença em termos de processamento entre o uso de precisão simples e dupla
em GPUs, os pesquisadores simularam o problema da cavidade utilizando variáveis em ambos
__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
41

os tipos de precisão. Os resultados determinados por eles indicaram que o cálculo para
precisão dupla é aproximadamente 2,5x mais lento do que o empregando precisão simples.
Logo, o programador deve pesar se é benéfico, ou não, a declaração de variáveis utilizando
precisão dupla para a simulação do problema almejado. Abaixo, na Figura 2.20, são
apresentados os resultados obtidos pelos pesquisadores.

Figura 2.20 – Speedup para diferentes tipos de precisão para a


simulação do problema da cavidade (THIBAULT; SENOCAK, 2012)

Salienta-se que a referência para o cálculo do speedup foi estabelecida como sendo a
performance alcançada pelo Intel Core 2 Duo.

2.3.2 Simulação problemas de Dinâmica dos Fluidos

No trabalho realizado por Couto (2016) foi elaborado um algoritmo computacional, baseado
em linguagem C, voltado para a aplicação da arquitetura CUDA, para a solução de problemas
de Dinâmica dos Fluidos. No referido trabalho, analisou-se problemas típicos da área e que
seguem descritos abaixo:

a) Escoamento em cavidade;

b) Escoamento em torno de um cilindro;

Para a simulação computacional dos problemas listados acima, o autor utilizou os seguintes
equipamentos:

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


42

 CPU: Intel Core i5 3.2 GHz

 Memória RAM: 16 GB

 Placa gráfica: Nvidia GeForce GTX 750 – 512 CUDA cores


Capacidade computacional: 5.0;
Poder de processamento: em PS → 1111,0 GFLOPS; em PD → 34.7 GFLOPS

No trabalho de Couto (2016), comparou-se os tempos de realização de algumas atividades no


código entre a forma sequencial para CPU e a paralela por GPU. Contudo, nas avaliações
realizadas pelo autor não foram inclusos os tempos despendidos para o envio de informações
do host para o device.

2.3.2.1 Escoamento em cavidade

Para este problema, o autor elaborou diversas malhas em elementos finitos, com as mesmas
apresentando de 98 a 40920 elementos. Desta forma, ele buscou determinar o tempo de
processamento do problema da cavidade em função do tamanho da malha.

Após a simulação numérica, o autor avaliou os tempos relativos ao tratamento das matrizes
fundamentais do modelo numérico e o de simulação total. Os resultados obtidos pelo autor
seguem apresentados na Figura 2.21.

(a) Tratamento das matrizes (b) Tempo total de simulação

Figura 2.21 – Speedup para o problema da cavidade em relação ao


tratamento das matrizes globais e tempo de simulação total (COUTO,
2016)

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
43

Conforme ilustrado na Figura 2.21, verifica-se que para as menores malhas, o desempenho
entre CPU e GPU ficam em patamares próximos. Contudo, à medida que a malha em
Elementos Finitos é refinada, percebe-se que a GPU começa a apresentar desempenhos muito
superiores em relação aos do código na versão serial de CPU. Tal resultado entra em
consonância com os apresentados em Thibault e Senocak (2012) que apontam que o
processamento paralelo em GPU só é interessante quando há uma grande demanda de
atividades em paralelo, para que assim se consiga contrabalancear o tempo de transmissão das
informações do host para o device. Ainda sobre os resultados de Couto (2016), verifica-se
speedups da ordem de 4x em relação ao código serial, mostrando assim a potencialidade do
uso de GPU para o tratamento de problemas de Dinâmica dos Fluidos. Por fim, os resultados
encontrados pelo autor, tanto em seu código serial quanto paralelo mostraram grande
concordância com os obtidos por Ghia et al. (1982) para o problema da cavidade com o
número de Reynolds (Re) igual a 400.

2.3.2.2 Escoamento em torno de um cilindro

Assim como no problema anterior, o autor buscou determinar o tempo de processamento do


problema do escoamento em torno de um cilindro para diferentes tamanhos de malha,
utilizando como parâmetros de comparação os tempos de tratamento das principais matrizes
do código numérico e o de simulação computacional. As malhas elaboradas pelo autor
tiveram tamanhos variam de 100 a 41612 elementos

Finalizada a simulação numérica, o autor obteve os resultados apontados na Figura 2.22, para
os tempos relativos ao tratamento das matrizes fundamentais do modelo numérico e de
simulação total. Os resultados obtidos, pertinentes ao problema de Dinâmica dos Fluidos,
foram comparados com os obtidos por Gomes (2013) e Shaffer et al. (1996), indicando boa
concordância.

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


44

(a) Tratamento das matrizes (b) Tempo total de simulação

Figura 2.22 – Speedup para o problema de escoamento em torno de


um cilindro para o tratamento das matrizes globais e tempo de
simulação total (COUTO, 2016)

Verifica-se, através da Figura 2.22, que o speedup para o código numérico paralelizado via
GPU atingiu patamares de 10x e 4x, respectivamente, para o tratamento das matrizes
principais e tempo total de simulação. Sendo assim, percebe-se que a medida que
refinamentos fossem necessários na malha de elementos finitos, o código paralelo forneceria a
possibilidade de maior discretização e entrega de resultados em menor tempo do que o código
sequencial sem refinamentos até uma razão aproximada de 4x. Conclui-se, portanto, que para
problemas que necessitem de altos números de Reynolds, consequentemente, maiores
refinamentos, os resultados obtidos por Couto (2016) sugeririam o uso de códigos paralelos
utilizando os recursos de processamento de GPUs.

2.3.3 Simulação de escoamentos compressíveis

Phillips et al. (2009), visando avaliar a potencialidade do uso de GPUs via arquitetura CUDA,
desenvolveram um código computacional capaz de resolver as equações de Euler para fluidos
compressíveis. No trabalho desenvolvido pelos autores, foi simulado o escoamento de um
fluido compressível através de um estreitamento, tal como mostrado na Figura 2.23, que
segue abaixo.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
45

Figura 2.23 – Malha do problema de estreitamento de Phillips et al


(2009) e resultados para os campos de pressão

Para o mesmo problema apontado na Figura 2.23, os autores utilizaram tanto CPUs quanto
GPUs, buscando compreender as diferenças na performance entre as distintas formas de
processamento empregadas. O critério estabelecido como meio de medida foi o número total
de pontos por malha lidas pelo tempo de uma única interação do código desenvolvido. Os
resultados obtidos por Phillips et al. (2009) podem ser vistos nas Figura 2.24, que aponta os
resultados para os casos de CPU/GPU únicos e clusters, respectivamente.

(a) CPU/GPU únicos

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


46

(b) Cluster de GPUs

Figura 2.24 – Resultados dos testes de performance do trabalho de


Phillips et al (2009)

Conforme ilustrado na Figura 2.24a, nota-se que para o caso de simulação empregando apenas
CPU ou GPU únicos, o desempenho no dimensionamento variou em função do tamanho da
malha empregada para a solução do problema. Nesse sentido, pode-se ver que para a menor
malha investigada por Phillips et al. (2009), que quando a demanda computacional por
processamento é baixa, o tempo de transferência de dados é demasiadamente alto em relação
ao de processamento, fazendo com que o speedup seja pequeno em relação ao das demais
malhas empregadas, ficando este na faixa de 3,5x em relação aos dos CPUs. No entanto, à
medida que a demanda de processamento se torna alta, compensando assim o fluxo de dados,
o desempenho ofertado pela GPU torna-se muito mais vantajoso, podendo oferecer speedups
da ordem de 20x em relação ao dos CPUs. Já na Figura 2.24b, ao utilizar múltiplas GPUs, os
autores verificaram ser possível atingir um patamar de speedup da ordem de 496x em relação
ao uso de apenas um núcleo do CPU Core 2 Duo, mostrando assim a potencialidade da GPU
para o processamento paralelo de dados.

Observando-se os resultados apresentados pelos autores, é possível verificar que o uso de


GPUs está condicionado a uma alta demanda por processamento, uma vez que para os
problemas avaliados, viu-se que para malhas pequenas, a performance para CPUs fica

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
47

relativamente próxima ao das GPU, e à medida que a discretização da malha em Elementos


Finitos tornou-se maior, o desempenho da GPU se sobressaiu ao do CPU. Sendo assim,
conforme colocam Phillips et al. (2009), “[...] Códigos computacionais de CFD e de outras
áreas da ciência utilizados na prática poderiam ser muitos beneficiados pela melhora de
performance proporcionada pelos processadores das GPUs.”.

2.3.4 Simulação de escoamentos em áreas urbanas

Na literatura contemporânea, encontra-se o trabalho de Senocak et al. (2009), que trata da


simulação computacional tridimensional de escoamentos. No presente trabalho, os autores
apontam que o advento de linguagens, que permitiram a incorporação de GPUs no
processamento paralelo de códigos numéricos, possibilitaram que a simulação de dispersão de
gases, de massa e de escoamentos em áreas urbanas seja realizada em tempos razoavelmente
curtos de processamento. Os presentes autores ainda indicam que na ocorrência de desastres
ou em ataques químicos, pode-se avaliar, através de clusters de GPUs, os impactos
decorrentes de tais eventos em tempos hábeis, de forma a propiciar tomadas de decisões mais
imediatas e assim, remediar as consequências. Nesse sentido, com o viés de demonstrar a
potencialidade do uso de GPU para tais situações, os autores confeccionaram uma malha
tridimensional representando parte da cidade de Oklahoma/USA. Dessa forma, a partir da
malha elaborada pôde-se comparar o tempo de simulação numérica empregando unicamente
CPUs frente ao obtido via uso exclusivo de GPUs, através da arquitetura CUDA. Na Figura
2.25 é demonstrada a malha em elaborada por Senocak et al. (2009)

Figura 2.25 – Malha tridimensional elaborada por Senocak et al.


(2009) representando parte da cidade de Oklahoma

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


48

Os resultados obtidos pelos autores demonstraram que o speedup obtido, utilizando placas
gráficas, para o processamento paralelo pode atingir patamares de 100x quando comparados
com o mesmo problema simulado por apenas um CPU. Nesse sentido, percebe-se que o uso
de GPUs possibilitaria em uma situação de evento extremo, uma resposta em um tempo 100x
menor do que via processamento via CPU. Logo, nota-se que em problemas, cuja natureza
implica em uma alta demanda processamento paralelo, os mesmos passarão a ser
confeccionados utilizando linguagens e diretrizes que permitam o uso de placas gráficas, dada
a performance de processamento que se atingir. Abaixo seguem, na Figura 2.26, os resultados
referentes ao speedup obtido pelos autores.

Figura 2.26 – Resultados do speedup obtido pelo uso de múltiplas


placas gráficas na simulação tridimensional do escoamento em
ambiente urbano (SENOCAK et al., 2009)

2.3.5 Direct Numerical Simulation

Desde as primeiras tentativas de simulação computacional acerca de problemas da Dinâmica


dos Fluidos, os pesquisadores depararam-se com problemas de discretização exigidos para o
uso direto das equações de Navier-Stokes, que demandam a representação de todas as escalas
de vórtices presentes em um escoamento. Em busca de contornar então a dificuldade inerente
ao nível de discretização demandado para tal tarefa, surgiram os modelos de modelagem de
turbulência, cujo emprego possibilitou a simulação, em tempos hábeis, de diversos problemas
de interesse da Dinâmica dos Fluidos e da Engenharia do Vento. Contudo, os modelos de
turbulência somente são capazes de aproximar os efeitos decorrentes das pequenas escalas dos
vórtices, não sendo suficientemente capazes de retratar o escoamento em sua plenitude de

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
49

efeitos. Sendo assim, sempre foi de interesse da Engenharia do Vento desenvolver simulações
numéricas diretas, empregando diretamente às equações de Navier-Stokes.

Atualmente, através do uso do poder de processamento das GPUs, a simulação direta


computacional está começando a se tornar uma realidade até para altos Re, uma vez que o uso
de clusters de GPUs está tornando-se cada vez mais acessível e comum para os centros de
pesquisa destinados a esta área da ciência. Sendo possível a cada nova geração de
equipamentos, obter-se respostas mais rápido e com maior nível de precisão. E nesse ponto,
cabe destacar o trabalho realizado por Oyarzun et al. (2013), em que os autores buscaram,
através de clusters com CPUs e GPUs, averiguar a performance relativa entre ambos para a
simulação direta numérica (DNS). Para isto, eles desenvolveram códigos utilizando a
linguagem MPI para a transferência de carga de trabalho entre os núcleos de processamento
de seu cluster, que contava com 2 CPUs Intel Xeon E560 e 2 GPUs M2090 por nó de
processamento.

Para avaliar o desempenho do código numérico da simulação direta, os autores escolheram


um trecho crítico do código responsável pela solução da equação de Poisson. A comparação
se baseou apenas em avaliar as diferenças entre a performance do uso de uma CPU e frente ao
de uma GPU para o cálculo da parte do código escolhida pelos autores. Os resultados
pertinentes à está análise seguem ilustrados abaixo, na Figura 2.27.

Figura 2.27 – Speedup em DNS comparando a performance de CPU


versus GPU (OYARZUN et al., 2013)

Analisando a Figura 2.27, onde são mostrados os resultados para o speedup para as diferentes
malhas utilizadas pelos autores, variando de 100.000 a 400.000 nós. Na figura, vê-se que o
menor speedup obtido no estudo realizado pelos autores foi superior a 6x para a GPU,

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


50

podendo o speedup máximo alcançar patamares superiores a 12x para um nível maior de
discretização. Sendo assim, nota-se que mesmo para a simulação computacional de problemas
de escoamento via DNS, as placas gráficas são capazes de permitir uma grande melhora na
performance de cálculo e assim, reduzir os tempos associados ao processamento numérico do
problema, que conforme os resultados apresentados por Oyarzun et al. (2013) pode ser na
ordem de 6x menor.

Dada a determinação do speedup entre o uso de CPU e GPU, os pesquisadores buscaram


avaliar o desempenho do processamento do trecho do código responsável pelo cômputo da
equação de Poisson para múltiplas placas gráficas. Os resultados obtidos pelos autores,
demonstraram que não houve benefício significativo em utilizar mais de 2 GPUs, sendo
descrito pelos autores que o maior responsável pelo decréscimo no desempenho fora o acesso
à memória entre as diversas GPUs empregadas no estudo.

2.3.6 Solução das equações de N-S através de um algoritmo heterogêneo

Em Lai et al. (2017) foi realizado um estudo acerca do uso de um algoritmo numérico que
utiliza os recursos de processamento de GPU e CPU, através da arquitetura CUDA, para a
solução das equações de Navier-Stokes. No referido trabalho, empregou-se um esquema
heterogêneo para a distribuição de tarefas do código, tal como o apresentado na Figura 2.28.

Figura 2.28 – Divisão das cargas de trabalho para um algoritmo


heterogêneo para uso de CPUs e múltiplas GPUs (2017)

Para o trabalho, os autores utilizaram um CPU Intel Xeon 2670 e uma GPU Nvidia 1070
como hardware e estudaram problemas clássicos da Dinâmica dos Fluidos, voltada para a área
da aeronáutica, tal como: elipsoide duplo, escoamento em torno de um jato e escoamento

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
51

supersônico. Os resultados determinados pelos autores mostram grande concordância com


testes experimentais e demonstraram, em relação ao código utilizando apenas CPU, que
houve um speedup da ordem de 20x a quase 50x para as diversas malhas em volumes finitos
confeccionadas para o estudo. Abaixo seguem os resultados de speedup em função do
tamanho da malha para os três problemas estudados por Lai et al. (2017).

(a) Elipsoide duplo (b) Escoamento em (c) Escoamento supersônico


torno de um jato

Figura 2.29 – Resultados do estudo de Lai et al. (2017)

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


52

3 ESTUDO DE APLICAÇÃO

Neste item serão apresentadas algumas comparações feitas entre o desempenho de diferentes
códigos e formas de paralelização dos mesmos. Para isto, serão simulados diferentes
algoritmos computacionais vislumbrando comparar o tempo de execução entre o uso de
apenas os núcleos lógicos do CPU e de núcleos de placas gráficas, utilizando recursos do
CUDA para paralelização do código. O hardware utilizado para a presente investigação é o
apresentado a seguir:

 CPU: Intel Core i7 6700K – 4 núcleos físicos / 8 núcleos lógicos

 Placa-mãe: Gigabyte Z170-D3H-CF

 Memória: Kinston 2x8GB DDR4

 Placa gráfica: Nvidia GeForce GTX 750 Ti – 640 CUDA cores


Capacidade computacional: 5.0;
Poder de processamento: em PS → 1388,8 GFLOPS; em PD → 43,4 GFLOPS

A seguir são apresentados os códigos computacionais utilizados e a comparação desenvolvida


entre os diferentes tipos de paralelização.

3.1 ENSAIO DE MODELO REDUZIDO EM TÚNEL DE VENTO

Este código numérico consiste em um algoritmo desenvolvido por Alminhana (2017) para a
análise de dados de ensaios experimentais realizados no túnel de vento de camada limite Prof.
Joaquim Blessmann. O modelo numérico é capaz de calcular os coeficientes de pressão nas
tomadas distribuídas nas fachadas do modelo e ainda determinar os coeficientes
aerodinâmicos locais e globais do mesmo.

3.1.1 Parâmetros de comparação

Inicialmente, tomou-se o código numérico em Fortran 90 em sua forma serial, isto é, sem
contar com rotinas de paralelização, e fez-se alterações no mesmo com o objetivo de habilitá-

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
53

lo a executar diretrizes OpenMP e CUDA. Desta forma, dividiu-se os códigos conforme


segue:

a) Serial: sem diretrizes com paralelização;

b) OpenMP: contando com paralelização utilizando apenas o CPU;

c) CUDA: contando com paralelização utilizando tanto CPU quanto GPU.

Além da divisão proposta, para medir o desempenho entre os diferentes tipos de paralelização
de tarefas, inseriu-se medidores de tempo em trechos críticos do código que demandam um
maior número de operações. Abaixo são apresentados os trechos utilizados para medir a
performance dos códigos.

TRECHO 1
Código Serial:

Código OpenMP:

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


54

Código CUDA:

Código CUDA – KLD:

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
55

TRECHO 2

Código Serial:

Código OpenMP:

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


56

Código CUDA:

Código CUDA – KLD:

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
57

TRECHO 3
Código Serial:

Código OpenMP:

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


58

Código CUDA:

Código CUDA – KLD:

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
59

3.1.2 Resultados da comparação

A partir dos trechos expostos no item 3.1, procedeu-se a comparação dos tempos relativos
para a execução dos mesmos. Os resultados obtidos seguem apresentados nas Tabelas 3.1 e
3.2.

Tabela 3.1 – Performance no código numérico do Túnel: precisão


simples
Serial (1) OpenMP (2) CUDA Módulos (3) CUDA KLD (4)
Simulação Tempo (ms) Tempo (ms) Tempo (ms) Tempo (ms)
1 2 3 Média 1 2 3 Média 1 2 3 Média 1 2 3 Média
Trecho 1 33.000 31.000 33.000 32.333 9.000 11.000 9.000 9.667 2.249 2.535 2.621 2.468 0.652 0.635 0.637 0.641
Trecho 2 5.999 5.999 5.999 5.999 2.000 3.000 2.000 2.333 4.766 4.597 4.536 4.633 2.232 2.236 2.236 2.235
Trecho 3 11.000 10.000 10.000 10.333 5.000 11.000 8.000 8.000 1.116 1.122 1.007 1.082 4.565 4.572 4.528 4.555

(fonte: elaborado pelo autor)

Tabela 3.2 – Performance no código numérico do Túnel: dupla


precisão
Serial (1) OpenMP (2) CUDA Módulos (3) CUDA KLD (4)
Simulação Tempo (ms) Tempo (ms) Tempo (ms) Tempo (ms)
1 2 3 Média 1 2 3 Média 1 2 3 Média 1 2 3 Média
Trecho 1 44.000 43.000 43.000 43.333 14.000 11.000 20.000 15.000 12.195 12.262 12.285 12.247 9.039 9.041 9.039 9.040
Trecho 2 5.999 5.999 6.000 5.999 4.999 2.000 8.000 5.000 6.147 5.982 5.926 6.018 3.229 5.014 3.232 3.825
Trecho 3 11.000 10.999 10.000 10.666 10.000 5.999 4.999 6.999 1.191 1.184 1.149 1.175 5.429 5.428 5.414 5.424

(fonte: elaborado pelo autor)

Analisando os resultados expostos nas Tabelas 3.1 e 3.2, observa-se que a questão da precisão
das variáveis é determinante para a performance do código em CUDA, uma vez que o tempo
de processamento chega a ser aumentado em 4,96x para o Trecho 1 utilizando kernels em
módulo e 14,1x utilizando KLD, já para os demais o aumento no tempo ficou na margem de
8.59% até 71.1%. Comparando os tempos entre os diferentes tipos de paralelização, tem-se
que para ambos os tipos de precisão, o código em CUDA conseguiu desempenhos bem
superiores para os trechos 1 e 3 do código, já para o segundo trecho, tem-se que a
paralelização via KLD foi a com melhor performance, sendo seguida pela em OpenMP. De
forma resumida, a Tabela 3.3 e a Figura 3.1 ilustram a performance relativa entre os
diferentes tipos de paralelização, tomando como referência os resultados obtidos na versão
serial do código.

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


60

Tabela 3.3 – Performance relativa (speedup) entre os códigos


numéricos do Túnel
Speedup
Precisão Simples Precisão Dupla
Simulação Serial OpenMP CUDA (3) CUDA (4) Serial OpenMP CUDA (3) CUDA (4)
(1)/(1) (1)/(2) (1)/(3) (1)/(4) (1)/(1) (1)/(2) (1)/(3) (1)/(4)
Trecho 1 1.0 3.3 13.1 50.4 1.0 2.9 3.5 4.8
Trecho 2 1.0 2.6 1.3 2.7 1.0 1.2 1.0 1.6
Trecho 3 1.0 1.3 9.6 2.3 1.0 1.5 9.1 2.0

(fonte: elaborado pelo autor)

Figura 3.1 – Tempos de execução e speedups para o programa de


tratamento de dados do túnel de vento

Ao final deste estudo, conclui-se que o uso de paralelização utilizando GPUs é capaz de
ocasionar melhoras significativas na performance de códigos numéricos, principalmente em
trechos que demandam uma quantidade significativa de cálculos, tal como no caso do Trecho
1. Além disso, assim como comentado anteriormente, o tipo de precisão das variáveis afeta
diretamente a velocidade de execução dos kernels, onde destaca-se aqui o speedup obtido para
a rotina computacional realizada via KLD para o Trecho 1.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
61

3.2 ESCOAMENTO DE FLUIDOS VISCOSOS

Neste item será apresentado os resultados de simulações numéricas de problemas clássicos da


Dinâmica dos Fluidos utilizando um algoritmo numérico apresentado por Braun (2007),
adaptado pelo presente autor para o uso de GPUs via arquitetura CUDA, utilizando kernels
em formato de KLDs para a paralelização do código, visto a performance obtida para o
código de análise de resultados do ensaio em túnel de vento.

A partir da modificação do algoritmo, que em sua forma original, permitia apenas o uso de
CPUs, seja na resolução de problemas via programa serial ou paralelizada em OpenMP, pode-
se medir o speedup entre uso de GPUs e CPUs. Para isto, utilizou-se a configuração descrita
no início do capítulo 3 e simulou-se numericamente os seguintes problemas: escoamento em
cavidade bidimensional e sobre degrau bidimensional. Ressalta-se que o código de fluidos em
CUDA não será apresentado no presente trabalho devido a extensão do mesmo.

3.2.1 Escoamento em cavidade bidimensional

A simulação do problema da Dinâmica dos Fluidos de escoamento em uma cavidade


bidimensional foi realizada tomando-se como referência a malha em Elementos Finitos e
parâmetros propostos em Petry (2002). Na Figura 3.2 são apresentadas a malha em Elementos
Finitos confeccionada e os principais parâmetros utilizados para a simulação do escoamento
do problema em questão, para maiores detalhes ver Petry (2002).

Figura 3.2 – Aspecto da malha proposta por Petry (2002) e condições


de contorno utilizadas na simulação numérica do problema de
escoamento em uma cavidade

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


62

Para testar a performance do uso do CUDA no código numérico proposto por Braun (2007),
simulou-se o problema da cavidade em três situações de simulação, sendo a primeira a versão
serial do programa, a segunda sendo uma versão OpenMP do mesmo, e por fim, tem-se a
versão que conta com KLDs, que possibilitaram a simulação numérica através do uso de
placas gráficas. Ainda se tem que à análise, em termos de performance, foi realizada a partir
dos resultados obtidos em 5 tomadas de dados realizadas para cada tipo de forma de
simulação computacional, tomando-se como referência os obtidos para a versão Serial do
código de fluidos. Um ponto que se destaca na análise realizada é quanto à precisão das
variáveis, que foram declaradas como sendo de precisão dupla. Na Tabela 3.4, que segue
abaixo, são apresentados os resultados obtido na simulação do problema de escoamento em
uma cavidade para os três códigos numérico utilizados.

Tempo (s) Speedup
Tomada
Serial OpenMP CUDA‐KLD Serial OpenMP CUDA‐KLD
1 1939.4 1284.1 2192.1 1.00 1.51 0.88
2 1893.1 1501.0 2143.9 1.00 1.29 0.90
3 1893.1 1297.2 2231.5 1.00 1.50 0.87
4 1891.2 1247.0 2218.8 1.00 1.56 0.87
5 1891.0 1345.4 2144.3 1.00 1.44 0.90
Média 1901.6 1334.9 2186.1 1.00 1.46 0.89

Tabela 3.4 – Tempos de simulação e speedups do problema de


escoamento em uma cavidade para 5 tomadas de dados.

Analisando-se os resultados obtidos para este problema, verifica-se que a versão serial do
código foi capaz de entregar uma melhor performance na capacidade de processamento do
que a obtida utilizando CUDA, com laços escritos em KLD. Em termos de speedups para a
simulação do problema da cavidade, vê-se que o código utilizando CUDA-KLD ficou abaixo
de 1,0, exibindo um valor médio de 0,89. A versão do código em OpenMP foi a que
apresentou melhor performance, visto que os tempos de simulação foram os menores entre
todas as tomadas de dados, resultando ao final, em um speedup médio de 1,46.

Os resultados encontrados neste estudo, no que concerne ao uso de GPUs na simulação


computacional, estão vinculados ao uso da memória da placa gráfica que fora ineficiente, a
medida que se contrastam diretamente com os determinados no outro código numérico
investigado no presente seminário. O presente autor, verificou que o desempenho da
simulação via CUDA foi prejudicado majoritariamente pela transferência de dados entre host

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
63

e device, e isto se dá em decorrência da escrita do código proposto por Braun (2007) e


também em função da abordagem de paralelização via KLD, que pelos resultados obtidos
indicasse mais recomendada para laços simples no código. Conforme apontando ao longo do
item 2.2.3, o maior problema de performance precária no CUDA está atrelado à troca
excessiva de dados entre host e device. Logo, a escrita do código em CUDA usando KLD
para o código de fluidos utilizado neste trabalho deve ser substituído por kernels escritos em
formato de módulos, que possibilitam o emprego de memória do tipo shared (possui menor
latência e maior velocidade de acesso) e possibilitam ainda a reescrita do código, de modo a
evitar trocas excessivas de informação entre host e device.

De modo a avaliar a qualidade dos resultados obtidos na simulação do problema de


escoamento em uma cavidade, são apresentados abaixo os campos de velocidade e pressão na
cavidade estudada nas Figuras 3.3 a 3.5.

Serial OpenMP CUDA

Figura 3.3 – Campos médios para a componente da velocidade ao


longo do eixo x para o problema de escoamento em uma cavidade.

Conforme observa-se na Figura 3.3, tem-se que de forma qualitativa os campos de velocidade,
para a componente ao longo do eixo x, da cavidade apresentam resultados semelhantes entre
as versões Serial e CUDA do código de fluidos utilizado na simulação computacional. Já os
resultados obtidos na versão em OpenMP se mostram aquém daqueles obtidos nas demais
versões do código de fluidos. E aqui ressalta-se que todas as simulações foram realizadas
utilizando os mesmos parâmetros de inicialização do programa e no fluido empregado.

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


64

Serial OpenMP CUDA

Figura 3.4 – Campos médios para a componente da velocidade ao


longo do eixo y para o problema de escoamento em uma cavidade.

Estendendo a análise qualitativa dos campos de velocidade para a componente ao longo do


eixo y e também para o campo de pressões, conforme ilustrado nas Figura 3.4 e 3.5, verifica-
se novamente uma aproximação entre os campos para as versões Serial e CUDA, ao passo
que a em OpenMP mostra diferenças significativas.

Serial OpenMP CUDA

Figura 3.5 – Campos médios de pressão para o problema de


escoamento em uma cavidade.

Dadas as diferenças observadas nos resultados obtidos entre diferentes versões do código
numérico, comparou-se os mesmos com os apontados por Ghia et al. (1982), uma referência
largamente utilizada para verificar a qualidade dos resultados de códigos computacionais de
simulação de escoamento de fluidos. A comparação realizada é apontada abaixo, na Figura
3.6.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
65

a) Componente horizontal da velocidade ao longo da b) Componente vertical da velocidade ao longo da


altura da cavidade cavidade

Figura 3.6 – Comparação entre os dados fornecidos por Ghia et al.


(1982) e os obtidos pelas diferentes versões do código de fluidos.

Observando-se os resultados obtidos nas simulações, nota-se que em relação aos dados
apontados por Ghia et al. (1982), as versões Serial e CUDA do código de fluidos apresentam
valores próximos ao do autor tomado como referência, porém verifica-se diferenças nos
resultados, o que pode ser fruto de uma interrupção prematura da simulação numérica que
poderia ter sido realizada por um período maior de tempo. Destaca-se ainda que a versão em
OpenMP conforme os resultados ilustrados na Figura 3.5, ainda não está próxima de atingir a
convergência numérica para os campos de velocidade dentro da cavidade, dado os parâmetros
utilizados na simulação numérica, o que contrasta com os resultados apresentados para as
outras versões do código computacional, que se mostram mais próximos dos apresentados por
Ghia et al. (1982). Neste ponto, destaca-se que apesar de a versão OpenMP ter sido a com
melhor performance computacional, a mesma apresenta resultados aquém das demais, o que
sugere que nesta versão do código pode haver problemas na estruturação das diretrizes de
paralelização.

3.2.2 Degrau

De modo a obter um melhor panorama da performance do código de fluidos proposto por


Braun (2007) em diferentes abordagens de paralelização, simulou-se além do problema de
escoamento em uma cavidade, o sobre um degrau. Para isto, tomou-se como referência a
malha proposta por Petry (2002) para este problema, sendo apresentado na Figura 3.7 os

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


66

principais parâmetros relativos a malha em Elementos Finitos e aos vinculados às condições


de contorno a serem empregados no modelo numérico.

Figura 3.7 – Malha proposta por Petry (2002) para o problema do


escoamento sobre um degrau

Além dos parâmetros apontados na Figura 3.7, utilizou-se propriedades do fluido que
resultassem em um número de Reynolds (Re) de 400.

Com os parâmetros ajustados para Re=400, simulou-se o problema do degrau para as versões
Serial, OpenMP e CUDA. Os tempos de processamento e speedups obtidos seguem
apresentados na Tabela 3.5. Ressalta-se que para este problema, devido ao tamanho da malha
em Elementos Finitos, fez-se apenas uma tomada de dados.

Tempo (h) Speedup
Tomada
Serial OpenMP CUDA‐KLD Serial OpenMP CUDA‐KLD
1 37.7 27.0 39.4 1.00 1.40 0.96

Tabela 3.5 – Tempos de simulação e speedups do problema de


escoamento sobre um degrau bidimensional.

Conforme apontam os dados apresentados na Tabela 3.5, vê-se que novamente a simulação
em OpenMP fora a que foi concluída em menor tempo, apresentando assim, o maior speedup
entre as diferentes versões do código de fluidos. Após, verifica-se que a versão Serial se
mostrou mais veloz no processamento do problema, finalizando a simulação em 37,7h. Nesse
sentido, conclui-se que a proposta em utilizar kernels com a estrutura de KLD acabou por
gerar um código numérico pouco eficiente em capacidade de processamento para a versão
CUDA, tal como também fora observado no escoamento em uma cavidade. Sendo assim,
nota-se que para que ocorra melhora na performance de processamento do código em CUDA

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
67

é necessário reescrever o código eliminando trocas de dados ao máximo e ainda assim, utilizar
kernels em arranjo de módulos, que possibilitam uma melhor administração da memória da
placa utilizada no processamento computacional das diretrizes contidas no código de fluidos.

Com o propósito de analisar a qualidade dos resultados obtidos na simulação do problema de


escoamento em um degrau, para as diferentes versões de código, são apresentados abaixo, nas
Figuras 3.8 a 3.10, os campos de velocidade do fluido, para a componente x.

Figura 3.8 – Campo de velocidades, componente ao longo do eixo x,


para o problema do escoamento sobre um degrau – Código Serial

Figura 3.9 – Campo de velocidades, componente ao longo do eixo x,


para o problema do escoamento sobre um degrau – Código OpenMP

Figura 3.10 – Campo de velocidades, componente ao longo do eixo x,


para o problema do escoamento sobre um degrau – Código CUDA

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


68

Com os resultados obtidos, comparou-se o comprimento de recolamento obtido para cada


simulação com o fornecido por Armaly et al (1983). Segundo os autores, para Re de 400, o
comprimento de recolamento deve ser de 8,0, quando se tem um degrau com altura de 1m.
Sendo assim, analisando-se os resultados apontados nas Figuras 3.8 a 3.10, conclui-se que
todas as versões do código de fluidos foram capazes de simular corretamente o campo de
velocidades do escoamento sobre um degrau, visto que o comprimento de recolamento de
ficou próximo ao apontado por dados experimentais.

Na Figura 3.11, são apresentados os campos de pressão obtidos para cada versão do código de
fluidos utilizado na simulação numérica, que demonstraram resultados similares, indicando
assim que o problema atingiu seu estado permanente.

Figura 3.11 – Campo de pressões para as diferentes versões do código


de fluidos

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
69

4 CONCLUSÕES

No presente seminário realizou-se uma revisão bibliográfica acerca do uso de paralelização


utilizando placas gráficas, buscando explorar as peculiaridades e características da arquitetura
CUDA. Ao longo do texto, identificou-se os principais pontos a serem observados na
elaboração de um código numérico utilizando CUDA C ou CUDA Fortran. Também foi fruto
da pesquisa empreendida, a identificação de trabalhos que utilizaram a linguagem CUDA para
a criação de algoritmos numéricos destinados à resolução de problemas de Dinâmica dos
Fluidos e Engenharia do Vento. E na etapa final do seminário, analisou-se, diferentes códigos
numéricos modificados pelo presente autor, de modo a avaliar a performance computacional
dos mesmos, em versões seriais e paralelas contando o uso de CPUs ou CPU+GPU.

Destaca-se que o principal desafio na elaboração de um algoritmo computacional contando


com diretrizes em CUDA está no tratamento dos seguintes pontos: esquema de declaração de
variáveis, forma de acesso na memória da GPU e o emprego de múltiplas GPUs.
Primeiramente, a declaração das variáveis pode ser um gargalo para o processamento do
código, uma vez que o device conta com diversos tipos de memória e cada um deles com uma
capacidade de bandwidth diferente. Nesse contexto, o programador deve avaliar o ônus e
bônus de se utilizar um dado tipo de memória para a declaração das variáveis do código.
Além disso, tem-se que o acesso da memória pode resultar em uma baixa performance,
citando-se o caso de acesso não sequencial nos threads blocks. Para estes casos, o uso da
memória compartilhada (shared) pode ser uma alternativa para contornar a diminuição do
bandwidth. E o terceiro ponto observado está relacionado com o tamanho do problema
investigado, destacando-se que para modelos computacionais altamente discretizados, o uso
de múltiplas GPUs se faz necessário. E isto se dá em decorrência da quantidade de operações
a serem realizadas, em que somente uma placa gráfica não seja capaz de fornecer um
desempenho adequado e entrega de resultados em tempo hábil, com speedups adequados.

Na busca realizada por trabalhos na literatura, viu-se que o emprego de GPUs, contando ou
não com recursos de MPI, foi capaz de aumentar substancialmente a velocidade de
processamento de problemas relacionados à Dinâmica de Fluidos e a Engenharia do Vento.
Nos trabalhos explorados, identificou-se speedups que ficaram na faixa de 10x a 100x, em

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


70

geral. Observando-se uma tendência no emprego de múltiplas GPUs para o processamento de


problemas científicos que demandam um alto nível de processamento e realização de tarefas
em paralelo, sendo esta abordagem promissora no processamento de problemas de alta
demanda computacional.

Nos estudos de caso realizados pelo presente autor do seminário, investigou-se o emprego da
linguagem CUDA para dois algoritmos numéricos, um destinado ao tratamento de dados de
ensaios de pressões em modelos rígidos do túnel de vento Prof. Joaquim Blessmann e o outro
confeccionado por Braun (2007), com ambos demandando um alto nível de processamento
para a realização de dadas tarefas. Na presente investigação, comparou-se o desempenho dos
códigos utilizando CPUs e GPUs/CUDA, em versões paralelas e seriais dos códigos.

Para o primeiro algoritmo investigado, verificou-se altos speedups em relação à versão serial
do código para CPU. Além disso, notou-se que o desempenho de processamento alcançado
pelo CUDA foi superior ao obtido utilizando a versão em OpenMP para 2 dos 3 trechos do
código. Outro ponto que merece destaque nas simulações realizadas foi o desempenho em
precisão simples do CUDA, que atingiu altíssimos speedups, sendo o desempenho superior,
em um dado trecho, 15x em relação ao código OpenMP. Para os resultados advindos das
variáveis declaradas como sendo de dupla precisão, nota-se um decréscimo substancial na
performance do código em CUDA, porém este ainda continua sendo mais veloz na execução
das rotinas programadas.

Para o segundo código investigado, cujo modelo matemático baseia-se na solução das
equações fundamentais da Dinâmica de Fluidos através do emprego de séries de Taylor,
minimização das equações por Bubnov-Galerkin, no contexto dos Elementos Finitos, viu-se
que a construção da versão do código empregando o uso de kernels em KLD constituiu em
uma solução ineficiente, uma vez que o speedup médio alcançado ficou abaixo do
apresentado pela versão Serial. Tal comportamento visto, aponta que a construção do código
de fluidos através de kernels em KLD demanda muitas trocas de informações entre host e
device, o que é um ponto crítico na performance do código utilizando recursos de cálculo de
placas gráficas. Sendo assim, o modelo do código numérico empregado deverá ser alterado de
modo a contar com kernels construídos usando a lógica de módulos, o que será capaz de
reduzir a troca de informações host/device e permitir uma melhora significativa na
performance computacional do código de fluidos contando com os núcleos de processamento
de placas gráficas.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
71

Embora o código de fluidos modificado pelo presente autor, contando com recursos de GPUs,
não tenha apresentado uma performance computacional superior às obtidas nas versões Serial
e OpenMP, vê-se um grande potencial no uso das GPUs, visto a performance obtida no
primeiro código investigado no presente seminário e também nos resultados apresentados por
diversos autores da mesma área acerca do uso do CUDA para a simulação de problemas de
Dinâmica dos Fluidos e de interesse da Engenharia do Vento. Dados os resultados
encontrados para o código de fluidos em versão CUDA, tem-se que a reescrita deste, visando
uma abordagem que reduza a troca de dados entre host e device, possibilitaria um acréscimo
importante na performance computacional, entregando ao final, respostas em um tempo
menor do que a versão em OpenMP.

Analisando os resultados encontrados na literatura e também os obtidos em relação aos


códigos investigados neste seminário, conclui-se que a arquitetura CUDA e o uso de placas
gráficas consiste em uma alternativa de muita potencialidade para a solução de problemas
relacionados à Dinâmica de Fluidos e Engenharia do Vento, dada a capacidade de
processamento que se pode dispor. Contudo, alerta-se que é fundamental que o programador
tenha conhecimento do hardware empregado, que programe o código computacional de modo
a explorar eficientemente os recursos do CUDA e que também realize testes buscando
detectar as melhores formas de otimização do código. Tais pontos são determinantes para se
obter um programa computacional de alta eficiência e performance de processamento.

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


72

REFERÊNCIAS

ARMALY, B. F.; DURST, F.; PEREIRA, J. C. F., SCHÖNING, B. 1983. Experimental and
Theoretical Investigation of Backward-Facing Step Flow, Journal of Fluid Mechanics, v.
127, pp. 473-496.

BRAUN, A. L. Simulação numérica na Engenharia do Vento incluindo efeitos de


interação fluido-estrutura. 2007. 300 f . Tese (Doutor em Engenharia) – Programa de Pós-
Graduação em Engenharia Civil, Universidade Federal do Rio Grande do Sul, 2007.

CARDOSO, E. A. Análise comparativa de algoritmos NP-Completo executados em CPU


e GPU utilizando CUDA. 2012. 84 f. Trabalho de Conclusão de Curso (Ciência da
Computação) – Centro de Ciências Tecnológicas da Terra e do Mar, Universidade do Vale
Itajaí, Santa Itajaí, 2012.

COUTO, L. F. M. do. Arquitetura de computação paralela para resolução de problemas


de dinâmica dos fluidos e interação fluido-estrutura. 2016. 239 f. Dissertação (Mestre em
Engenharia) – Escola Politécnica da Universidade de São Paulo, Universidade de São Paulo,
São Paulo, 2016.

GOMES, H. C. Método dos Elementos Finitos com Fronteiras Imersas aplicado a


problemas de Dinâmica dos Fluidos e Interação Fluido-Estrutura. 2013. Tese (Doutor em
Engenharia) – Escola Politécnica da Universidade de São Paulo, Universidade de São Paulo,
2013.

GUIA, U.; GHIA, K. N.; SHIN, C. T. High-Re solutions for incompressible flow using
Navier-Stokes equations and a multigrid method. Journal of Computational Physics, v. 48,
p. 387-411, 1982.

NOGUEIRA, M; PENA, D; MILANÉS, A. Implementação de um algoritmo paralelo na


GPU para o problema da Máxima Subsequência Crescente permitindo inversões e
rotações. In: SBPO – Simpósio Brasileiro de Pesquisa Operacional, Porto de Galinhas:
Pernambuco, vol. XLVII, 2015.

OYARZUN, G.; BORREL, R.; GOROBETS, A; LEHMKUHL, O; OLIVA, A. Direct


numerical simulation of incompressible flows on unstructured meshes using hybrid
CPU/GPU supercomputers. In: ParCFD 2013 – Parallel Computational Fluid Dynamics
Conference, Changsha: China, 2013.

PETRY, A. P. Análise numérica de escoamentos turbulentos tridimensionais


empregando o Método dos Elementos Finitos e simulação de grandes escalas. 2002. 149
f. Tese (Doutor em Engenharia) – Programa de Pós-Graduação em Engenharia Mecânica,
Universidade Federal do Rio Grande do Sul, 2002.

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.
73

PHILLIPS, E. H.; ZHANG, Y; DAVIS, R. L.; OWENS, J. D. Rapid Aerodynamic


Performance Prediction on a Cluster of Graphics Processing Units. In: Proceedings of the
47th AIAA Aerospace Sciences, Orlando: Estados Unidos da América, 2009.

PGI Compiler & Tools, 2017. “CUDA Fortran programming guide and reference”. Nvidia
Corporation, 2017, USA.

RUETSCH, G.; FATICA, M. CUDA Fortran for Scientists and Engineers: best practices
for efficient CUDA Fortran programming. 1 ed. Waltham: Elsevier, 2014.

SANDERS, J.; KANDROT, E. CUDA by Example: an introduction to general-purpose GPU


programming. 1 ed. Boston: Addison-Wesley, 2010.

SENOCAK, I; THIBAULT, J; CAYLOR, M. Rapid response Urban CFD Simulations using a


GPU Computing Paradigm on Desktop Supercomputers. In: Eigthth Symposium on thr Urban
Environment, Phoenix: Estados Unidos da América, 2009.

SHAFFER, M; TUREK, S.; DURST, F; KRAUSE, E.; RANNACHER, R. Benchmark


computations of laminar flow around a cylinder. Flow Simulation with High-Performance
Computers II, v. 48, p. 547-566, 1996.

THIBAULT, J. C.; SENOCAK, I. Accelerating incompressible flow computations with a


Pthreads-CUDA implementation on small-footprint multi-GPU plataforms. Journal of
Supercomputing,vol. 59, p. 693-719, 2012

Estudo do uso da linguagem CUDA na simulação computacional de problemas da Engenharia do Vento


74

ANEXOS:

ESPECIFICAÇÕES DE GPUs TESLA NVIDIA (RUESTCH; FATICA, 2014)

__________________________________________________________________________________________
Guilherme Wienandts Alminhana (guilherme.alminhana@gmail.com) Seminário de Doutorado. PPGEC/UFRGS. 2018.

Anda mungkin juga menyukai