Universidade Federal do ABC. Dissertação de Mestrado. Aderbal de Morais Junior

Tamanho: px
Começar a partir da página:

Download "Universidade Federal do ABC. Dissertação de Mestrado. Aderbal de Morais Junior"

Transcrição

1 Universidade Federal do ABC Curso de Pós Graduação em Ciência da Computação Dissertação de Mestrado Aderbal de Morais Junior UMA BIBLIOTECA PARA DESENVOLVIMENTO DE APLICAÇÕES CUDA EM AGLOMERADOS DE GPUS Santo André - SP 2013

2 Curso de Pós Graduação em Ciência da Computação Dissertação de Mestrado Aderbal de Morais Junior UMA BIBLIOTECA PARA DESENVOLVIMENTO DE APLICAÇÕES CUDA EM AGLOMERADOS DE GPUS Trabalho apresentado como requisito parcial para obtenção do título de Mestre em Ciência da Computação, sob orientação do Professor Dr. Raphael Yokoingawa de Camargo. Santo André - SP 2013

3 Este exemplar foi revisado e alterado em relação à versão original, de acordo com as observações levantadas pela banca no dia da defesa, sob responsabilidade única do autor e com a anuência de seu orientador. Santo André, de de 20. Assinatura do autor: Assinatura do orientador: 3

4 Agradecimento À Universidade Federal do ABC. À minha esposa Tiana e filhos que me deram força e incentivo para vencer este desafio. Ao orientador Prof. Doutor Raphael Y. de Camargo, pelo acompanhamento de todo trabalho. Ao amigo Diogo Fernando Trevisan pela ajuda nas dúvidas que surgiram no decorrer do curso.

5 Resumo A cada dia fica evidente a importância de placas gráficas (GPUs) no auxílio ao processamento de grandes quantidades de informações. Muitas inovações surgiram para resolver problemas complexos e com este trabalho queremos auxiliar o desenvolvimento de algoritmos eficientes usando o processamento da GPU e do computador. Com a linguagem CUDA podemos explorar a capacidade de processamento das GPUs, não só na computação gráfica, mas também na científica. A dissertação tem a proposta de criar uma biblioteca para auxiliar o desenvolvimento de aplicações CUDA para aglomerados de GPUs. Esta biblioteca identifica as características de computadores que possuem GPUs na rede e ajuda na partição e distribuição dos dados de acordo com o poder computacional das GPUs em cada máquina. Propõe-se desta forma facilitar o uso de aglomerados de GPUs, escondendo as complexidades no gerenciamento de múltiplos processos. Criar uma biblioteca de suporte para aglomerados com GPUs que executam programas paralelos em CUDA é uma forma de auxílio para simplificar o algoritmo, pois a identificação de GPUs disponíveis na rede e execução de aplicativos em aglomerados com placas gráficas ainda causa transtornos que podem ser resolvidos com o uso da biblioteca de suporte. Palavras-chave: CUDA, MPI, GPU, Cluster e Aglomerado.

6 Abstract Every day it is evident the importance of graphics cards (GPUs) to aid the processing of large amounts of information. Many innovations have emerged to solve complex problems and, with this work, we develop efficient algorithms using the processing of GPUs and CPU. With CUDA we can exploit the processing power of GPUs, not only in computer graphics, but in scientific applications. The dissertation deals with the proposal of creating a library to assist the development of applications for CUDA on GPU clusters. This library identifies the characteristics of computers that have GPUs in the network and provides assistence to the partitioning and distribution of data according to the computational power of GPUs on each machine. In this way the proposal intends to facilitate the use of clusters of GPUs, by hiding the complexities of managing multiple processes. To create a support library for clusters with GPUs running CUDA parallel programs is a form of aid to simplify the algorithm, since the identification of GPUs available on the network and running applications on clusters with graphics cards still causes difficulties, which can be facilitated with the use of the support library. Keywords: CUDA, MPI, GPU and Cluster.

7 Conteúdo 1 Introdução Objetivo Justificativa Metodologia Contribuições Arquitetura de GPUs NVIDIA Arquitetura Fermi Arquitetura Kepler CUDA Aglomerados de GPUs Arquitetura de Aglomerados que Utilizam GPUs Técnicas de Programação MPI para Aglomerados de GPUs Programação Híbrida para Aglomerados de GPUs: MPI com CUDA Uma Biblioteca para Desenvolvimento de Aplicações CUDA em Aglomerados de GPUs - CCL Funcionalidades Descoberta de Recursos Distribuição dos Dados e Balanceamento de carga Coleta de Resultados Caso de Uso: Utilizando a Biblioteca na Multiplicação de Matrizes Caso de Uso: Utilizando a Biblioteca na Subsequência Máxima Experimentos Experimentos na Arquitetura

8 5.1.1 Multiplicação de Matrizes Subsequência Máxima Conclusões Experimentos na Arquitetura Multiplicação de Matrizes Subsequência Máxima Conclusões Experimentos na Arquitetura Multiplicação de Matrizes Subsequência Máxima Conclusões Conclusão 76 A Manual do Usuário para biblioteca CUDA Cluster Library 82 A.1 Criando seu Projeto com a biblioteca CCL A.2 Funções de Interface com o Usuário

9 Lista de Figuras 2.1 Arquitetura de memória de uma GPU [37] Arquitetura CPU e GPU [29] Mudanças que ocorreram na arquitetura Fermi [30] Diferenças entre arquitetura Kepler e Fermi [33] Tecnologias suportadas na Kepler e Fermi [33] API CUDA [8] Descrição de um bloco dentro do Grid [5] Fluxo de execução em CUDA [7] Duas placas no mesmo nó com o sistema UVA [34] Placa Infiniband com GPUDirect [36] MPI Allgather Diferentes espaços de memória em aplicações CUDA e MPI Usuário passa como parâmetro os arranjos displacements e sendcount para processar dados na GPU CCL Gatherv Identificação de GPUs e partição de dados entre processos com uso da biblioteca Multiplicação de matrizes Fluxograma das funções da biblioteca usadas na multiplicação de matrizes CCL Gather Intervalo I e Z [4] Fluxograma das funções da biblioteca usadas na subsequência máxima Arquitetura 1: multiplicação de matrizes (64 x 64) e (640 x 640) Arquitetura 1: multiplicação de matrizes (1280 x 1280) e (5000 x 5000) Gráfico Arquitetura 1 para Subsequência Máxima

10 5.4 Arquitetura 2: multiplicação de matrizes (64 x 64) e (640 x 640) Arquitetura 2: multiplicação de matrizes (1280 x 1280) e (5000 x 5000) Tempo gasto na GPU para processar os dados Gráfico Arquitetura 2 para Subsequência Máxima Gráfico micro Z800 (Arquitetura 3): multiplicação de matrizes 64 x 64 e 640 x Gráfico micro Z800 (Arquitetura 3): multiplicação de matrizes 1280 x 1280 e 5000 x Gráfico Arquitetura 3 Subsequência Máxima

11 Capítulo 1 Introdução A utilização de GPUs (Unidade de Processamento Gráfico) [24] para executar o processamento junto com a CPU pode trazer ganho de desempenho na computação científica e gráfica. A GPU consegue acelerar aplicativos de uso geral ao transferir parte deste processamento para ela, enquanto a outra parte continua na CPU. São desenvolvidas e pensadas para ter maior desempenho em tarefas paralelas, e fornecem acesso aos desenvolvedores através do modelo de programação paralela CUDA. A plataforma CUDA (Compute Unified Device Architeture) [2] foi desenvolvida pela NVIDIA e possui ampla utilização na computação de alto desempenho. Com a plataforma CUDA existe a possibilidade de acesso direto na GPU e pode-se enviar códigos em C, C++ ou Fortran sem precisar de uma outra linguagem para compilar os programas. O desenvolvimento de aplicações nesta plataforma pode ser complexo, pois necessita desenvolver o programa pensando na parte que será paralelizada pela GPU e na parte sequencial executada pela CPU. No caso de aglomerados de GPUs é mais difícil pois deseja-se utilizar múltiplos computadores com GPUs na execução de um programa. Para programar em aglomerados de GPUs, é preciso pensar em como aproveitar este paralelismo, pois podem existir computadores de configurações diferentes e GPUs de diferentes modelos. É necessário também um compilador na rede que possa suportar esta programação paralela em diversos nós do aglomerado. Neste caso, uma abordagem que pode ser utilizada é o desenvolvimento de aplicações com a biblioteca MPI [20] em conjunto com CUDA. Assim, os dados da aplicação podem ser particionados e distribuídos entre os nós do aglomerado de GPUs [1] ao utilizar trocas de mensagens. Os processos de cada nó podem então realizar suas tarefas e, em seguida, devolver os resultados obtidos. Para obter um balanceamento de carga adequado, é preciso descobrir as características de 11

12 cada computador. Balancear carga de trabalho é uma atividade primordial no aglomerado para diminuir o tempo ocioso e ocupar todo o hardware disponível da melhor forma possível. Alcançar este balanceamento no aglomerado depende do paralelismo da bibilioteca, do aplicativo que será executado e do número de threads. Muitos núcleos ociosos podem prejudicar o desempenho do aglomerado, por isso é preciso reduzir o tempo ocioso nas operações paralelas e distribuir esta carga da melhor forma possível. A bibilioteca desenvolvida busca facilitar o desenvolvimento de aplicações CUDA em aglomerados de GPUs, distribuir os dados da melhor forma possível e falicitar a comunicação entre os processos. 1.1 Objetivo O objetivo consiste em desenvolver uma biblioteca para facilitar o desenvolvimento de apliações CUDA/MPI em aglomerados de GPUs. Desta forma, consegue-se diminuir o tempo na elaboração de códigos em CUDA para aglomerados, onde programadores não precisarão se preocupar em identificar modelos de GPUs ou capacidade de placas para executar seus programas. Busca ser uma opção para desenvolver aplicações CUDA em aglomerados de GPUs, que visa estabelecer uma forma de gestão para este tipo de programa, ao tratar adequadamente os dados que precisam ser executados neste ambiente. É possível com o uso da biblioteca distribuir os dados entre os nós do aglomerado e processar de acordo com o poder computacional das GPUs, desta forma GPUs mais potentes recebem quantidade maior de dados para calcular o resultado esperado. Esta biblioteca também tem o objetivo de ocultar as complexidades de troca de mensagens MPI entre os nós e diminuir as dificuldades de realizar desenvolvimento de aplicações para aglomerados de GPUs. 1.2 Justificativa Este projeto busca minimizar problemas que desenvolvedores encontram para executar aplicações CUDA em diversos nós que possuem GPUs. A biblioteca se torna uma solução viável para distribuir os dados de uma aplicação CUDA em diversos computadores no ambiente homogêneo ou heterogêneo, pois após encontrar as GPUs disponíveis e seu poder computacional, distribui os dados após selecionar critérios para encontrar o melhor desempenho. Estes critérios de divisão e processamento que podem ser escolhidos pelo usuário, buscam GPUs mais potentes para receber uma quantidade maior de dados ou distribui de forma linear. A biblioteca pode ser uma ferramenta para trabalhar com aglomerados que possuem GPUs e facilitar o desenvolvimento de algumas aplicações que podem ser adaptadas neste ambiente. O projeto também utiliza a troca 12

13 de mensagens entre os nós de uma forma simples, pois esta oculta nas funções da biblioteca. Assim o usuário não precisará de muito conhecimento sobre MPI para executar sua aplicação no aglomerado de GPUs. 1.3 Metodologia O projeto envolve a criação de uma biblioteca para desenvolvimento de aplicações CUDA em aglomerados de GPUs. Este projeto usa o ambiente Linux com a plataforma CUDA e biblioteca MPI configurados. No processo de investigação ou experimental foram escolhidos para caso de uso a multiplicação de matrizes e subsequência máxima, pois este projeto está dirigido à solução de um problema específico e exige testes de funcionalidade e desempenho. A abordagem do projeto é qualitativa, pois analisa dados e verifica a relevância da biblioteca como uma opção viável, após coletar resultados de desempenho no aglomerado de GPUs. O desenvolvimento do projeto seguiu então alguns passos para sua completa realização: Escolha do ambiente Linux. Configurações da plataforma CUDA, MPI e compilador gcc. Criação da biblioteca: foram desenvolvidas funções, códigos e pastas necessárias para seu funcionamento. Criação da aplicação para caso de uso: foram adaptadas duas aplicações (multiplicação de matrizes e subsequência máxima) para utilizar a biblioteca e realizar os experimentos. Realização dos testes em arquiteturas homogêneas e heterogêneas de computadores para colher dados e resultados (pesquisa documental) sobre funcionamento e desempenho da aplicação que usou a biblioteca. 1.4 Contribuições Este projeto de pesquisa contribuiu para executar aplicações CUDA em aglomerados de GPUs buscando resolver alguns problemas neste ambiente: Elaborou uma aplicação para o usuário identificar se o computador do aglomerado possui uma ou mais GPUs, seu poder computacional, modelo e quantidade de núcleos; Definiu como distribuir os dados para realizar o balanceamento do trabalho de acordo com o poder computacional de cada GPU; 13

14 Facilitou a comunicação entre os processos na coleta de resultados, onde foi preciso ocultar parte das complexidades do MPI na biblioteca e com isso auxiliar o programador no desenvolvimento de aplicações CUDA em aglomerados. Este trabalho contribuiu com experimentos realizados adaptando um programa em CUDA de multiplicação de matrizes e subsequência máxima para avaliar a funcionalidade e desempenho da biblioteca desenvolvida. Os experimentos foram realizados em três diferentes arquiteturas de computadores: homogêneos (computadores e GPUs do mesmo modelo), heterogêneos (GPUs de diferentes modelos) e um computador com duas GPUs. Estes experimentos realizados apresentaram alguns resultados importantes para as três arquiteturas: A Arquitetura de computadores e GPUs do mesmo modelo (homogêneos) apresentou resultados que mostraram que a biblioteca cumpriu sua função de distribuir e processar os dados nas GPUs. O teste da biblioteca na aplicação multiplicação de matrizes trouxe ganho de desempenho ao incluir mais nós no aglomerado, pois o tempo de execução diminuiu pela metade ao dobrar o número de computadores. Já no experimento da subsequência máxima cumpriu sua funcão de distribuir e processar os dados mas não houve ganho considerável de desempenho ao aumentar o número de nós. A Arquitetura de computadores com GPUs de diferentes modelos (heterogêneos) apresentou resultados que mostraram que a biblioteca cumpriu sua função de distribuir e processar os dados nas GPUs. O teste da biblioteca com a aplicação multiplicação de matrizes se tornou eficiente ao escolher o critério correto de distribuição de dados (Linear, Core, Sm, Memory Division). A escolha correta do critério de divisão foi importante para um bom desempenho pois aproveitou o poder computacional das GPUs disponíveis e distribuiu de forma balanceada a porcentagem de dados entre elas, desta forma GPUs de maior poder computacional receberam uma quantidade maior de dados para processar no ambiente heterogêneo. A Arquitetura com um computador e duas GPUs apresentou resultados que mostraram que a biblioteca cumpriu sua função de distribuir e processar os dados nas GPUs. O teste da biblioteca na aplicação multiplicação de matrizes trouxe melhor desempenho do que dois ou quatro nós das outras arquiteturas. Este computador obteve bom desempenho pois possui duas GPUs de maior poder computacional comparado ao ambiente homogêneo ou heterogêneo. No experimento da subsequência máxima, a biblioteca cumpriu sua função de 14

15 distribuir os dados e processar na GPU, mas não houve ganho considerável de desempenho comparado as outras arquiteturas, pois a computacão que o computador tem que fazer para processar contém um vetor pequeno que causou maior tempo na comunicação e envio de dados do que o tempo de processamento na GPU. Os experimentos trouxeram resultados que mostram que a biblioteca desenvolvida funciona, pode ser adaptada para uma aplicação e obter um bom desempenho no ambiente de aglomerado de GPUs. 15

16 Capítulo 2 Arquitetura de GPUs NVIDIA A NVIDIA evoluiu significativamente no desenvolvimento de novas placas gráficas comparado com a velocidade dos processadores da CPU. Em pouco tempo ganhou destaque na computação paralela para realizar tarefas de propósito geral. Podem trazer maior desempenho do que a CPU para realizar operações gráficas, filtros de textura, operações de dupla precisão e atômicas [30]. Os chips de processamento das GPUs são desenvolvidos para maior desempenho gráfico e existem algumas placas com maior capacidade computacional do que outras. Além dos chips de processamento, as placas apresentam conexão PCI-Express, que podem ser 2.0, 3.0 dependendo do modelo escolhido. As GPUs possuem multiprocessadores com muitos núcleos e utilizam a estrutura de instrução que permite que os dados sejam processados em paralelo simultaneamente. A mesma instrução pode ser executada por várias threads para manter o hardware ocupado. Estes multiprocessadores são conhecidos como Streaming Multiprocessor (SM), e possui uma grande quantidade de CUDA Core, que são as unidades de processadores programáveis da GPU. As GPUs possuem alguns tipos de memória e seguem uma hierarquia para seu funcionamento, conforme pode ser visto na Figura 2.1. Os multiprocessadores utilizam para o processamento de dados uma unidade básica de execução chamado Warp, cada Warp possui trinta e duas threads. Um grupo de Warp forma um bloco. Conforme a Figura 2.1, as threads são separadas por blocos e utilizam as memórias da seguinte forma: memória compartilhada - é acessível somente as threads de um bloco para ler e escrever. Esta memória possui um tempo de acesso muito baixo e alta largura de banda. memória de registro - as threads possuem acesso de leitura e escrita. memória local - são variáveis declaradas e cada thread possui a sua. 16

17 Figura 2.1: Arquitetura de memória de uma GPU [37]. memória global - é acessada por todas as threads de todos os blocos para ler e escrever, ou seja, a memória global é acessível a qualquer thread. Possui um tempo de acesso muito maior comparado com a memória compartilhada. memória de constante - acessível a todos os blocos e threads para leitura. memória de textura - também é acessível a todos os blocos e threads para leitura. No acesso à memória de uma GPU, o SM faz o escalonamento baseado em pedaços de tempo. Quando o processador executa uma determinada instrução de acesso à memória, é gerada uma requisição desta memória e existe troca de Warp na execução até que todos os valores deste Warp estejam disponíveis. Sua arquitetura contém um número muito maior de processadores do que uma CPU, conforme pode ser visto na Figura 2.2, podendo realizar um grande número de operações paralelas sobre os mesmos dados [29]. As GPUs são dedicadas exclusivamente ao processamento de dados, não tem assim a tarefa de guardar informações em memórias cache como a CPU e nem de tratar um controle de fluxo, pois as aplicações gráficas são extremamente paralelas. 17

18 Figura 2.2: Arquitetura CPU e GPU [29]. As GPUs reordenam o acesso com maior rapidez através de agrupamento dos blocos de uma vez, para reduzir desta forma as latências e para melhorar o acesso à suas memórias. Como cada multiprocessador executa várias threads, cada uma delas possui acesso a memória compartilhada e de registradores para aumentar a velocidade na troca de informações entre threads do mesmo grupo. Para não ocorrer conflitos quando mais de uma delas tentarem ler ou escrever ao mesmo tempo na memória, a GPU possui um controlador para gerenciar o multiprocessador e as threads concorrentes e paralelas. Desta forma a arquitetura de uma GPU possui maior capacidade para operações de ponto flutuante por segundo e executa múltiplas threads em cada multiprocessor [37]. 2.1 Arquitetura Fermi A arquitetura Fermi [32] é um avanço que ocorreu nas arquiteturas de GPUs comparada com as placas G80 e GT200 [25], que foi a visão inicial gráfica sobre a computação paralela. Com a arquitetura Fermi [30], a NVIDIA empregou melhorias como dupla precisão com ponto flutuante, possibilidade de utilizar mais memória compartilhada e desempenho melhorado no acesso a instruções de memória para endereçamento de 64 bits. Algumas destas mudanças podem ser vistas na Figura 2.3. A arquitetura Fermi possui 512 núcleos CUDA organizados em 16 multiprocessadores, superior a G200 que possui 240 núcleos [32]. Cada multiprocessador possui 32 núcleos, que compartilham a memória cache (L2). Contém um número maior de transistores na sua arquitetura e suporta a versão IEEE , padrão para ponto flutuante. Pode escalonar até 2 Warps simultaneamente e consegue realizar um melhor balanceamento de threads para as tarefas na GPU. Pode executar múltiplos kernels em paralelo quando possível e traz desempenho considerável em determinadas tarefas comparado com a arquitetura anterior. Possui memória global de até 6GB, Cache L2 (768KB) compartilhado entre todos os cores e 18

19 Figura 2.3: Mudanças que ocorreram na arquitetura Fermi [30]. 16 SMs. A arquitetura Fermi suporta 64 bits e versões anteriores somente executavam no espaço de memória de 32 bits [31]. 2.2 Arquitetura Kepler A arquitetura Kepler [33] foi lançada em 2012 com a placa GTX680 [26] e utiliza API CUDA 5.1 em diante. O grande avanço na arquitetura é o número de núcleos CUDA ou CUDA Core por SM, que foi aumentado para 192. A Kepler chegou então com um número de 15 SM, 192 núcleos de precisão simples e 64 unidades de precisão dupla [7]. Além de poder utilizar 4 Warps (4 x 32 threads paralelas) concorrentes ao mesmo tempo (quad warp scheduler) [33] conforme Figura 2.4. Na arquitetura Kepler, existe uma diferença importante em relação a Fermi, que foi reduzir a quantidade de Streaming Multiprocessor pela metade e aumentar o número de CUDA Core de 32 para 192 cores. O desempenho melhor de Kepler se deve a frequência de operação para gráficos e memória que pode atingir 6008 Mhz com a GK104 e representa um incremento sobre a Fermi, que possui por exemplo 4008 Mhz na GF110, traz assim um ganho considerável de desempenho comparado com outras arquiteturas. Ocorreu aumento de CUDA Cores de 512 para 1536 comparado com a Fermi GTX580 [26]. A velocidade de cada Core também aumentou na Kepler GTX680 para 1002 Mhz e precisa utilizar PCIe 3.0. A dissipação térmica (TDP) e diminuição do consumo também ocorreu na Kepler, que apesar de possuir 1536 CUDA Cores e clocks elevados, possui um consumo baixo. Na Kepler GeForce GTX680 tem TDP máximo de 170W. Comparado com a Fermi que possui em média 219W, houve uma melhora significativa em 19

20 Figura 2.4: Diferenças entre arquitetura Kepler e Fermi [33]. termos de consumo de energia [26]. Outro ponto importante em Kepler [33] foi aumento de threads por multiprocessdores e bloco. A implementação do chamado Hyper-Q também foi importante pois permite que vários núcleos da CPU usem simultaneamente os núcleos da arquitetura Kepler para processar os dados [33]. Isso aumenta a utilização da GPU e diminui ociosidade dos núcleos da CPU que possuem mais de um core. O Hyper-Q é ideal para aplicações de cluster que usam MPI e possibilita disparar kernels simultaneamente. Esta tecnologia não é suportada na arquitetura Fermi e alguns modelos de GPU da arquitetura Kepler, como a GK104. O paralelismo dinâmico também não é suportado em alguns modelos da Kepler, como pode ser visto na Figura 2.5 que informa suporte destas tecnologias pela GK CUDA CUDA (Compute Unified Device Architeture) é uma plataforma de programação paralela para uso geral em GPUs fabricadas pela NVIDIA [21]. Também pode ser definido como uma arquitetura paralela de propósito geral destinada a utilizar o poder de processamento das placas gráficas da NVIDIA, permitindo controlar a execução de threads e gerenciar memória destas GPUs [28]. Na plataforma CUDA [23] é possível acessar a GPU usando funções em C (C for CUDA), o que 20

21 Figura 2.5: Tecnologias suportadas na Kepler e Fermi [33]. a torna uma ferramenta simples para desenvolvedores que tem familiaridade com a linguagem. CUDA pode ser obtido gratuitamente na página da NVIDIA [21] e está disponível para Windows (XP, Vista e 7), Linux e MacOS X, em versões de 32 e 64 bits. É composto por: CUDA Driver: Permite o acesso ao hardware da GPU; CUDA Toolkit: Ferramentas e bibliotecas para programação em CUDA; CUDA SDK: Compilador e exemplos de código. A comunicação entre a CPU e GPU através da plataforma CUDA necessita de uma pilha de softwares que determina a hierarquia de compilação, conhecida como API CUDA [27], que pode ser visto em Figura 2.6. O modelo proposto visualiza a GPU como co-processador (Device) e a CPU como responsável pelo processamento principal (Host). Na API, o CUDA Driver corresponde a camada intermediária entre o código compilado e a GPU. Por atuar no último nível de abstração, apresenta maior complexidade na sua manipulação direta pela necessidade de maior entendimento das funcionalidades de comunicação com a GPU utilizada pela plataforma CUDA [8]. O CUDA driver possibilita um controle maior dos recursos viabilizando o desenvolvimento de dispositivos mais complexos e específicos. O CUDA Runtime API atua como um intermediário entre o desenvolvedor e o driver, de forma a facilitar a programação pois oculta alguns detalhes de baixo nível. É responsável por realizar implicitamente a inicialização, gerenciamento de contexto e dos módulos. Os programas desenvolvidos normalmente utilizam o CUDA Runtime, que fornece primitivas e funções de altonível. Além disso, é possível utilizar também a API do driver, que permite um melhor controle da aplicação [29]. 21

22 Figura 2.6: API CUDA [8]. A API CUDA também fornece suporte a diversas funções auxiliares representadas por várias bibliotecas prontas, que estão no nível mais alto, como por exemplo CUBLAS (Basic Linear Algebra Subprograms) e UFFT (Fast Fourier Transform - FFT). Estas bibliotecas permitem ligar o programa aos recursos computacionais da GPU [28]. A flexibilidade de API CUDA permite que a NVIDIA mude frequentemente a arquitetura de sua GPU sem torná-la ultrapassada [8]. Para realizar um determinado trabalho na GPU, um programa desenvolvido em CUDA deve transferir dados entre o espaço de memória do computador host até a GPU. Programas desenvolvidos em CUDA precisam de chamadas ao kernel, o qual é uma função desenvolvida no código para informar à CPU (host) que a partir daquele momento o processamento dos dados será executado na GPU por várias threads em paralelo. O código do programa desenvolvido na plataforma CUDA precisa obedecer algumas regras para utilizar os multiprocessadores e distribuir as threads na memória da GPU. O programa precisa definir o bloco de threads, que é a forma de organização destas threads dentro do hardware nos multiprocessadores (SM) da GPU. Os blocos de uma GPU podem ser definidos também como grupos de threads organizados para processar os dados de forma paralela. 22

23 Figura 2.7: Descrição de um bloco dentro do Grid [5]. Um grupo de blocos formou então um grid [6] que distribui as threads devidamente mapeadas individualmente com sua identificação no bloco. Neste modelo de programação que utiliza as threads, blocos e grids, o processamento da aplicação CUDA inicia-se então na CPU e invoca-se o kernel (função executada na GPU chamada pelo host), o processamento passa então para a GPU. Após processar e finalizar o kernel, o processamento retorna novamente para a CPU. A fim de utilizar CUDA de maneira a obter o melhor desempenho, programadores precisam escolher a melhor maneira de dividir os dados em blocos menores e o desafio de encontrar o número ideal de threads, blocos que manterão a GPU totalmente utilizada [11]. Para aproveitar a capacidade da GPU é preciso que o hardware esteja o mais ocupado possível. Quando a GPU processa uma quantidade pequena de dados, provavelmente não haverá ganho de desempenho comparado com uma aplicação que utiliza somente o processamento da CPU [5]. Programar em uma GPU requer criar aplicações que tenham partes sequenciais e partes paralelas, qualquer solução que não se enquadra no paralelismo não é interessante de ser desenvolvida em 23

24 CUDA, pois aumenta a complexidade e não ocorre ganho de desempenho. A Figura 2.8 mostra de forma mais clara o fluxo de uma aplicação desenvolvida em CUDA. Figura 2.8: Fluxo de execução em CUDA [7]. Para encaminhar os dados que serão processados na GPU, é necessário então realizar alguns passos no código para utilizar CUDA: Passo 1: Inicia a execução do programa na CPU e aloca espaço de memória na GPU. Passo 2: Copia os dados da CPU para a GPU. Passo 3: Chama o kernel Passo 4: Processa os dados da aplicação na GPU. Passo 5: Retorna o processamnto para CPU. Passo 6: Copia os dados com o resultado da GPU para a CPU. Passo 7: Mostra os resultados e libera o espaço de memória que foi reservado. Os fatores para análise de um código que será criado em CUDA incluem o tamanho do conjunto global de dados, a máxima quantidade de dados locais que um bloco de threads pode 24

25 compartilhar, o número de SM na GPU e o tamanho das memórias on-chip [10]. No CUDA é possível especificar blocos multidimensionais de threads com índices. O acesso aos blocos realizado utiliza-se (blockidx), que permite indexação utilizando blockidx.x ou blockidx.y. Nos índices das threads utiliza-se a variável threadidx, a qual é um vetor de até três dimensões. O acesso neste exemplo é feito na dimensão threadidx.x e threadidx.y. 1 // Kernel definition 2 global void MatAdd (float A[N][N], float B[N][N], float C[N][N]) 3 { 4 int i = threadidx.x; 5 int j = threadidx.y; 6 C[i][j] = A[i][j] + B[i][j]; 7 } 8 int main () 9 { // Kernel invocation with one block of N * N * 1 threads 12 int numblocks = 1; 13 dim3 threadsperblock(n, N); 14 MatAdd<<<numBlocks, threadsperblock>>>(a, B, C); } Este código é um exemplo simples de soma de matrizes que faz chamada do kernel com a função MatAdd executa o processamento da matriz A e B e armazena o resultado em C. Todas as threads possuem sua identificação no bloco com duas dimensões para linhas e colunas. Os diversos blocos de threads podem ser organizados em um nível acima de abstração, em grids com uma ou duas dimensões, como se cada bloco de threads fosse um elemento da matriz. O grid tem definido o número total de blocos e de threads que serão criados e gerenciados pela GPU para uma dada função [3]. O programador específica o número de threads por bloco e o número de blocos dentro de um grid, conforme o código a seguir que mostra a soma de matrizes por bloco. 1 // Kernel definition 2 global void MatAdd(float A[N][N], float B[N][N], float C[N][N]) 3 { 4 int i = blockidx.x * blockdim.x + threadidx.x; 5 int j = blockidx.y * blockdim.y + threadidx.y; 6 if (i < N && j < N) 7 C[i][j] = A[i][j] + B[i][j]; 25

26 8 } 9 int main() 10 { // Kernel invocation 13 dim3 threadsperblock(16, 16); 14 dim3 numblocks(n / threadsperblock.x, N / threadsperblock.y); 15 MatAdd<<<numBlocks, threadsperblock>>>(a, B, C); 16 } Para alocar memória, utilizam-se as funcões CUDAMalloc(), e CUDAFree(). A primeira é usada para alocação de memória na GPU. E a segunda é utilizada para liberar o espaço de memória que foi utilizado na GPU pelo programa, conforme mostra o código em CUDA. 1 int n = 1024; 2 int nbytes = 1024 * sizeof(int); 3 int *h_a = 0; 4 int *d_a = 0; 5 h_a = (int*) malloc(nbytes); // Allocate memory on host. 6 CUDAMalloc((void**)&d_a, nbytes); // Allocate GPU memory. 7 CUDAMemset ( d_a, 0, nbytes); // Initialization of GPU memory. 8 CUDAMemcpy(d_a, h_a, nbytes, CUDAMemcpyHostToDevice); // Copy the host to GPU 9 10 // an application code deleted CUDAFree(d_a); // Release memory on the GPU. 13 free(h_a); // Release memory on the host. Para programar na plataforma CUDA é preciso observar algumas características das memórias e algumas extensões chamadas de qualificadores, que são necessárias para utilizar o driver e suas memórias. qualificadores de função: que define se a execução será na CPU ou GPU. qualificadores de variável: que define onde elas serão armazenadas, na CPU ou na GPU. Existem três qualificadores de tipo de função: device, global e host. O qualificador device define uma função que será executada na GPU e só pode ser chamada a partir da GPU. 26

27 O qualificador global define o que a plataforma CUDA chama de kernel, ou seja, uma função que é executada na GPU e é chamada a partir da CPU. o qualificador host define uma função que será executada na CPU e que só pode ser chamada a partir da CPU. Os qualificadores de variável são: device, constant e shared. O qualificador device define uma variável que reside na memória global da GPU. São acessíveis por todas as threads de um grid e também a partir da CPU através do uso da biblioteca de runtime do CUDA. O tempo de vida é da aplicação. O qualificador constant define uma variável que reside no espaço de memória constante da GPU. O qualificador shared reside na memória compartilhada da GPU e são acessíveis apenas pelas threads de um mesmo bloco. O tempo de vida é do bloco. Os acessos à memória global usando funções CUDA são realizados por Warps e pode-se utilizar transações de até 128 bytes em um acesso coalesced. Quanto maior a capacidade computacional da GPU (acima de 2.0), mais facilmente pode ter acesso coalesced. O acesso coalesced consiste em realizar uma única transação na memória para aumentar o desempenho, pois recupera um segmento com todas as threads de um half-warp (metade), ou seja, quando as threads estão alinhadas como um vetor, elas acessam elementos na ordem com uma única transação. Quanto mais desalinhados estão os dados maior é a queda no desempenho, pois é preciso mais transações na memória para acessar os dados. Para usar a memória global usamos o qualificador device. O acesso a memória compartilhada na plataforma CUDA, é alocada por blocos e cada bloco têm acesso à sua memória. As threads podem acessar dados na memória compartilhada que foram carregadas na memória global por outras threads. Para alcançar a largura de banda, a memória compartilhada é dividida em módulos (bancos) para ler e escrever simultaneamente. Para usar a memória compartilhada usamos o qualificador shared. A memória local é privada, dividida e alocada para as threads, cada uma com seu próprio espaço de memória e usadas na GPU para preencher toda a ocupação do hardware. Esta memória não esta em cache e não existem qualificadores para o programador utilizar no código em CUDA. Os acessos aos caches de memória de constantes e de texturas de um multiprocessador servem para dar agilidade no processamento, pois as memórias responsáveis por esses tipos de armazenamentos se encontram na memória global do dispositivo, dessa forma os núcleos conseguem 27

28 diminuir número de acessos às memórias externas ao multiprocessador. Para usar a memória contante usamos o qualificador constant. Em CUDA o compilador nvcc faz a maioria do trabalho na conversão de código em C para um arquivo executável que será executado na GPU. O nvcc separa a compilação do código que faz com que partes do código escritas em C sejam compiladas utilizando o compilador da linguagem C, e as partes do código escritas em CUDA são compiladas com o nvcc [28]. É importante ressaltar que na plataforma CUDA o processamento é realizado na CPU até o compilador encontrar a palavra reservada kernel. A partir deste ponto o processamento será realizado na GPU. O processo de reservar memória e a cópia dos dados para a GPU são realizados pela CPU, já a GPU tem a função de executar o processamento dos dados [29]. Após este processo, a GPU termina o processamento e retorna a execução para ser processada na CPU. 28

29 Capítulo 3 Aglomerados de GPUs Aglomerados ou clusters são um conjunto de computadores interconectados que trabalham juntos para resolver um determinado problema. Cada computador trabalha de maneira independente executando uma pequena tarefa de modo a auxiliar na resolução do problema maior [9]. Um cluster de GPUs é um aglomerado de computadores no qual cada nó está equipado com uma ou mais unidades de processamento gráfico (GPUs). Para aproveitar o poder computacional das GPUs modernas para computação de propósito geral em unidades de processamento gráfico (GPGPU), cálculos muito rápidos podem ser realizados com um cluster de GPU. Em um aglomerado que possui GPUs, podem existir computadores de diversas configurações e modelos (heterogêneos) ou computadores com uma única configuração e modelo (homogêneos). Também podem existir diversas arquiteturas e modelos de GPUs ou somente uma única arquitetura de placa nos diversos computadores. Esta definição do hardware depende dos recursos disponíveis e do tipo de resultado que se deseja obter na hora de montar um cluster desta natureza. Os aglomerados normalmente utilizam uma conexão local rápida com roteadores ou switchs ligados em uma rede ethernet ou fibra ótica. A diferença na arquitetura de comunicação escolhida pode afetar e muito o modo como a programação do aglomerado é feita [13]. Os programas paralelos desenvolvidos para executarem nos aglomerados normalmente utilizam o paradigma de troca de mensagens para compartilhar informações entre os processos no aglomerado devido ao baixo custo [9]. O tipo de aglomerado mais utilizado e que precisa de troca de mensagens é chamado de Beowulf, aplicado na computação paralela com maior escala do que outros, pois possui algumas características que fizeram dele uma opção de baixo custo, pois evita gastos com software e supercomputadores. Beowulf não exige uma arquitetura específica ou máquinas homogêneas [19]. 29

30 Um cluster desta classe possui: Conexão entre os nós, que pode ser feita por meio da ethernet. Deve haver um ou mais nós mestres (front-end) para realizar o controle dos nós escravos (back-end). O sistema operacional normalmente é baseado em código aberto, e o mesmo deve conter todas as ferramentas necessárias para a configuração do cluster. 3.1 Arquitetura de Aglomerados que Utilizam GPUs Uma arquitetura para aglomerados de GPUs precisa permitir que os desenvolvedores ajustem suas aplicações com o objetivo de atingir um desempenho ideal para aproveitar ao máximo o desempenho paralelo da GPU que usa programação em CUDA [23] ou OpenCL [22]. Também deve levar em conta o fluxo de trabalho pretendido em sua rede, seus computadores e placas disponíveis [14]. A primeira opção de comunicação em aglomerados de GPUs é utilizar troca de mensagens com a biblioteca MPI [20], onde o desempenho depende de como foi elaborado o código e da arquitetura da rede. A NVIDIA [23] também desenvolveu algumas tecnologias em suas placas para auxiliar o desempenho, como por exemplo a GPUDirect [33], que é um recurso que permite que as GPUs de um único nó ou GPUs de diferentes nós da rede, façam a troca de dados diretamente sem a necessidade de ir para a memória da CPU, diminui significativamente a latência de troca de mensagens com MPI para enviar e receber informações usando a memória da GPU. Com duas placas no mesmo nó é possível implementar o sistema UVA (Unified Virtual Addressing) junto com a tecnologia GPUDirect [30], que traz nesta junção o espaço de memória único entre placas, que funciona somente em GPUs da série Tesla 20, Fermi e com API CUDA 4.0 ou superior, conforme pode ser visto na Figura 3.1. Este código mostra a sintaxe de um programa desenvolvido em CUDA para tecnologia UVA e GPUDirect. 1 CUDASetDevice(0); // Set device 0 as current 2 float* p0; 3 size_t size = 1024 * sizeof(float); 4 CUDAMalloc(&p0, size); // Allocate memory on device 0 5 CUDASetDevice(1); // Set device 1 as current 6 float* p1; 30

31 Figura 3.1: Duas placas no mesmo nó com o sistema UVA [34]. 7 CUDAMalloc(&p1, size); // Allocate memory on device 1 8 CUDASetDevice(0); // Set device 0 as current 9 MyKernel<<<1000, 128>>>(p0); // Lauch kernel on device 0 10 CUDASetDevice(1); // Set device 1 as current 11 CUDAMemcpyPeer(p1, 1, p0, size); // Copy p0 to p1 12 MyKernel<<<1000, 128>>>(p1); // Lauch kernel on device 1 Conforme descrito neste exemplo é possivel copiar dados diretamente da GPU p1 para p2 e executar o kernel sem passar pela memória da CPU. Para comunicação entre nós diferentes do aglomerado é possível utilizar a GPUDirect com a tecnologia InfiniBand [33], que tem como função fazer uma ligação entre os nós de um cluster [17]. Suas características incluem alta transferência de dados, baixa latência, qualidade de serviço e também é projetado para ser escalável. A arquitetura InfiniBand define uma conexão entre os nós de processamento e alto desempenho de entrada e saída de dados entre nós. São fabricados pela Mellanox e Intel. Na junção de InfiniBand e GPUDirect, a cópia de dados de uma GPU para outra em nós diferentes, pode ser feita sem precisar de transferência de dados para memória da CPU [7], pois os dados são copiados da memória da GPU e transmitidos diretamente pela InfiniBand, traz assim desempenho e eficiência na troca de dados Figura 3.2. Por enquanto, a maioria das arquiteturas de computadores que fazem parte de aglomerados tendem a usar MPI e comunicação entre os nós via ethernet devido ao baixo custo. A comunicação entre os nós é feita por troca de mensagens, o que aumenta a complexidade para o desenvolvimento de aplicações. É preciso então escolher como utilizar GPUs de diferentes nós do aglomerado para executar um programa em CUDA, sendo necessário integrar MPI em uma transmissão via ethernet ou 31

32 Figura 3.2: Placa Infiniband com GPUDirect [36] escolher InfiniBand para transmissão dos dados entre os nós [18]. Para montar um aglomerado de bom desempenho é necessário também escolher o hardware, principalmente quando ele possui GPU. Deve-se ter como meta distribuir eficientemente programas paralelos com um conjunto de placas gráficas, e assim realizar a transferência entre as GPUs o mais rápido possível para evitar o tempo ocioso [16]. Por isso uma bom cluster GPU deve possuir : Uma escolha melhor do modelo de GPU para se adaptar as suas necessidades computacionais; Escolher uma plataforma de hardware correta para apoiar a GPU (processador, placa-mãe, memória RAM, disco rígido, fonte de alimentação); Escolher a infra-estrutura de rede que irá suportar todo o fluxo de comunicação do cluster. Apesar da tecnologia GPUDirect se tornar uma futura tendência para programação em clusters, ainda é preciso adquirir um outro hardware, o adaptador InfiniBand. Este adaptador pode proporcionar uma diminuição significativa na latência de comunicação, mas é preciso ter maior disponibilidade de recursos financeiros e realizar modificações no hardware. Por isso foi feita a escolha no projeto de utilizar MPI, rede ethernet e desenvolver a biblioteca. É possível criar uma solução às aplicações que podem ser adaptadas e executadas na maioria dos aglomerados de GPUs. 32

33 3.2 Técnicas de Programação MPI para Aglomerados de GPUs MPI (Message-Passing Interface) [20] é uma biblioteca de troca de mensagens, desenvolvida para ambientes de memória distribuída, máquinas paralelas e redes heterogêneas. A biblioteca MPI representa um paradigma de programação paralela pois as informações são movidas de um espaço de endereçamento de um processo para o espaço de outro processo [12]. É uma biblioteca de rotina que fornece funcionalidade básica para que os processos se comuniquem. Em MPI, o paralelismo é explícito, o programador é responsável pela distribuição dos dados e os problemas são divididos em pequenas partes que são distribuídas para as máquinas do aglomerado para realizar o cálculo em cimas dessas partes. Os resultados obtidos das máquinas são enviados a um processo receptor, normalmente o root, que coleta os resultados, agrupa e dá o resultado esperado. Os processos podem ser executados em um computador ou em vários, cada um destes processos tem uma identificação única atribuído pelo sistema quando o processo é inicializado [35]. A identificação do processo é representada por um número inteiro, que começa de 0 (zero) até N 1, desta forma N é o número de processos. Cada um destes processos tem um rank, e este rank é utilizado para enviar e receber mensagens. A diferença de cada um dos processos envolvidos no processamento dentro do aglomerado é realizada através destes identificadores. Um grupo criado em MPI é um conjunto ordenado de N processos, e este grupo associado a um comunicador predefinido chamado MPI COMM WORLD, ele é o comunicador padrão que inclui todos os processos definidos pelo usuário numa aplicação MPI [3]. O MPI proporciona uma API para a comunicação entre os processos com algumas funções principais: MPI Send, MPI Recv, MPI Bcast, MPI Barrier, MPI Allgather e MPI Reduce. Para iníciar e o encerrar um programa em MPI é usado o MPI Init e MPI Finalize. O MPI Comm size retorna o número de processos dentro de um grupo e MPI Comm rank identifica um processo MPI dentro de um determinado grupo. Retorna sempre um valor inteiro entre 0 e n 1, n é o número de processos. É preciso lembrar que o MPI agrupa todos os processos em um comunicador padrão chamado MPI COMM WORLD, como no exemplo de código a seguir. 1 #include <mpi.h> 2 #include <stdio.h> 3 int myrank, totalprocess; 4 MPI_Init(&argv, &argc); 5 MPI_Comm_size}(MPI_COMM_WORLD}, &totalprocess); 33

34 6 MPI_Comm_rank}(MPI_COMM_WORLD}, &myrank); 7 printf("in this program there are processes running. s\n", totalprocess);} 8 if (myrank == 0){ 9 printf("hello! I am the root process.\n");} 10 else 11 printf("hello! I am the process.\n", myrank); 12 MPI_Finalize(); 13 } Para compilar um programa desenvolvido em MPI utiliza-se o comando mpicc e sua execução é com o comando mpirun ou mpiexec: mpicc hello.c -o hello para compilar e mpirun -np 4 hello para executar. A variável totalprocess neste programa contém o número correspondente ao total de processos MPI e a variável myrank contém o valor do identificador de cada um dos processos dentro do comunicador MPI COMM WORLD. A definição de quantos processos serão executados é feita através da chamada mpirun passado como argumento o número de processos a serem executados. MPI apresenta duas funções principais para troca de mensagens: MPI Send e MPI Recv. Estas duas funções definem uma mensagem e o seu tamanho, define o tipo de dados da mensagem que será enviada ou recebida, qual o destinatário, sua tag, que identifica a mensagem, um comunicador rank de referência para os processos, e no MPI Recv, também define o status da mensagem que foi recebida. Com estas funções é possível enviar suas mensagens e dados de um processo para outro. 1 2 #include <mpi.h> 3 #include <stdio.h> 4 #include <string.h> 5 int main(int argc, char *argv[]){ 6 int myrank, totalprocess, rankorigin, rankdestination, tag = 50; 7 char message[50]; 8 MPI_Status status; 9 MPI_Init(&argv, &argc); 10 MPI_Comm_size (MPI_COMM_WORLD), &totalprocess); 11 MPI_Comm_rank (MPI_COMM_WORLD), &myrank); 12 if (myrank!= 0){ 13 printf(message, "This process is \n", myrank); 34

35 14 rankdestination = 0; 15 MPI_Send(message, strlen(message) + 1, MPI_CHAR, rankdestination, tag, 16 MPI_COMM_WORLD); 17 else 18 for (rankorigin = 1; rankorigin < totalprocess; rankorigin++) { 19 MPI_Recv(message, 100, MPI_CHAR, rank_origin, tag, MPI_COMM_WORLD, &status); 20 printf("\n", message);} 21 } 22 MPI_Finalize(); 23 return 0; 24 } Neste programa cada processo imprime uma mensagem, que inclui o seu rank que identifica a origem desta mensagem e realiza uma chamada a função MPI Send que informa que o processo destino é o root. Estes são os comandos básicos usados para criar uma aplicação em MPI, mas na biblioteca deste projeto foi preciso utilizar outras funções de comunicação coletiva que estão ocultas para o usuário. Funções para o envio e recebimento de chamadas do tipo que utiliza buffers na comunicação coletiva, que precisa de uma barreira para sincronizar os dados. Algumas destas funções que foram utilizadas são: MPI Barrier - é utilizado para sincronização dos dados. MPI Bcast (raiz void * buffer, int count, MPI Datatype datatype, int, MPI Comm comm) - faz comunicação um para vários, ou seja, faz a movimentação de dados transmitindo uma mensagem a partir do processo root para todos os outros processos do grupo. MPI Allgather (void * sendbuf, int sendcount, MPI Datatype sendtype, void * recvbuf, int recvcount, MPI Datatype recvtype, MPI Comm comm) - faz uma comunicação de vários para vários, pois reúne os dados de todas as tarefas e distribuí também para todos, é utilizado quando os blocos de dados são de tamanho fixo. A Figura 3.3 abaixo, mostra que cada processo possui uma infomação, utilizando o MPI Allgather estas informações são enviadas de todos os processos para todos, desta forma no final cada processo possui a informação completa. MPI Allgatherv também reúne os dados de todas as tarefas e distribuí para todos, com a diferença de que os dados reunidos e distribuídos são de tamanhos variados ou de diferentes tipos. 35

36 Figura 3.3: MPI Allgather A biblioteca utiliza estas funções pois envolve comunicação dos dados com todos os processos dentro de um comunicador, que no caso é MPI COMM WORLD. A comunicação chamada de vários para vários, é necessário pois todos precisam coletar os dados e distribuir para todos. O código a seguir mostra a comunicação coletiva para sincronizar e coletar os dados de todos os processos. 1 int main(int argc, char *argv[]) 2 { 3 int my_rank; 4 int numprocess; 5 MPI_Init (&argc, &argv); /* starts openmpi */ 6 MPI_Comm_rank (MPI_COMM_WORLD, &my_rank); /* create the process*/ 7 8 MPI_Comm_size (MPI_COMM_WORLD, &numprocess); 9 /* each process collects your information */ 10 proppc = collectioninfo(&numprop, my_rank); 11 MPI_Barrier(MPI_COMM_WORLD); /* buffer who will receive the information from all other processes */ 14 propall = malloc(numprocess * sizeof(struct Summarizedproperties)); /* gathering and distributing all for all */ 17 MPI_Allgather (proppc, sizeof(*proppc), MPI_BYTE, propall, sizeof(*proppc), MPI_BYTE, MPI_COMM_WORLD); 18 } 36

37 3.3 Programação Híbrida para Aglomerados de GPUs: MPI com CUDA Para utilizar MPI com CUDA é preciso alocar os diferentes espaços de memória na tranferência entre a GPU e CPU e descobrir a melhor forma de copiar os dados na memória para os processos antes de executar o kernel [15]. Precisa ser identificado o rank do processo e os dados serão subdividos pela quantidade de GPUs encontradas nestes processos, que precisam depois realizar uma sincronização dos resultados em uma barreira para as mensagens MPI. Será preciso gerar um processo por GPU presente no nó e deve-se escolher se a comunicação será via ethernet ou Infiniband. Cada um dos processos tem sua memória, GPU e RAM, e a troca de dados é feita pelo MPI como pode ser visto em Figura 3.4. Figura 3.4: Diferentes espaços de memória em aplicações CUDA e MPI As responsabilidades da CPU ou host são copiar dados para memória da GPU, receber dados que estão na GPU e iniciar a tranferência via MPI. A responsabilidade da GPU é fazer o trabalho de processamento que antes era feito somente na CPU. Quando se executa um programa usando MPI é preciso levar em conta o processador do computador, o processador da GPU, a cópia dos dados que foi feita para a memória e também a comunicação que foi escolhida. No aglomerado que usa MPI e CUDA é preciso um bom balanceamento de carga para responder aos requisitos de muitos nós que precisam do mesmo serviço (distribuição dos dados com MPI e processamento da sua parte com CUDA). Este balanceamento de carga tenta distribuir de acordo com a disponibilidade de processamento de cada computador no aglomerado. O balanceamento deve ser aplicado em pontos que podem se tornar um gargalo no programa, caso contrário não irá contribuir com a melhoria de desempenho, e dependendo da estratégia pode prejudicar o desempenho devido a possíveis sobrecargas no sistema. É preciso garantir que todas as máquinas recebam 37

38 uma carga de trabalho equivalente ou que leve em conta o poder computacional das GPUs e seus recursos disponíveis. É bom ressaltar que não existe a obrigatoriedade do balanceamento de carga ser centralizado, pode ser distribuído entre os nós que compõem o sistema. O desenvolvedor precisa definir na sua aplicação como utilizar CUDA e MPI entre os processos [18]. O método escolhido para utilizar MPI e CUDA neste projeto foi com comunicação via ethernet sem tecnologia Infiniband entre os nós. A biblioteca começa com um processo mestre ou root para executar o programa que encontra as placas e distribui os dados em paralelo para ele mesmo e para os outros processos. As GPUs calculam em paralelo os dados em cada placa com suas respectivas threads. Quando o conjunto de threads completam a execução dos resultados, são então sincronizadas e finalizados os outros processos, permanecendo somente o root. A biblioteca deste projeto foi capaz de realizar um balanceamento do processamento de carga nas tarefas, pois distribui a utilização de todas as GPUs com alguns parâmetros definidos na biblioteca e que são informados pelo usuário no programa. Para criar estes parâmetros foi preciso desenvolver funções que leva em conta as características encontradas nas GPUs para processar os dados de acordo com a poder computacional. No programa que precisa utilizar a biblioteca, cada processo inicializa esta biblioteca e requisita informações de GPUs com funções MPI, depois todos os processos encontram as GPUs disponíveis, coletam suas características com funções desenvolvidas na plataforma CUDA e armazenam em uma estrutura. Depois copiam-se os dados para memória da GPU e cada processo invoca o kernel CUDA para processar a sua parte na divisão do array, seguindo o parâmetro definido pelo usuário (baseado nas características e modelos de GPUS encontradas). O usuário realiza a escolha do parâmetro para dividir e processar os dados de forma linear, número de multiprocessadores de cada GPU, CUDA Cores ou memória (mais detalhes sobre a forma de processamento dos dados estão no capitulo 4.3). Depois de processar os dados, os resultados são coletados e reunidos pelo processo root com MPI. As requisições neste tipo de aplicação híbrida precisa ser distribuída entre os nós disponíveis com GPUs e possuir escalabilidade para expandir no aglomerado a inclusão de novos nós. 38

39 Capítulo 4 Uma Biblioteca para Desenvolvimento de Aplicações CUDA em Aglomerados de GPUs - CCL Desenvolvemos uma biblioteca que ajuda na criação e execução de aplicações CUDA [23] para aglomerados de GPUs [24]. Esta biblioteca chamada CUDA Cluster Library ou CCL, foi utilizada com duas aplicações CUDA em vários nós com GPUs. Após pesquisar as tecnologias que poderiam ser suportadas, opções viáveis e disponíveis, descobriu-se que a forma mais acessível seria implementar a biblioteca com processamento distribuído no aglomerado através da troca de mensagens com MPI [20] e rede ethernet, devido ao seu baixo custo e praticidade. O código completo da biblioteca CCL esta em raphael. camargo/ccl/ para consulta pública. Este capítulo descreve as funcionalidades da biblioteca, descoberta de recursos, como foi realizado a distribuição de dados, balanceamento de carga e coleta dos resultados. 4.1 Funcionalidades A biblioteca possui um conjunto de pastas, subpastas e arquivos que são necessários para o seu funcionamento. Para usar a biblioteca de suporte, basta copiar a pasta com os arquivos contidos nela junto com seu programa em CUDA para um diretório e seguir alguns passos que podem ser vistos com mais detalhes no apêndice A. As funções da biblioteca estão contidas no código MPIClusterLibraryCUDAInterface.c e CUDAClusterLibrary.cu e são destinadas a utilizar recursos da GPU e distribuir dados en- 39

40 tre diferentes nós do cluster, também são importantes para fazer cópias entre CPU e GPU do mesmo nó. Na aplicação que o usuário precisa executar no aglomerado de GPUs, é necesssário fazer a chamada da biblioteca, que possui todas as funções necessárias para adaptar e executar esta aplicação no aglomerado. A biblioteca descobre os recursos disponíveis no aglomerado, pois identifica qual o nó que possui GPU e seu poder computacional. Esta função de coletar propriedades precisa trabalhar diretamente na GPU com um código desenvolvido na plataforma CUDA, pois são criadas funções para cópia entre CPU e GPU e alocação de memória que utiliza estas informações. As propriedades e características de todas as GPUs encontradas no aglomerado são armazenadas em variáveis e apoiam a biblioteca para definir qual o nó que possui a GPU com maior poder computacional, para depois processar os dados em maior quantidade nesta GPU e realizar o balanceamento de carga. As funções desenvolvidas no código CUDA contém: Funções para contar a quantidade de GPUs. Funções internas para verificar a arquitetura das GPUs. Funções para coletar características das GPUs. Funções internas para copiar dados entre CPU e GPU de cada nó. Funções para alocar memória nas GPUs. Para realizar a troca de dados entre diferentes nós do aglomerado foram desenvolvidas funções de interface para o usuário, e estão na biblioteca em um código que utiliza de forma oculta o MPI. Este código.c contém: Funções para inicializar a troca de mensagens. Funções para enviar e receber dados entre os processos. Funções de comunicação coletiva para particionar e distribuir os dados. Funções para definir qual a parte do array será dividido para o processo e onde inicia sua parte. Funções para escolher a forma que serão processados os dados na GPU, escolhendo um critério que pode ser definido pelo usuário (balanceamento). Coletar e reunir os resultados. 40

41 Algumas funções importantes para o usuário desenvolvedor são descritas a seguir: void CCL initializelib() - Inicializa a biblioteca. CCL getgpuprops() - Recebe e envia as propriedades e características da GPU que está sendo utilizada, para o processo correspondente, afim de obter qual o nó responsável por esta GPU(seu hostname ou ip) e capacidade computacional desta placa, que será importante para definir o critério escolhido na distribuição dos dados. void CCL BCast() - Envia dados de um nó para todos os outros nós. void CCL Gather() - reune os dados de todos os nós, e coloca tudo em um, no processo root. void CCL Send() - Envia e recebe dados da CPU de um determinado nó do aglomerado para outro nó que faz parte deste aglomerado. É uma função síncrona, que internamente especifica quem envia e quem recebe os dados. CCL autoscattercounters() - Esta função conta quantos dados cada nó do aglomerado irá receber e processar na GPU, devolve o array sendcount que pode ser usado na função CCL Scatterv e identifica também para o usuário o tipo de divisão que será utilizado para obter o resultado. CCL autoscatterdisplacements() - Esta função informa a posição inicial do array que cada nó irá começar a processar na GPU, retorna o displacements que pode ser usado em CCL Scatterv. Pega a quantidade de dados definido pela função CCL autoscattercounters e identifica a posição inicial da sua parte no array para processar os dados na GPU, após escolher qual o critério da divisão e distribuição dos dados que será utilizado pelo usuário Figura

42 Figura 4.1: Usuário passa como parâmetro os arranjos displacements e sendcount para processar dados na GPU. CCL Scatterv() - Particiona e envia um array (conjuntos de dados) para todos os processos, pode enviar para a memória da CPU ou da GPU. Para usar CCL Scatterv() é necessário utilizar CCL autoscattercounters() e CCL autoscatterdisplacements() para distribuir a porção correta de dados entre os nós. void CCL Gatherv() - reune conjuntos de dados (array) de todos processos com tamanhos variáveis e coloca em um processo root. Para usar CCL Gatherv é necessário criar dois arrays recvcounters(de 1 até NumProcessos) e displacements(1 até Num- Processos). Desta forma recvcounters possui o array e NumProcessos é o número de elementos que serão recebidos de cada processo Figura 4.2. Figura 4.2: CCL Gatherv 42

43 void CCL Synchronize() - sincroniza dados de todos os nós. int CCL getmyrank() - Retorna o rank do nó que chama esta função. void CCL finalizelib() - Finaliza a biblioteca MPI e limpa os dados. *Os parâmetros de cada função podem ser vistas com mais detalhes no apêndice A. 4.2 Descoberta de Recursos Cada processo deve inicializar a execução da biblioteca de suporte e o processo root distribui os dados para os nós com GPUs. Depois cada processo deve chamar o kernel CUDA e retornar o resultado. Para isso acontecer o usuário precisa das informações sobre todas as GPUs disponíveis nos computadores do aglomerado com a função CCL getgpuprops() e CCL getclusterdata, que recebe e envia as propriedades de todas GPUs para os processos. Coleta as propriedades de todos os processos e manda para todos de forma oculta com MPI Allgatherv que é uma função MPI. Estas propriedades são armazenadas em uma estrutura, que guarda as informações sobre a placa, como versão da arquitetura, quantidade de memória, velocidade do clock e número de núcleos. Para coletar as propriedades da placa e suas características, a biblioteca usa internamente uma função da API CUDA chamada CUDAGetDeviceProperties, que lê as informações das GPUs e as armazena em uma variável. O usuário precisa também no seu código de uma função que tem acesso ao número de placas de cada nó chamada CCL getnumberofgpuinnode(), que utiliza internamente uma função da API CUDA chamada CUDAGetDeviceCount para ler a quantidade de GPUs em cada computador do aglomerado. Esta somatória de GPUs é armazenada em uma variável numberofgpus que esta em uma estrutura contida no código GPUProperties.h e distribuída para todos os processos de forma oculta com MPI Allgatherv. A garantia de que todos os processos terão as configurações das placas de cada computador é realizado com o uso de uma barreira MPI. 4.3 Distribuição dos Dados e Balanceamento de carga Com as funções MPI de forma oculta, os dados de uma aplicação que utilizam a biblioteca CCL podem ser particionados e distribuídos entre os nós do aglomerado de GPUs, assim o programador responsável por desenvolver a forma de utilizar funções da biblioteca no código para envio, 43

44 recebimento de mensagens entre os processos, por sincronizar as informac o es na leitura, gravac a o da memo ria e para obter o resultado correto do ca lculo das tarefas paralelas. Cada um dos no s da rede MPI distribui os dados para os no s com GPUs, desta forma e importante escolher uma plataforma de hardware e infraestrutura de rede correta para apoiar a GPU na busca do melhor resultado. A Figura 4.3 mostra as requisic o es entre os processos que utiliza a biblioteca. Figura 4.3: Identificac a o de GPUs e partic a o de dados entre processos com uso da biblioteca. O processo de cada no que executa esta biblioteca, conta quantas GPUs o aglomerado possui, depois o processo root deve coletar o numero de GPUs de todos os no s e suas caracterı sticas, pois o processo root reu ne as caracterı sticas das GPUs encontradas e faz um pre -processamento em um arquivo hostname, que possui os Ips de todos os no s com GPUs e outras propriedades, pois seria custoso utilizar as informac o es de computadores que na o conte m GPUS no algoritmo. Com estas informac o es o processo root pode depois dividir a quantidade de dados entre os no s para que as GPUs possam processar os dados de acordo com o poder computacional. A biblioteca portanto utilizou simultaneamente a API CUDA para processamento e MPI para troca de mensagens, o processo MPI identificado como root, e o que primeiramente utiliza a memo ria e o processador do computador no envio e recebimento de informac o es para encontrar GPUs disponı veis nos outros processos e executar a distribuic a o dos dados. Depois na aplicac a o CUDA, e realizado co pia de dados da CPU para memo ria da GPU e em seguida processa sua parte atrave s de mu ltiplas threads na GPU. Este procedimento pode ser visto no pseudoco digo a seguir: 44

45 INICIANDO_A_BIBLIOTECA() SE PROCESSO = ROOT DISTRIBUI_DADOS_DA_APLICAÇÃO_ENTRE_PROCESSOS_GPU() CHAMA_O_KERNEL_CUDA<<<>>>() SE PROCESSO!= ROOT DEVOLVE_DADOS_PARA_O_PROCESSO_ROOT() De posse das informações sobre quantidade de GPUs por máquina e do poder computacional de cada GPU, é possível realizar a partição dos dados, seguido da distribuição destes para os processos correspondentes. Desta forma é preciso lembrar que para utilizar mais de uma GPU no computador, invocam-se dois processos para sua execução, ou seja, se existir duas GPUs no mesmo nó, faz a chamada de dois processos. Assim fica determinado uma GPU para cada processo. A vantagem é uma divisão fácil no mesmo computador e não perde tempo ao enviar dados pela rede, mas a desvantagem é que pode diminuir o desempenho. Para particionar e distribuir estes dados, foi implementada uma função CCL Scatterv. A função CCL Scatterv precisa da posição inicial da parte dos dados de cada processo no array e alguns critérios de distribuição e processamento para dividir os dados que são pré-definidos pelo usuário, utiliza para isto funções CCL autoscattercounters e CCL autoscatterdisplacements no código. Desta forma os dados podem ser distribuidos e processados na GPU que leva em conta a capacidade computacional para balanceamento de carga. Este balanceamento pode ser obtido seguindo alguns parâmetros no código: Linear Division - Os dados são divididos e processados na GPU de forma linear(iguais). Core Division - Os dados são divididos e processados na GPU levando em conta o número de CUDA Cores contidos em cada GPU. Desta forma a GPU que possui mais CUDA Cores recebe e processa mais dados. SM Division - Os dados são divididos e processados na GPU levando em conta o número de SMs contidos em cada GPU. Assim a GPU que possui o maior número de SMs recebe e processsa mais dados. Memory Division - Os dados são divididos e processados na GPU levando em conta a GPU que possui maior memória disponível, onde a GPU que possui mais memória recebe e processa mais dados. Estes parâmetros que precisam ser escolhidos no código pelo próprio usuário, para dividir o array e processar dados na GPU são realizados da sequinte forma: 45

46 Se a escolha for Linear Division, divide e processa os dados na GPU levando em conta a quantidade de dados do array / número de GPUs para balancear a carga. Se a escolha for SM Division, processa os dados na GPU levando em conta o número total de SMs encontrado em todas as GPUs para balancear a carga. Para isso é calculada a porcentagem do número total de SM que cada GPU possui, assim, caso exista no aglomerado uma GPU com 8 SM e outra com 2 SM, obtendo total de 10 SMs, então a GPU de 8 SM possui 80% do total de SM no cluster e a GPU com 2 SM tem 20% deste total. Os dados são divididos 80% para a primeira GPU e 20% para a outra GPU. Se a escolha for Memory Division processa os dados na GPU levando em conta a memória total encontrada em todas as GPUs para balancear a carga. Para isso é calculada a porcentagem da memória total que cada GPU possui, assim, caso exista no aglomerado uma GPU com 768MB de memória e outra com 256MB, obtendo total de 1024MB então a gpu de 768MB tem 75% da memória total do cluster e a GPU de 256MB tem 25% da memória total do cluster. Os dados são divididos 75% para a primeira GPU e 25% para a outra GPU. Este mesmo critério de divisão é utilizado para dividir os dados quando a escolha é Core Division, porém, é calculado a porcentagem total de Cores que cada GPU possui. A escolha dos critérios de divisão e processamento busca particionar os dados de uma melhor maneira, pois utiliza este critério para tentar obter um melhor desempenho. Se temos várias placas com quantidade de memória diferentes, então podemos dividir por memória para distribuir melhor os dados. Caso as GPUs existentes possuem a mesma quantidade de memória, mas seus processadores são de quantidades diferentes, então talvez compense dividir por CUDA Core. Se forem todas GPUs iguais, tanto faz esta divisão. A diferença no tempo que pode ocorrer entre uma escolha de critério ou outra, é algo que muitas vezes está fora do controle da biblioteca CCL, o processo root por exemplo, quase sempre demora mais tempo do que outros processos, pois o mesmo gera os dados, particiona, envia, recebe e gera o resultado final. Já os outros processos podem ter recebido os dados da rede um antes do outro, e obriga desta forma o processo root a esperar para continuar o processamento. 4.4 Coleta de Resultados Cada um dos processos pode criar sua parte dos dados e enviar esta parte para a GPU calcular o resultado. Depois cada resultado é enviado para o processo root, que reune os dados de todos 46

47 os nós, e coloca tudo em um (no processo root). Isto é feito através de chamadas MPI que foram ocultas na biblioteca, que aplica suas próprias funções para este propósito como CCL Gather e CCL Gatherv. Após reunir os resultados, os mesmos podem ser impressos ou armazenados pelo usuário no seu programa, então é feito a finalização da biblioteca com CCL finalizelib(), que libera a memória da GPU e termina a execução. Para os programas que utilizam esta biblioteca, o código em CUDA foi compilado com o compilador NVIDIA nvcc e os códigos em C++ são compilados utilizando o compilador MPI mpicc. Ambos compiladores utilizam um compilador C++ do sistema, geralmente o gcc em sistemas linux e o Microsoft Visual C++ em sistemas Windows, mais detalhes sobre a compilação podem ser vistos no apêndice A. 4.5 Caso de Uso: Utilizando a Biblioteca na Multiplicação de Matrizes Como estudo de caso para utilizar a biblioteca de suporte, uma aplicação de multiplicação de matrizes desenvolvido em CUDA foi executada em algumas arquiteturas disponíveis para verificar desempenho e tempo de execução. A multiplicação de matrizes consiste em realizar o produto interno de uma linha da matriz A n n com uma coluna da matriz B n n. A maioria dos problemas computacionais podem ser divididos em partes menores e muitas vezes independentes uma das outras. Na multiplicação de matrizes estas partes serão executadas paralelamente ao mesmo tempo no cluster. Na Figura 4.4 uma linha da matriz A e uma coluna de B obtem um resultado da multiplicação armazenado em C. Para se calcular a posição 2, 1 com matrizes 4 4, somam-se os produtos A(2, k) B(k, 1), para k = 1,..., 4 com resultado na matriz C. Figura 4.4: Multiplicação de matrizes. A implementação da multiplicação de matrizes com CUDA e MPI precisa seguir alguns passos 47

48 principais: Inicialização: Ocorre quando o MPI e blocos de threads CUDA são criados, realiza alocação de memória para a CPU e GPU e executa cada bloco com chamadas no kernel; Processamento dos dados: Ocorre quando as matrizes A e B são distribuídas entre os processos usando funções MPI de comunicação coletiva, depois são transferidas para GPU realizar os cálculos e montar a matriz resultado C, coloca de forma ordenada os elementos da matriz de cada processo e depois transfere novamente para CPU juntar os resultados ; Finalização: Ocorre quando os recursos são liberados e os processos MPI terminados. Para desenvolver a multiplicação de matrizes, os processos MPI são escondidos nas funções desta biblioteca, mas o desenvolvedor precisa definir no seu programa como serão enviadas a Matriz A, Matriz B e como serão recebidos os resultados conforme mostra parte do código utilizado na biblioteca. 1 int main(int argc, char *argv[]) 2 { 3 //initializes the lib. 4 CCL_intializeLib(argc, argv); 5 CCL_getClusterData(0); 6 int i; 7 8 //collects information about the GPUs (whether in has more than one). 9 printf("gpus: \n"); 10 for(i = 0; i < CCL_getNumberOfGPUInNode(); i++) 11 { 12 struct GPUProperties *prop = CCL_getGPUProperty(i); 13 printf("gpu Name: %s\n",prop->name);//print name. 14 printf("memory: %f\n",prop->memorysize); 15 printf("memory \%: %f\n",prop->memorypercentualcapacity); 16 printf("sm \%: %f\n",prop->smpercentual); 17 printf("core \%: %f\n",prop->corepercentual); 18 } //counter of how many items are received from each process, 21 // the "space" between each item in the array. 22 int *recvcounters, *displacements; 23 48

49 24 //calculates the division of the array based on the memory of the GPU s. 25 recvcounters = CCL_autoScatterCounters(N_ELEMS, CCL_LINEAR_DIVISION ); 26 displacements = CCL_autoScatterDisplacements(N_ELEMS, recvcounters, CCL_LINEAR_DIVISION); //the recvcounters has many elements each process sends / multiplies. 29 // initialize the data CUDA Kernel 30 initializedata(n_elems, recvcounters[ccl_getmyrank()]); //p0 create matrix A 33 if(ccl_getmyrank() == 0) 34 { 35 creatematrixa(); 36 //copy from GPU 37 CCL_CPU_GPU_Copy(A, getdevicea(), N_ELEMS, CCL_TYPE_FLOAT); 38 } //The bcast uses to send to the GPU for all processes. 41 CCL_BCast(0, CCL_DEVICE_GPU, getdevicea(), CCL_TYPE_FLOAT, N_ELEMS); //The strap of the CPU memory 44 if(a) 45 free(a); //each process creates its share of B 48 creatematrixb(); //copy columns B to GPU memory 51 CCL_CPU_GPU_Copy(B, getdeviceb(), N_ELEMS, CCL_TYPE_FLOAT); //invokes the kernel 54 printf("call Kernel p%d start %d size %d\n",ccl_getmyrank(), displacements[ccl_getmyrank()], recvcounters[ccl_getmyrank()]); 55 callkernel( HEIGHT, WIDTH, displacements[ccl_getmyrank()], recvcounters [CCL_getMyRank()]); CCL_Synchronize(); 58 49

50 59 if(ccl_getmyrank() == 0) 60 { 61 C = (float*) malloc(sizeof(float) * N_ELEMS); 62 } printf("ccl GatherV in process %d \n ",CCL_getMyRank()); //uses recvcounters arrangements and displacements calculated to know how many elements need to receive. 67 CCL_Gatherv(0, getdevicec(), C, CCL_TYPE_FLOAT, recvcounters, displacements, CCL_DEVICE_GPU, CCL_DEVICE_CPU); //frees the memory of the GPU. 70 cleandata(); 71 if(c) 72 free(c); 73 CCL_finalizeLib(); return 0; 76 } Na multiplicação desenvolvida com a CCL, o processo root cria então a matriz A, depois envia a matriz usando a função da biblioteca chamada CCL Bcast a todos os outros processos, que recebe e copia a matriz para a memória da GPU. Cada processo gera completamente a matriz B e copia essa matriz para memória da GPU. Depois cada processo é responsável por gerar na sua GPU a série de resultados da matriz C, conforme pode ser visto na Figura

51 Figura 4.5: Fluxograma das funções da biblioteca usadas na multiplicação de matrizes. 51

52 Para não ocorrer uma condição de corrida no processamento, cada bloco será responsável por multiplicar uma coluna de B por uma linha de A, similar a um produto interno de vetores. Neste cálculo, cada thread calcula ciclicamente A[i] B[i] (pensando nas linhas e colunas como vetores) e armazena as multiplicações locais em um espaço de memória compartilhada. A matriz C será completamente armazenada quando cada processo calcular sua parte. São enviados os dados de cada processo para o root que reúne todos os resultados obtidos e produz a matriz resultado final. No desenvolvimento da multiplicação de matrizes é chamada uma função CCL Gather, que coleta dados de todos os nós, conforme Figura 4.6. Figura 4.6: CCL Gather As matrizes são armazenadas de forma contígua, desta forma é possível usar o CCL Gather para coletar o resultado local de cada processo, levando este resultado para o processo root. Mas existe uma restrição na função que só pode ser utilizado quando cada processo tem uma parte da matriz do mesmo tamanho, pois ela recolhe porções iguais de cada processo. Como o CCL Gather só coleta dados com o mesmo tamanho nos processos, ele acabou se tornando uma limitação. Uma opção desenvolvida para corrigir este problema foi a função CCL Gatherv, que recolhe conjuntos de dados (arrays) de todos os processsos com tamanhos e tipos variados e coloca todos no processo root. Em Displacements encontra-se o deslocamento da posição inicial que cada GPU tem para processar a sua parte da matriz, ou seja, de onde cada processo começará a copiar os dados no arranjo, a partir de qual posição este processo recebe dados da matriz para copiar para GPU para realizar o processamento da sua parte na GPU. A função é importante para processar os dados de acordo com poder computacional encontrado em cada nó. No exemplo desta multiplicação de matrizes, assim como outras aplicações desenvolvidas com o uso da biblioteca CCL, o programador precisa implementar o programa como se estivesse desenvolvendo para múltiplas GPUs em um único computador. O que esta biblioteca faz é detectar a configuração das máquinas, realiza a distribuição dos dados entre os processos e coletar os dados 52

53 de volta ao processo principal. Deste modo, o programador pode desenvolver um programa para aglomerados de GPUs sem se preocupar com detalhes do MPI para troca de mensagens. 4.6 Caso de Uso: Utilizando a Biblioteca na Subsequência Máxima A implementação do algoritmo paralelo para diversos nós da Subsequência Máxima utilizou o algoritmo de Luz(2013) [4] para distribuir os dados no aglomerado de GPUs. Este algoritmo foi criado por Luz(2013) [4] para ser executado em um computador com múltiplas GPUs. Para isto Luz(2013) [4] precisou alocar um vetor de dados na CPU para armazenar os n números da sequência e depois utiliza o cudamemcpy() para copiar os dados para memória global da GPU. No código foi preciso utilizar também a memória compartilhada em um conjunto de 32 threads, para ler um conjunto de 32 elementos da memória global de uma vez. A primeira fase consiste em declarar um vetor de dados na memória compartilhada em grupos de 32 threads para preencher os dados, e com isso é possível realizar em seguida a transferência de dados da memória global para compartilhada. O código da subsequência aloca na memória compartilhada um vetor de elementos, que é acessível por bloco. Cada bloco aloca um vetor de elementos. A segunda fase consiste em determinar o número de threads e blocos lançados, onde foi criado um kernel com k blocos e com 128 threads em cada bloco, deixa assim cada bloco responsável por um determinado intervalo e responsável por processar estes intervalos de elementos conforme a Figura 4.7. Segundo o seu algoritmo, cada bloco precisa transferir o seu intervalo da memória global, para a memória compartilhada e logo depois serão processados. Cada bloco repete esses dois procedimentos: transferência e processamento, n vezes, até que todos intervalos sejam processados. A fase final consiste em fazer com que as threads utilizem o vetor compartilhado e não mais o vetor que foi armazenado na memória global. Cada thread terá então uma quantidade suficiente de dados para processar e é responsável por processar uma quantidade de 32 elementos do vetor compartilhado. Primeiro as 128 threads transferem os dados da memória global para a compartilhada. Depois a transferência é realizada em grupos de 32 threads e cada thread processará os seus 32 elementos. Após o processamento, todas threads transferem novamente os dados para o vetor compartilhado com seu resultado. 53

54 Figura 4.7: Intervalo I e Z [4]. Maiores detalhes de como Luz(2013) [4] realizou o processamento nas GPUs podem ser encontradas no seu trabalho de mestrado. A adaptação deste código para ser utilizado com a biblioteca, precisou de outro kernel chamado runkernelsubseqmax para ser executado em diferentes nós e N GPUs, modifica assim uma parte do código de Luz(2013)[4], que usa o vetor repartido entre o número de GPUs do mesmo nó. O código adaptado da subsequência máxima que utiliza a biblioteca, precisa chamar o kernel subseqmax em cada GPU encontrada no aglomerado, que tem a função de calcular a subsequência, e no final o processo root chama o kernel subseqmaxfinal para receber os resultados. Neste código foram alteradas as chamadas para N GPUs que utiliza agora a biblioteca na distribuição dos dados em outros computadores. Para realizar a mudança no código, as funções que Luz(2013)[4] criou para chamada de kernel e alocação de memória na GPU, precisou incluir extern C no inicio para serem acessadas no código que contém funções de troca de mensagens. Esta adaptação que utiliza a biblioteca CCL, delega funções ao processo root para criar os vetores e enviar os dados para GPUs de outros processos, logo depois divide e processa os dados de acordo com o poder computacional de cada GPU. Este código adaptado utiliza as funções CCL getclusterdata(), CCL autoscattercounters() e CCL autoscatterdisplacements(), onde o processo root encaminha os dados particionados entre os processos com a função CCL Scatterv(), 54

55 depois copia-se para memória da GPU e processa os dados de acordo com o critério informado pelo desenvolvedor para balanceamento de carga. Todo o fluxo para executar a subsequência pode ser visto na Figura 4.8. Figura 4.8: Fluxograma das funções da biblioteca usadas na subsequência máxima. Primeiro CCL getclusterdata() contém as informações necessárias de cada GPU encontrada no aglomerado. Depois CCL autoscattercounters() conta quantos dados do array inicial e final cada GPU irá processar na sua GPU, conforme explicação da função no capitulo 4.1. A função CCL autoscatterdisplacements() identifica a posição inicial no array que cada GPU irá processar. A função CCL Scatterv particiona e distribui os dados para os nós, que copiam estes dados para memória da GPU. Depois é realizado a chamada do kernel para o processamento dos dados seguindo os parâmetros de balanceamento de carga (linear, sm, core e memory division), que são informados pelo usuário no seu programa junto com as funções CCL autoscattercounters() 55

56 e CCL autoscatterdisplacements(), conforme pode ser visto no código: 1... // suppressed part 2 3 //sends the array h to the GPU nodes. 4 int *recvcounters, *displacements; 5 6 recvcounters = CCL_autoScatterCounters(N_ELEMS, CCL_LINEAR_DIVISION ); 7 displacements = CCL_autoScatterDisplacements(N_ELEMS, recvcounters, CCL_LINEAR_DIVISION); 8 9 if(ccl_getmyrank() == 0) 10 printf("send array vet_h for other processes \n"); 11 CCL_Scatterv(0, vet_h, getvet(), recvcounters, displacements, CCL_DEVICE_CPU, CCL_DEVICE_GPU); float elapsedtime = runkernelsubseqmax(n_elems, CCL_getNumberOfProcess ()); Após todos os processos calcularem sua parte da subsequência, o processo root reune todos os resultados com a função CCL Gatherv(). O programa pode então finalizar a biblioteca e a execução com a função CCL finalizelib(). 56

57 Capítulo 5 Experimentos Foram realizados diversos experimentos em algumas arquiteturas disponíveis para testar a funcionalidade e desempenho da biblioteca CUDA Cluster Library (CCL) na execução de Multiplicação de Matrizes e Subsequência Máxima. Utilizando diferentes critérios para divisão dos dados: Linear Division, Core Division, SM Division e Memory Division. Para execução dos testes foram utilizadas 3 arquiteturas de diferentes configurações. Arquitetura 1 - utiliza até 8 computadores homogêneos : - Modelo T3500 com Intel Xeon Quad Core 2.53 GHz. - 6 GB de RAM e GPU NVIDIA Quadro FX1800(64 CUDA Core, 768 MB, 8 SM). - Executado sobre o sistema operacional Ubuntu Conectados por uma rede Ethernet. - Testes realizados com 1, 2, 4 e 8 computadores(nós). - Biblioteca OpenMPI configurada junto ao SDK NVIDIA CUDA 5.0. Arquitetura 2 - utiliza até 10 computadores heterôgeneos: - 6 computadores T3500 com GPU modelo FX1800(64 CUDA Core, 768 MB, 8 SM), Intel Xeon Quad Core 2.53 GHz e 6 gb RAM. - 2 computadores T3500 com GPU modelo NVS295(8 CUDA Core, 256 MB, 1 SM), Intel Xeon Quad Core 2.53 GHz e 6 gb RAM. - 2 computadores HP 8100 Core I5 sem GPU. - Executado sobre o sistema operacional Ubuntu

58 - Conectados por uma rede Ethernet. - Testes realizados com 2 e 8 computadores(nós). - Biblioteca OpenMPI configurada junto ao SDK NVIDIA CUDA 5.0. Arquitetura 3 - utiliza um computador com 2 GPUs: - 1 computador HP Z800 com duas GPUs modelo Quadro 4000 (256 CUDA Core, 2 GB, 8 SM), Intel Xeon Six Core 3,46 GHz e 24 gb RAM. - Executado sobre o sistema operacional Ubuntu Conectados por uma rede Ethernet. - Biblioteca OpenMPI configurada junto ao SDK NVIDIA CUDA 5.5. Para medir o tempo de execução nos experimentos, foi preciso utilizar o comando time do Linux, usando como parâmetro o real time, que mostra o tempo gasto do programa na sua entrada e saida para processar e realizar as chamadas de kernel. Os experimentos realizados em todas arquiteturas, contou com 10 execuções seguidas sobre a matriz (64 x 64), (640 x 640), (1280 x 1280) e 10 execuções da subsequência máxima. A matriz de tamanho (5000 x 5000) foi executada 3 vezes em cada arquitetura. A média de tempo foi adquirida com todas as execuções realizadas nos experimentos e se encontra nas tabelas e gráficos para confirmar o desempenho da aplicação junto com a bibilioteca de suporte para aglomerados de GPUs. 5.1 Experimentos na Arquitetura Multiplicação de Matrizes O teste da Multiplicação de Matrizes na Arquitetura 1 foi realizado com o tamanho (64 x 64), (640 x 640), (1280 x 1280) e (5000 x 5000). O teste realizado na Arquitetura 1 utilizou 1, 2, 4 e 8 nós para comparar os resultados. Apresenta tabela e gráficos na Figura 5.1, Figura 5.2 com o tempo de execução dos diferentes critérios para divisão e processamento dos dados usando número de nós diferentes. Na tabela também é possível verificar o speedup do tempo original / tempo de outros processos (speedup x). 58

59 Tabela 5.1: Tempo em milissegundo VS Número de nós Arquitetura 1(homogêneos) Matriz: 64 x 64 Número de nós Core Division SM Division Memory Division Linear Division 1 0,213 0,216 0,214 0, ,297 (0,717x) 0,300 (0,720x) 0,295 (0,725x) 0,296 (0,716x) 4 0,352 (0,605x) 0,352 (0,613x) 0,354 (0,604x) 0,356 (0,595x) 8 0,355 (0,600x) 0,355 (0,608x) 0,356 (0,601x) 0,356 (0,595x) Tabela 5.2: Tempo em segundos VS Número de nós Arquitetura 1(homogêneos) Matriz: 640 x 640 Número de nós Core Division SM Division Memory Division Linear Division 1 2,843 2,794 2,816 2, ,792 (1,586x) 1,794 (1,557x) 1,798 (1,566x) 1,792 (1,585x) 4 1,069 (2,659x) 1,085 (2,575x) 1,084 (2,597x) 1,062 (2,676x) 8 0,767 (3,706x) 0,756 (3,695x) 0,753 (3,739x) 0,735 (3,866x) Tabela 5.3: Tempo em segundos VS Número de nós Arquitetura 1(homogêneos) Matriz: 1280 x 1280 Número de nós Core Division SM Division Memory Division Linear Division 1 8,513 8,613 8,632 8, ,776 (1,782x) 4,797 (1,795x) 4,786 (1,803x) 4,784 (1,804x) 4 3,143 (2,708x) 3,127 (2,754x) 3,221 (2,679x) 3,135 (2,753x) 8 2,212 (3,848x) 2,152 (4,002x) 2,239 (3,855x) 2,163 (3,990x) Tabela 5.4: Tempo em minutos VS Número de nós Arquitetura 1(homogêneos) Matriz: 5000 x 5000 Número de nós Core Division SM Division Memory Division Linear Division 1 09:25:56 09:26:16 09:25:53 09:25: :20:17 (1,487x) 06:20:06 (1,489x) 06:20:03 (1,488x) 06:19:58 (1,489x) 4 03:52:29 (2,434x) 03:52:16 (2,438x) 03:52:34 (2,434x) 03:52:42 (2,433x) 8 02:01:26 (4,664x) 02:01:31 (4,667x) 02:02:06 (4,633x) 02:02:03 (4,634x) 59

60 Nos testes realizados da multiplicação de matrizes na Arquitetura 1, podemos ver o tempo de execução e o desempenho dos diferentes números de nós. Nota-se algumas observações importantes no teste com arquitetura de computadores homogêneos: O tempo de execução da matriz (64 x 64) aumenta ao incluir mais nós no aglomerado, diferente do que ocorre em outros tamanhos da matriz. Quando dobramos o número de nós para executar uma matriz igual ou superior a (640 x 640), o tempo de execução diminui consideravelmente. Entre os critérios para dividir e processar os dados (Linear, Core, SM e Memory Division), a diferença no desempenho é mínima, ocorre somente flutuação no tempo, pois o aglomerado utiliza a mesma arquitetura de computadores e GPUs (FX1800). Desta forma é possível notar que quando o tamanho da matriz é pequena, não existe a vantagem de executar em vários nós, pois a troca de mensagens entre os nós e a distribuição dos dados consome um tempo maior do que a execução em um computador. A partir do teste com a matriz (640 x 640), o tempo de execução melhora consideravelmente quando aumenta o número de nós, mostrando que a distribuição dos dados entre nós e o processamento nas GPUs se torna vantajoso. Desta forma a biblioteca de suporte para aglomerados de GPUs cumpre sua função de distribuir os dados e processar na GPU. Também traz ganho considerável de desempenho ao aumentar o número de nós para executar a multiplicação de matrizes de tamanho médio ou grande Subsequência Máxima O teste da Subsequência Máxima com a Arquitetura 1 no tamanho do vetor final de 20480, apresenta uma tabela e gráfico Figura 5.3 com o tempo de execução dos diferentes critérios para divisão dos dados usando número de nós diferentes. Na tabela também é possível verificar o speedup do tempo original / tempo de outros processos (speedup x). Tabela 5.5: Tempo em segundos VS Número de nós executando Subsequência Máxima Arquitetura 1(homogêneos) Número de nós Core Division SM Division Memory Division Linear Division 1 6,963 6,97 6,981 6, ,943 (1,002x) 6,653 (1,047x) 6,974 (1,001x) 6,781 (1,029x) 4 6,658 (1,045x) 6,705 (1,039x) 6,721 (1,038x) 6,806 (1,025x) 8 6,701 (1,039x) 6,718 (1,037x) 6,706 (1,041x) 6,703 (1,041x) A tabela e o gráfico mostra que não houve ganho considerável de desempenho ao aumentar o número de nós na Arquitetura 1(homogêneos). Os critérios para dividir e processar sofre somente uma flutuação no tempo pois utilizam a mesma arquitetura de computadores e GPUs. 60

61 Figura 5.1: Arquitetura 1: multiplicação de matrizes (64 x 64) e (640 x 640) Conclusões Após realizar todos os testes na Arquitetura 1, podemos concluir que na execução da multiplicação de matrizes, quando a mesma possui um tamanho pequeno, não é vantajoso realizar a distribuição e processamento dos dados na GPU, pois a cópia para memória da GPU e a troca de mensagens entre os nós consome um tempo maior do que a execução em uma CPU. A partir do tamanho da matriz de (640 x 640), o tempo de execução melhora consideravelmente quando aumenta o número de nós no aglomerado. Entre os critérios de divisão para processamento na GPU (Linear, Core, SM e Memory Division), a diferença é mínima e a variação no desempenho é muito similar, ocorre somente flutuação no tempo, pois utilizam a mesma arquitetura de computadores e GPUs (Quadro FX1800 com 64 61

62 Figura 5.2: Arquitetura 1: multiplicação de matrizes (1280 x 1280) e (5000 x 5000). CUDA Core, 8 SM e 768 MB). No ambiente homogêneo os critérios de divisão possuem o mesmo tempo de execução na GPU quando executam o mesmo tamanho da matriz. Um exemplo que podemos citar para explicar porque isto acontece é contar o tempo que a GPU gasta para processar os dados no kernel. No teste realizado com dois nós no aglomerado e com GPUs do mesmo modelo (homogêneos), a execução da matriz (640 x 640) possui o mesmo tempo para todos os critérios de divisão escolhidos (Linear, Memory, SM, Core), obtendo uma média igual para processar os dados em 0,5 segundos em cada GPU. Na matriz(1280 x 1280) possuem média de 3.6 segundos para processar em cada GPU e matriz (5000 x 5000) com o tempo similar de segundos ou 6m minutos. Não importa o critério escolhido, a GPU sempre processa os dados com o mesmo tempo em ambas GPUs que são do mesmo modelo (FX1800) e recebem a mesma porcentagem de dados. 62

63 Figura 5.3: Gráfico Arquitetura 1 para Subsequência Máxima. Já no teste realizado na subsequência máxima que utilizou o mesmo valor no vetor de entrada e vetor final, cumpriu sua função de distribuir e processar os dados mas não houve ganho considerável de desempenho ao aumentar o número de nós na Arquitetura 1. A computação que cada computador tem que fazer para processar a subsequência é pequena, utilizando o mesmo tamanho do vetor e causa maior tempo de comunicação e envio de dados do que o tempo de processamento na GPU. A biblioteca CCL é fácil de utilizar e pode ser adaptada em vários códigos, basta que utilize as funções para troca de dados entre os nós e pensar em um código para múltiplas GPUs e seus respectivos processos seguindo alguns passos que podem ser vistos no A. Para o experimento da multiplicação de matrizes foi preciso desenvolver a parte em C que utiliza o MPI para troca de dados entre os nós para acessar dados que estão na CPU e GPU. Este código inicializa a biblioteca, declara e chama funções para troca de dados entre os nós, aloca memória, chama o kernel e copia os resultados. Já o código CUDA da matriz contém o número de blocos, chamada de variáveis, alocação de memória na GPU, funções do kernel para processar na GPU e funções para liberar a memória. É possível desenvolver outras aplicações para utilizar a biblioteca assim como foi feito com a multiplicação de matrizes. Também é possível compilar aplicações que precisam de MPI/CUDA com um único Makefile, que também pode ser adaptado para outras aplicações. 63

64 A biblioteca se comporta como um processo do próprio MPI, pois o processo root envia e recebe os dados e outros processos também enviam. Pode-se apostar no sobretempo comum a troca de mensagens por conta do envio de dados. 5.2 Experimentos na Arquitetura Multiplicação de Matrizes O experimento da Multiplicação de Matrizes na Arquitetura 2 foi realizado com o tamanho (64 x 64), (640 x 640), (1280 x 1280) e (5000 x 5000). No experimento da Arquitetura 2 foi utilizado dois e oito nós com a seguintes configurações: O primeiro experimento com dois nós utiliza um computador T3500 com GPU NVS295 e outro computador T3500 com GPU FX1800. O experimento com oito nós utiliza dois computadores T3500 com GPU NVS295 e seis computadores T3500 com GPU FX1800. Apresenta uma tabela e gráficos Figura 5.4, Figura 5.5 com o tempo de execução dos diferentes critérios para divisão e processamento dos dados usando número de nós diferentes. Na tabela também é possível verificar o speedup do tempo original / tempo de outros processos. Tabela 5.6: Tempo em milissegundo VS Número de nós Arquitetura 2(2 e 8 computadores heterogêneos) Matriz: 64 x 64 Número de nós Core Division SM Division Memory Division Linear Division 2 0,266 0,267 0,283 0, ,259 (0,211x) 1,272 (0,209x) 1,473 (0,192x) 1,499 (0,193x) Tabela 5.7: Tempo em segundos VS Número de nós Arquitetura 2(2 e 8 nós heterogêneos) Matriz: 640 x 640 Número de nós Core Division SM Division Memory Division Linear Division 2 2,108 2,103 2,313 3, ,256 (1,678x) 1,263 (1,665x) 1,688 (1,370x) 2,828 (1,373x) Tabela 5.8: Tempo em segundos VS Número de nós Arquitetura 2(2 e 8 nós heterogêneos) Matriz: 1280 x 1280 Número de nós Core Division SM Division Memory Division Linear Division 2 7,205 7,177 9,403 20, ,596 (2,775x) 2,628 (2,730x) 3,268 (2,877x) 6,223 (3,210x) É possível observar no teste realizado na arquitetura heterogênea, que a biblioteca CCL cumpre sua função de processar os dados de acordo com o poder computacional das GPUs encontradas para obter melhor desempenho. Quando a escolha do critério de divisão foi SM, Core ou Memory 64

65 Tabela 5.9: Tempo em minutos VS Número de nós Arquitetura 2(heterogêneos) Matriz: 5000 x 5000 Número de nós Core Division SM Division Memory Division Linear Division 2 08:40:35 08:41:23 09:39:56 20:02: :11:23 (2,721x) 03:12:10 (2,713x) 03:42:52 (2,604x) 07:16:58 (2,753x) Division, a execução da multiplicação de matrizes se tornou vantajosa comparado a Linear Division, que divide e processa os dados igualmente pelo número de GPUs encontradas e não pelo seu poder computacional. Causa assim uma sobrecarga em GPUs com baixo poder computacional. Nos testes da multiplicação de matrizes realizados na Arquitetura 2, a pequena matriz (64 x 64) perde desempenho ao incluir mais nós na sua execução. Isto ocorre porque cada nó processa uma quantidade pequena de dados e não existe a vantagem de executar em vários nós, pois a troca de mensagens e a distribuição dos dados consome um tempo maior do que a execução em um computador. Nos resultados obtidos a partir da matriz (640 x 640) é possível verificar que quando aumenta o número de nós o desempenho melhora pois diminui o tempo de execução. Nota-se também que como existe nesta arquitetura GPUs de capacidade computacional diferentes, é possível obter ganho de desempenho ao escolher um critério de divisão que se comporta melhor do que o outro. O melhor desempenho ocorre no critério de divisão de dados SM Division e Core Division. O experimento com dois nós foi executado em um computador T3500 com GPU NVS295 (8 CUDA Core, 1 SM e 256 MB) e outro com GPU FX1800 (64 CUDA Core, 8 SM e 768 MB). Na divisão de dados é calculada a porcentagem do número total do critério escolhido, no caso de SM Division uma GPU possui 8 SM (FX1800) e outra 1 SM (NVS295), obtendo total de 9 SMs, então a GPU FX1800 possui 89% do total de SMs e NVS295 11% deste total. Os dados foram divididos 89% para a primeira GPU e 11% para a outra GPU. O mesmo critério de divisão é utilizado em Core Division e Memory Division porém, é calculado a porcentagem total de Cores e memória que cada GPU possui. Para a escolha Linear Division, os dados foram divididos metade para cada GPU, desta forma sobrecarrega a GPU NVS295 que possui baixa capacidade computacional e explica a perda de desempenho quando se escolhe este critério (Linear Division), pois a GPU FX1800 processa seus dados primeiro mas o processo root precisa esperar a parte que foi destinada à GPU NVS295 para sincronizar os dados e obter o resultado final. Já na escolha SM e Core Division para executar a aplicação, a GPU FX1800 fica com uma porcentagem maior de dados, pois sua quantidade de SM fica com 89% dos dados e Core 88%, aumenta assim o desempenho porque processa mais dados na GPU com melhor poder computacional. Na escolha Memory Division FX1800 fica com 75% dos dados, o que a torna melhor do que Linear Division. 65

66 Figura 5.4: Arquitetura 2: multiplicação de matrizes (64 x 64) e (640 x 640). O experimento com oito nós possui duas GPUs NVS295 (8 CUDA Core, 1 SM e 256 MB) e seis GPUs FX1800 (64 CUDA Core, 8 SM e 768 MB). Na escolha de SM e Core Division como critério de divisão dos dados, as seis GPUs FX1800 do aglomerado ficam com uma porcentagem de 96% para SM e 96% para CUDA Core para processar os dados, o que explica o melhor desempenho para estes critérios na execução da aplicação. Para Memory Division a porcentagem de dados é 90% e também se torna uma opção vantajosa. Já Linear Division divide os dados pela quantidade de GPUs (8 partes iguais) e causa um tempo maior para sua execução. Os testes de Core e SM apresentaram resultados iguais com dois ou oito nós, pois a escolha de qualquer um destes critérios utiliza a quantidade de CUDA Cores contida em seus SMs para processar os dados após a divisão. Recebem praticamente a mesma porcentagem de dados quando a escolha é Core ou SM Division, pois as GPus FX1800 e NVS295 possuem a mesma quantidade 66

67 Figura 5.5: Arquitetura 2: multiplicação de matrizes (1280 x 1280) e (5000 x 5000). de CUDA Core em cada SM. A opção Memory Division que fica com 75% dos dados para processar em uma GPU e 25 % em outra, apresentou 10 % a 12 % de lentidão comparado com SM Division que contém 89% dos dados para processar na FX1800. Foi possível analisar estes resultados com a função gettimeofday e cudadevicesynchronize() para verificar o tempo gasto na GPU para execução do kernel. A análise do tempo que será demonstrado na Figura 5.6 como exemplo, verifica o desempenho de uma GPU em dois nós (uma GPU FX1800 e outra NVS295): Para executar a aplicação da matriz (640 x 640) em Linear Division com dois nós, usando a função gettimeofday e cudadevicesynchronize() para calcular o tempo, foi gasto para inicializar a biblioteca 0.0 ms, depois precisa de 0.2 ms para encontrar as características das GPUs e encaminhar ao processo root. O tempo de transferência dos dados e 67

68 Figura 5.6: Tempo gasto na GPU para processar os dados divisão de acordo com poder computacional gasta 0.3 ms, depois processa os dados no kernel da GPU FX1800 em 0.5 ms e na NVS295 em 3.5 s. Obtendo o tempo total de execução de segundos. Assim a NVS295 é cerca de 7 vezes mais lenta do que a FX1800. A análise de tempo da matriz (1280 x 1280) usando Linear Division processa os dados no kernel da GPU FX1800 em 2.9 segundos e na NVS295 em 20.8 segundos, cerca de oito vezes mais lenta do que a FX1800. Obtendo tempo total de 21,326 segundos. Comparado com SM Division que precisou de 4.0 segundos na FX1800 para processar 88.8% dos dados e 6.5 segundos na NVS295 para processar 11.2% dos dados da matriz (1280 X 1280). Obtendo em SM Division o tempo total de execução de 6.9 segundos. É possível conhecer o tempo que a FX1800 gasta para processar os mesmos 11% de dados que foi delegado a NVS295 com o critério SM Division, basta para isso dividir 88.8% dos dados que foram atribuídos a FX1800 por 4 segundos, obtendo 22.2% por segundo ou 11.1% em 0.5 segundos. Desta forma é possível identificar que a NVS295 foi cerca de 12 vezes mais lenta do que a FX1800 ao processar a mesma quantidade de dados. A análise de tempo da matriz (5000 x 5000) em Linear Division processa os dados no kernel da GPU FX1800 em segundos e na NVS295 em segundos, quase oito vezes 68

69 mais lento do que a FX1800. Obtendo o tempo total de 1205 seg. ou 20m05,156 minutos, que contém o tempo restante que se gasta para transferir os dados, sincronizar e colher os resultados dos processos. Comparado com Memory Division que gastou o tempo de segundos na FX1800 para processar 75% dos dados e segundos na NVS295 para processar 25% dos dados. É possível conhecer o tempo que a FX1800 gasta para processar os mesmos 25% de dados que foi delegado a NVS295 com o critério Memory Division, basta para isso dividir segundos por 3 para obter o tempo gasto em 25% dos dados, obtendo assim 69.4 segundos, cerca de 8 vezes mais rápida do que a NVS295 que processa na GPU a mesma quantidade de dados em segundos. No ambiente heterogêneo Linear Division sempre obteve o pior desempenho influenciado pela GPU de baixo poder computacional. É possível também observar que na Arquitetura 2 (heterogêneos), o tempo para executar uma matriz a partir de (640 x 640) é maior comparado ao tempo de execução da Arquitetura 1 (homogêneos). Isto ocorre pois a Arquitetura 1 contém oito GPUs FX1800 e a Arquitetura 2 contém somente seis destas GPUs e outras duas placas NVS295 de menor poder computacional, ocasiona assim sobretempo quando se executa a mesma matriz. O experimento realizado na Arquitetura 2 se mostra eficiente quando aumenta o número de nós e também na escolha correta do critério de divisão, mas devido a pouca capacidade computacional de duas GPUs, esta eficiência sofre uma influência negativa no desempenho, pois atrasa o processamento e envio de dados Subsequência Máxima O teste da Subsequência Máxima utilizando a Arquitetura 2 no tamanho do vetor final de 20480, apresenta uma tabela e gráfico Figura 5.7 com o tempo de execução dos diferentes critérios para divisão dos dados usando número de nós diferentes. Na tabela também é possível verificar o speedup do tempo original / tempo de outros processos (speedup x). Tabela 5.10: Tempo em segundos VS Número de nós executando Subsequência Máxima Arquitetura 2(heterogêneos) Número de nós Core Division SM Division Memory Division Linear Division 2 6,129 6,132 6,122 6, ,398 (0,957x) 6,321 (0,970x) 6,438 (0,950x) 6,806 (0,970x) Na execução da subsequência máxima pela Arquitetura 2 é possivel notar que quando aumenta o número de nós não ocorre ganho de desempenho. Entre os critérios de divisão a diferença é minima, mas é possível notar que SM, Core e Memory possui uma pequena vantagem de desempenho 69

70 Figura 5.7: Gráfico Arquitetura 2 para Subsequência Máxima. comparado a Linear Division. A biblioteca CCL cumpre sua função de distribuir os dados e processar na GPU mas não houve ganho de desempenho ao adaptar o código de Luz(2013) [4] para ser executado no aglomerado Conclusões Após os testes realizados na Arquitetura 2, concluímos que GPUs de pouca capacidade computacional ou muito inferior podem causar influência negativa no desempenho, pois atrasa o processamento dos dados e envio do resultado para o processo root. Os testes na Arquitetura 2 mostram que a matriz (64 x 64) perde desempenho ao incluir mais nós no aglomerado, pois cada nó processa uma quantidade pequena de dados e não ocorre a vantagem de executar em vários nós porque a troca de mensagens e a distribuição dos dados consome um tempo maior do que a execução em um computador. Quando o tamanho da matriz é igual ou maior (640 x 640), pode-se obter um ganho considerável de desempenho quando aumenta o número de nós no aglomerado e seleciona o melhor critério para dividir e processar os dados. Houve diferença significativa de desempenho na multiplicação de matrizes ao modificar os diferentes critérios de divisão (Linear, Core, SM, Memory) e o maior desempenho esta para SM Division e Core Division que ficam com uma porcentagem maior de dados na GPU de melhor poder computacional. No ambiente heterogêneo é importante escolher o melhor critério de divisão para aproveitar o poder computacional das GPUs disponíveis, pois a porcentagem de dados sempre será diferente entre os diferentes critérios. A melhor escolha para o usuário pode ser SM, Core, Memory dependendo da quantidade que possui cada GPU. 70

71 A biblioteca CCL pode dividir os dados entre os nós, processar na GPU mais potente e ser adaptada em muitos códigos para o ambiente heterogêneo, basta que utilize as funções para troca de dados entre os nós e pensar no código para múltiplas GPUs e seus respectivos processos. É possível também compilar estas aplicações MPI/CUDA com um único Makefile, que também pode ser adaptado para outras aplicações. Desta forma a biblioteca de suporte para aglomerados de GPUs cumpre sua função de distribuir os dados da matriz e processar o resultado na GPU. Já no teste realizado para subsequência máxima que utilizou o mesmo valor no vetor de entrada e vetor final, cumpriu sua função de distribuir os dados e processar o resultado mas não houve ganho considerável de desempenho ao aumentar o número de nós na Arquitetura 2(heterogêneos). A computação que cada computador tem que fazer para processar a subsequência é pequena, ao utilizar o mesmo tamanho do vetor, o que causa maior tempo na comunicação e envio de dados do que o tempo de processamento na GPU. Mesmo incluindo novos nós na execução da subsequência, o tempo de comunicação e envio de dados através da troca de mensagens faz com que cada nó demore muito para receber sua parte do vetor e comparar os valores com os outros nós para processar e obter o vetor final. Desta forma o tempo de processamento nas GPUs teria que ser melhor, com GPUs de maior poder computacional para compensar a perca de tempo de comunicação entre os nós. A biblioteca CCL se comporta como um processo do próprio MPI, pois o processo root envia e recebe os dados e outros processos também enviam, desta forma é possível dizer que existe um sobretempo comum a troca de mensagens por conta do envio de dados. 5.3 Experimentos na Arquitetura Multiplicação de Matrizes O teste da Multiplicação de Matrizes na Arquitetura 3 foi realizado com o tamanho (64 x 64), (640 x 640), (1280 x 1280) e (5000 x 5000). Apresenta uma tabela e gráficos Figura 5.8, Figura 5.9 com o tempo de execução dos diferentes critérios para divisão e processamento dos dados. Tabela 5.11: Tempo em milissegundo do micro Z800 (Arquitetura 3) Tamanho da matriz: 64 x 64 Micro Z800(2 gpus Quadro 4000) Core Division SM Division Memory Division Linear Division 1,278 1,308 1,254 1,138 Nos testes realizados na Arquitetura 3 (duas GPUs Quadro 4000), é possível notar alguns resultados importantes: 71

72 Tabela 5.12: Tempo em segundos do micro Z800 (Arquitetura 3) Tamanho da matriz: 640 x 640 Micro Z800(2 gpus Quadro 4000) Core Division SM Division Memory Division Linear Division 1,754 1,856 1,842 1,759 Tabela 5.13: Tempo em segundos do micro Z800 (Arquitetura 3) Tamanho da matriz: 1280 x 1280 Micro Z800(2 gpus Quadro 4000) Core Division SM Division Memory Division Linear Division 3,526 3,49 3,507 3,504 O tempo de execução da pequena matriz (64 x 64) nesta arquitetura com duas GPUs de maior poder computacional se mostrou mais lenta do que a execução da matriz de mesmo tamanho na Arquitetura 1 e 2. O tempo de execução da matriz (640 x 640) se mostrou mais eficiente do que dois nós da Arquitetura 1 e similar a oito nós da Arquitetura 2. O tempo de execução da matriz (1280 x 1280) se mostrou mais eficiente do que dois nós da Arquitetura 1 e dois nós da Arquitetura 2. O tempo de execução da matriz (5000 x 5000) se mostrou mais eficiente do que dois nós da Arquitetura 1 e dois nós da Arquitetura 2. É possível verificar que o desempenho da Arquitetura 3 para executar uma matriz superior ou igual (640 x 640), se mostrou mais eficiente do que executar em alguns nós de outras arquiteturas que utilizam GPUs com capacidade computacional inferior. Isto ocorreu pois a Arquitetura 3 possui duas GPUs modelo Quadro 4000 com maior número de CUDA Core (256), 8 SMs, 2 GB de memória e um computador com melhor processador (Six Core 3,46 GHz) e 24 gb de RAM. Influencia positivamente nos resultados quando a divisão dos critérios utiliza os recursos para dividir o dados entre as threads e processar na GPU. Desta forma a execução neste computador com melhor processador, memória RAM e duas GPUs de maior poder computacional, pode obter melhor desempenho em alguns casos do que realizar a execução no aglomerado com até quatro nós das arquiteturas testadas anteriormente. Já no aglomerado da Arquitetura 1 com oito nós se mostrou mais eficiente do que o computador com Tabela 5.14: Tempo em minutos do micro Z800 (Arquitetura 3) Tamanho da matriz: 5000 x 5000 Micro Z800(2 gpus Quadro 4000) Core Division SM Division Memory Division Linear Division 04:41:12 04:41:16 04:41:06 04:40:58 72

73 Figura 5.8: Gráfico micro Z800 (Arquitetura 3): multiplicação de matrizes 64 x 64 e 640 x 640 duas GPUs (Arquitetura 3). Nota-se que na arquitetura que possui duas GPUs de maior poder computacional, a execução da matriz (64 x 64) se tornou mais lenta comparado ao teste nas outras arquiteturas, isto ocorreu pois o tempo que se leva para distribuir os dados entre GPUs e processar uma quantidade pequena de dados, causa um sobretempo desnecessário para troca de mensagens na execução Subsequência Máxima O teste da Subsequência Máxima utilizando a Arquitetura 3 no tamanho do vetor final de 20480, apresenta uma tabela e gráfico na Figura 5.10 com o tempo de execução dos diferentes critérios para divisão dos dados. 73

74 Figura 5.9: Gráfico micro Z800 (Arquitetura 3): multiplicação de matrizes 1280 x 1280 e 5000 x 5000 Tabela 5.15: Tempo em segundos micro Z800 (Arquitetura 3) Subsequência Máxima Micro Z800(2 gpus Quadro 4000) Core Division SM Division Memory Division Linear Division Tempo 6,732 6,831 6,864 6,862 Figura 5.10: Gráfico Arquitetura 3 Subsequência Máxima. 74

Arquitetura e Programação de GPU. Leandro Zanotto RA: 001962 Anselmo Ferreira RA: 023169 Marcelo Matsumoto RA: 085973

Arquitetura e Programação de GPU. Leandro Zanotto RA: 001962 Anselmo Ferreira RA: 023169 Marcelo Matsumoto RA: 085973 Arquitetura e Programação de GPU Leandro Zanotto RA: 001962 Anselmo Ferreira RA: 023169 Marcelo Matsumoto RA: 085973 Agenda Primeiras Placas de Vídeo Primeira GPU Arquitetura da GPU NVIDIA Arquitetura

Leia mais

IFPE. Disciplina: Sistemas Operacionais. Prof. Anderson Luiz Moreira

IFPE. Disciplina: Sistemas Operacionais. Prof. Anderson Luiz Moreira IFPE Disciplina: Sistemas Operacionais Prof. Anderson Luiz Moreira SERVIÇOS OFERECIDOS PELOS SOS 1 Introdução O SO é formado por um conjunto de rotinas (procedimentos) que oferecem serviços aos usuários

Leia mais

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064 Sistemas Distribuídos Professora: Ana Paula Couto DCC 064 Processos- Clientes, Servidores, Migração Capítulo 3 Agenda Clientes Interfaces de usuário em rede Sistema X Window Software do lado cliente para

Leia mais

Sistema Operacional Correção - Exercício de Revisão

Sistema Operacional Correção - Exercício de Revisão Prof. Kleber Rovai 1º TSI 22/03/2012 Sistema Operacional Correção - Exercício de Revisão 1. Como seria utilizar um computador sem um sistema operacional? Quais são suas duas principais funções? Não funcionaria.

Leia mais

Hardware (Nível 0) Organização. Interface de Máquina (IM) Interface Interna de Microprogramação (IIMP)

Hardware (Nível 0) Organização. Interface de Máquina (IM) Interface Interna de Microprogramação (IIMP) Hardware (Nível 0) Organização O AS/400 isola os usuários das características do hardware através de uma arquitetura de camadas. Vários modelos da família AS/400 de computadores de médio porte estão disponíveis,

Leia mais

Prof. Marcos Ribeiro Quinet de Andrade Universidade Federal Fluminense - UFF Pólo Universitário de Rio das Ostras - PURO

Prof. Marcos Ribeiro Quinet de Andrade Universidade Federal Fluminense - UFF Pólo Universitário de Rio das Ostras - PURO Conceitos básicos e serviços do Sistema Operacional Prof. Marcos Ribeiro Quinet de Andrade Universidade Federal Fluminense - UFF Pólo Universitário de Rio das Ostras - PURO Tipos de serviço do S.O. O S.O.

Leia mais

Sistemas Operacionais Gerência de Dispositivos

Sistemas Operacionais Gerência de Dispositivos Universidade Estadual de Mato Grosso do Sul UEMS Curso de Licenciatura em Computação Sistemas Operacionais Gerência de Dispositivos Prof. José Gonçalves Dias Neto profneto_ti@hotmail.com Introdução A gerência

Leia mais

1 http://www.google.com

1 http://www.google.com 1 Introdução A computação em grade se caracteriza pelo uso de recursos computacionais distribuídos em várias redes. Os diversos nós contribuem com capacidade de processamento, armazenamento de dados ou

Leia mais

PARALELIZAÇÃO DE APLICAÇÕES NA ARQUITETURA CUDA: UM ESTUDO SOBRE VETORES 1

PARALELIZAÇÃO DE APLICAÇÕES NA ARQUITETURA CUDA: UM ESTUDO SOBRE VETORES 1 PARALELIZAÇÃO DE APLICAÇÕES NA ARQUITETURA CUDA: UM ESTUDO SOBRE VETORES 1 DUTRA, Evandro Rogério Fruhling 2 ; VARINI, Andre Luis 2 ; CANAL, Ana Paula 2 1 Trabalho de Iniciação Científica _UNIFRA 2 Ciência

Leia mais

Processos e Threads (partes I e II)

Processos e Threads (partes I e II) Processos e Threads (partes I e II) 1) O que é um processo? É qualquer aplicação executada no processador. Exe: Bloco de notas, ler um dado de um disco, mostrar um texto na tela. Um processo é um programa

Leia mais

O hardware é a parte física do computador, como o processador, memória, placamãe, entre outras. Figura 2.1 Sistema Computacional Hardware

O hardware é a parte física do computador, como o processador, memória, placamãe, entre outras. Figura 2.1 Sistema Computacional Hardware 1 2 Revisão de Hardware 2.1 Hardware O hardware é a parte física do computador, como o processador, memória, placamãe, entre outras. Figura 2.1 Sistema Computacional Hardware 2.1.1 Processador O Processador

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Aula 6 Estrutura de Sistemas Operacionais Prof.: Edilberto M. Silva http://www.edilms.eti.br Baseado no material disponibilizado por: SO - Prof. Edilberto Silva Prof. José Juan Espantoso

Leia mais

ICORLI. INSTALAÇÃO, CONFIGURAÇÃO e OPERAÇÃO EM REDES LOCAIS e INTERNET

ICORLI. INSTALAÇÃO, CONFIGURAÇÃO e OPERAÇÃO EM REDES LOCAIS e INTERNET INSTALAÇÃO, CONFIGURAÇÃO e OPERAÇÃO EM REDES LOCAIS e INTERNET 2010/2011 1 Protocolo TCP/IP É um padrão de comunicação entre diferentes computadores e diferentes sistemas operativos. Cada computador deve

Leia mais

Sistemas Operacionais Processos e Threads

Sistemas Operacionais Processos e Threads Sistemas Operacionais Processos e Threads Prof. Marcos Monteiro, MBA http://www.marcosmonteiro.com.br contato@marcosmonteiro.com.br 1 Estrutura de um Sistema Operacional 2 GERÊNCIA DE PROCESSOS Um processo

Leia mais

Um Driver NDIS Para Interceptação de Datagramas IP

Um Driver NDIS Para Interceptação de Datagramas IP Um Driver NDIS Para Interceptação de Datagramas IP Paulo Fernando da Silva psilva@senior.com.br Sérgio Stringari stringari@furb.br Resumo. Este artigo apresenta o desenvolvimento de um driver NDIS 1 para

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Sistemas Operacionais Prof. Marcelo Sabaris Carballo Pinto Gerenciamento de Dispositivos Gerenciamento de Dispositivos de E/S Introdução Gerenciador de Dispositivos Todos os dispositivos

Leia mais

Sistemas Distribuídos

Sistemas Distribuídos Sistemas Distribuídos Modelo Cliente-Servidor: Introdução aos tipos de servidores e clientes Prof. MSc. Hugo Souza Iniciando o módulo 03 da primeira unidade, iremos abordar sobre o Modelo Cliente-Servidor

Leia mais

Tecnologia PCI express. Introdução. Tecnologia PCI Express

Tecnologia PCI express. Introdução. Tecnologia PCI Express Tecnologia PCI express Introdução O desenvolvimento de computadores cada vez mais rápidos e eficientes é uma necessidade constante. No que se refere ao segmento de computadores pessoais, essa necessidade

Leia mais

Capacidade = 512 x 300 x 20000 x 2 x 5 = 30.720.000.000 30,72 GB

Capacidade = 512 x 300 x 20000 x 2 x 5 = 30.720.000.000 30,72 GB Calculando a capacidade de disco: Capacidade = (# bytes/setor) x (méd. # setores/trilha) x (# trilhas/superfície) x (# superfícies/prato) x (# pratos/disco) Exemplo 01: 512 bytes/setor 300 setores/trilha

Leia mais

Organização e Arquitetura de Computadores I. de Computadores

Organização e Arquitetura de Computadores I. de Computadores Universidade Federal de Campina Grande Departamento de Sistemas e Computação Curso de Bacharelado em Ciência da Computação Organização e Arquitetura de I Organização Básica B de (Parte V, Complementar)

Leia mais

Cálculo Aproximado do número PI utilizando Programação Paralela

Cálculo Aproximado do número PI utilizando Programação Paralela Universidade de São Paulo Instituto de Ciências Matemáticas e de Computação Cálculo Aproximado do número PI utilizando Programação Paralela Grupo 17 Raphael Ferras Renan Pagaiane Yule Vaz SSC-0143 Programação

Leia mais

TRABALHO COM GRANDES MONTAGENS

TRABALHO COM GRANDES MONTAGENS Texto Técnico 005/2013 TRABALHO COM GRANDES MONTAGENS Parte 05 0 Vamos finalizar o tema Trabalho com Grandes Montagens apresentando os melhores recursos e configurações de hardware para otimizar a abertura

Leia mais

MANUTENÇÃO DE MICRO. Mário Gomes de Oliveira

MANUTENÇÃO DE MICRO. Mário Gomes de Oliveira MANUTENÇÃO DE MICRO Mário Gomes de Oliveira 1 IRQ Pedido de atenção e de serviço feito à CPU, para notificar a CPU sobre a necessidade de tempo de processamento. 2 IRQ (Interrupt Request line ou Linha

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Gerência de processos Controle e descrição de processos Edson Moreno edson.moreno@pucrs.br http://www.inf.pucrs.br/~emoreno Sumário Representação e controle de processos pelo SO Estrutura

Leia mais

Infra-Estrutura de Software. Introdução. (cont.)

Infra-Estrutura de Software. Introdução. (cont.) Infra-Estrutura de Software Introdução (cont.) O que vimos Complexidade do computador moderno, do ponto de vista do hardware Necessidade de abstrações software Sistema computacional em camadas SO como

Leia mais

Sistemas Distribuídos Capítulos 3 e 4 - Aula 4

Sistemas Distribuídos Capítulos 3 e 4 - Aula 4 Sistemas Distribuídos Capítulos 3 e 4 - Aula 4 Aula passada Threads Threads em SDs Processos Clientes Processos Servidores Aula de hoje Clusters de Servidores Migração de Código Comunicação (Cap. 4) Fundamentos

Leia mais

Estruturas do Sistema de Computação

Estruturas do Sistema de Computação Estruturas do Sistema de Computação Prof. Dr. José Luís Zem Prof. Dr. Renato Kraide Soffner Prof. Ms. Rossano Pablo Pinto Faculdade de Tecnologia de Americana Centro Paula Souza Estruturas do Sistema de

Leia mais

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA Responda 1) Quem desenvolveu a linguagem C? Quando? 2) Existe alguma norma sobre a sintaxe da linguagem C? 3) Quais são os tipos básicos de dados disponíveis na linguagem C? 4) Quais são as principais

Leia mais

SISTEMAS OPERACIONAIS CAPÍTULO 3 CONCORRÊNCIA

SISTEMAS OPERACIONAIS CAPÍTULO 3 CONCORRÊNCIA SISTEMAS OPERACIONAIS CAPÍTULO 3 CONCORRÊNCIA 1. INTRODUÇÃO O conceito de concorrência é o princípio básico para o projeto e a implementação dos sistemas operacionais multiprogramáveis. O sistemas multiprogramáveis

Leia mais

Introdução aos Computadores

Introdução aos Computadores Os Computadores revolucionaram as formas de processamento de Informação pela sua capacidade de tratar grandes quantidades de dados em curto espaço de tempo. Nos anos 60-80 os computadores eram máquinas

Leia mais

Capítulo 8 Arquitetura de Computadores Paralelos

Capítulo 8 Arquitetura de Computadores Paralelos Capítulo 8 Arquitetura de Computadores Paralelos Necessidade de máquinas com alta capacidade de computação Aumento do clock => alta dissipação de calor Velocidade limitada dos circuitos => velocidade da

Leia mais

Aula 3. Sistemas Operacionais. Prof: Carlos Eduardo de Carvalho Dantas (carloseduardoxpto@gmail.com) http://carloseduardoxp.wordpress.

Aula 3. Sistemas Operacionais. Prof: Carlos Eduardo de Carvalho Dantas (carloseduardoxpto@gmail.com) http://carloseduardoxp.wordpress. Sistemas Operacionais Aula 3 Prof: Carlos Eduardo de Carvalho Dantas (carloseduardoxpto@gmail.com) http://carloseduardoxp.wordpress.com Nunca cone em um computador que você não pode jogar pela janela.

Leia mais

ESTUDO DE CASO WINDOWS VISTA

ESTUDO DE CASO WINDOWS VISTA ESTUDO DE CASO WINDOWS VISTA História Os sistemas operacionais da Microsoft para PCs desktop e portáteis e para servidores podem ser divididos em 3 famílias: MS-DOS Windows baseado em MS-DOS Windows baseado

Leia mais

Prof.: Roberto Franciscatto. Capítulo 1.1 Introdução

Prof.: Roberto Franciscatto. Capítulo 1.1 Introdução Sistemas Operacionais Prof.: Roberto Franciscatto Capítulo 1.1 Introdução Tipos de Sistemas Operacionais Sistemas Monoprogramáveis / Monotarefa Voltados tipicamente para a execução de um único programa.

Leia mais

29/3/2011. Primeira unidade de execução (pipe U): unidade de processamento completa, capaz de processar qualquer instrução;

29/3/2011. Primeira unidade de execução (pipe U): unidade de processamento completa, capaz de processar qualquer instrução; Em 1993, foi lançada a primeira versão do processador Pentium, que operava a 60 MHz Além do uso otimizado da memória cache (tecnologia já amadurecida) e da multiplicação do clock, o Pentium passou a utilizar

Leia mais

BACHARELADO EM SISTEMAS DE INFORMAÇÃO EaD UAB/UFSCar Sistemas de Informação - prof. Dr. Hélio Crestana Guardia

BACHARELADO EM SISTEMAS DE INFORMAÇÃO EaD UAB/UFSCar Sistemas de Informação - prof. Dr. Hélio Crestana Guardia O Sistema Operacional que você usa é multitasking? Por multitasking, entende-se a capacidade do SO de ter mais de um processos em execução ao mesmo tempo. É claro que, num dado instante, o número de processos

Leia mais

Gerência de Memória RAM em Computadores com Mais de 4GB O sistema Windows x86 (32bits) não tem capacidade de reconhecer, fisicamente, mais que 3,X GB de RAM, a não ser que seja ativado, manualmente, o

Leia mais

SISTEMAS OPERACIONAIS ABERTOS Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com

SISTEMAS OPERACIONAIS ABERTOS Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com - Aula 2-1. PRINCÍPIOS DE SOFTWARE DE ENTRADA E SAÍDA (E/S) As metas gerais do software de entrada e saída é organizar o software como uma série de camadas, com as mais baixas preocupadas em esconder as

Leia mais

Parte da Tarefa. Parte da Tarefa. Parte da Tarefa SEND RECEIVE SEND RECEIVE

Parte da Tarefa. Parte da Tarefa. Parte da Tarefa SEND RECEIVE SEND RECEIVE Produto Escalar com MPI-2 (C++) Aula Sistemas Distribuídos Prof. Dr. Marcelo Facio Palin profpalin@gmail.com 1 1 O que é Paralelismo? Conceitos Paralelismo é uma técnica usada em tarefas grandes e complexas

Leia mais

CAPÍTULO 2 CARACTERÍSTICAS DE E/S E PORTA PARALELA

CAPÍTULO 2 CARACTERÍSTICAS DE E/S E PORTA PARALELA 8 CAPÍTULO 2 CARACTERÍSTICAS DE E/S E PORTA PARALELA A porta paralela, também conhecida por printer port ou Centronics e a porta serial (RS-232) são interfaces bastante comuns que, apesar de estarem praticamente

Leia mais

3. Arquitetura Básica do Computador

3. Arquitetura Básica do Computador 3. Arquitetura Básica do Computador 3.1. Modelo de Von Neumann Dar-me-eis um grão de trigo pela primeira casa do tabuleiro; dois pela segunda, quatro pela terceira, oito pela quarta, e assim dobrando sucessivamente,

Leia mais

1.1. Organização de um Sistema Computacional

1.1. Organização de um Sistema Computacional 1. INTRODUÇÃO 1.1. Organização de um Sistema Computacional Desde a antiguidade, o homem vem desenvolvendo dispositivos elétricoeletrônicos (hardware) que funciona com base em instruções e que são capazes

Leia mais

Sistemas Computacionais II Professor Frederico Sauer

Sistemas Computacionais II Professor Frederico Sauer Sistemas Computacionais II Professor Frederico Sauer Livro-texto: Introdução à Organização de Computadores 4ª edição Mário A. Monteiro Livros Técnicos e Científicos Editora. Atenção: Este material não

Leia mais

Notas da Aula 15 - Fundamentos de Sistemas Operacionais

Notas da Aula 15 - Fundamentos de Sistemas Operacionais Notas da Aula 15 - Fundamentos de Sistemas Operacionais 1. Software de Entrada e Saída: Visão Geral Uma das tarefas do Sistema Operacional é simplificar o acesso aos dispositivos de hardware pelos processos

Leia mais

Comparativo de desempenho do Pervasive PSQL v11

Comparativo de desempenho do Pervasive PSQL v11 Comparativo de desempenho do Pervasive PSQL v11 Um artigo Pervasive PSQL Setembro de 2010 Conteúdo Resumo executivo... 3 O impacto das novas arquiteturas de hardware nos aplicativos... 3 O projeto do Pervasive

Leia mais

SIMULADOR DE ROTEAMENTO DE PACOTES (V. 3 20/05/2010)

SIMULADOR DE ROTEAMENTO DE PACOTES (V. 3 20/05/2010) SIMULADOR DE ROTEAMENTO DE PACOTES (V. 3 20/05/2010) OBJETIVO GERAL Este trabalho possui o objetivo de exercitar a lógica de programação dos alunos do Terceiro ano do Curso de BSI e também desenvolver

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Gerência de Arquivos Edson Moreno edson.moreno@pucrs.br http://www.inf.pucrs.br/~emoreno Sumário Conceituação de arquivos Implementação do sistemas de arquivo Introdução Sistema de

Leia mais

Programação Concorrente Processos e Threads

Programação Concorrente Processos e Threads Programação Concorrente Processos e Threads Prof. Eduardo Alchieri Processos O conceito mais central em qualquer sistema operacional é o processo Uma abstração de um programa em execução Um programa por

Leia mais

Programação Paralela com Troca de Mensagens. Profa Andréa Schwertner Charão DLSC/CT/UFSM

Programação Paralela com Troca de Mensagens. Profa Andréa Schwertner Charão DLSC/CT/UFSM Programação Paralela com Troca de Mensagens Profa Andréa Schwertner Charão DLSC/CT/UFSM Sumário Modelo de programa MPI Comunicação em MPI Comunicadores Mensagens Comunicação ponto-a-ponto Comunicação coletiva

Leia mais

PROCESSO DE DESENVOLVIMENTO DE SOFTWARE. Modelos de Processo de Desenvolvimento de Software

PROCESSO DE DESENVOLVIMENTO DE SOFTWARE. Modelos de Processo de Desenvolvimento de Software PROCESSO DE DESENVOLVIMENTO DE SOFTWARE Introdução Modelos de Processo de Desenvolvimento de Software Os modelos de processos de desenvolvimento de software surgiram pela necessidade de dar resposta às

Leia mais

Visão Geral de Sistemas Operacionais

Visão Geral de Sistemas Operacionais Visão Geral de Sistemas Operacionais Sumário Um sistema operacional é um intermediário entre usuários e o hardware do computador. Desta forma, o usuário pode executar programas de forma conveniente e eficiente.

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Aula 13 Gerência de Memória Prof.: Edilberto M. Silva http://www.edilms.eti.br Baseado no material disponibilizado por: SO - Prof. Edilberto Silva Prof. José Juan Espantoso Sumário

Leia mais

Placa de vídeo em CUDA

Placa de vídeo em CUDA Placa de vídeo em CUDA Matheus Costa Leone de Souza Krystian Aparacido Resumo Quando você tem um cálculo que possa ser grande demais para você realizar a mão, a primeira solução que lhe vem a cabeça é

Leia mais

ISO/IEC 12207: Gerência de Configuração

ISO/IEC 12207: Gerência de Configuração ISO/IEC 12207: Gerência de Configuração Durante o processo de desenvolvimento de um software, é produzida uma grande quantidade de itens de informação que podem ser alterados durante o processo Para que

Leia mais

Sistemas Operacionais. Prof. André Y. Kusumoto andrekusumoto.unip@gmail.com

Sistemas Operacionais. Prof. André Y. Kusumoto andrekusumoto.unip@gmail.com Sistemas Operacionais Prof. André Y. Kusumoto andrekusumoto.unip@gmail.com Estruturas de Sistemas Operacionais Um sistema operacional fornece o ambiente no qual os programas são executados. Internamente,

Leia mais

Programação Engenharia Informática (11543) 1º ano, 1º semestre Tecnologias e Sistemas de Informação (6619) 1º ano, 1º semestre

Programação Engenharia Informática (11543) 1º ano, 1º semestre Tecnologias e Sistemas de Informação (6619) 1º ano, 1º semestre Programação Engenharia Informática (11543) 1º ano, 1º semestre Tecnologias e Sistemas de Informação (6619) 1º ano, 1º semestre Cap. 01 Fundamentos de Computadores Sumário : Conceitos básicos: computador,

Leia mais

O que veremos nesta aula? Principais Aspectos de Sistemas Operacionais. Visão geral de um sistema computacional

O que veremos nesta aula? Principais Aspectos de Sistemas Operacionais. Visão geral de um sistema computacional O que veremos nesta aula? Principais Aspectos de Sistemas Operacionais Laboratório de Sistemas Operacionais Aula 1 Flávia Maristela (flavia@flaviamaristela.com) Tudo o que já vimos antes... Introdução

Leia mais

Processamento de Dados

Processamento de Dados Processamento de Dados Execução de Programas Os computadores não entendem nada além de comandos, dados e endereços escritos em linguagem binária, também chamada de linguagem de baixo nível. Ela utiliza

Leia mais

Capítulo 4. MARIE (Machine Architecture Really Intuitive and Easy)

Capítulo 4. MARIE (Machine Architecture Really Intuitive and Easy) Capítulo 4 João Lourenço Joao.Lourenco@di.fct.unl.pt Faculdade de Ciências e Tecnologia Universidade Nova de Lisboa 2007-2008 MARIE (Machine Architecture Really Intuitive and Easy) Adaptado dos transparentes

Leia mais

Software de segurança em redes para monitoração de pacotes em uma conexão TCP/IP

Software de segurança em redes para monitoração de pacotes em uma conexão TCP/IP Software de segurança em redes para monitoração de pacotes em uma conexão TCP/IP Paulo Fernando da Silva psilva@senior.com.br Sérgio Stringari stringari@furbbr Resumo. Este artigo apresenta a especificação

Leia mais

BARRAMENTO DO SISTEMA

BARRAMENTO DO SISTEMA BARRAMENTO DO SISTEMA Memória Principal Processador Barramento local Memória cachê/ ponte Barramento de sistema SCSI FireWire Dispositivo gráfico Controlador de vídeo Rede Local Barramento de alta velocidade

Leia mais

Capítulo 3. Avaliação de Desempenho. 3.1 Definição de Desempenho

Capítulo 3. Avaliação de Desempenho. 3.1 Definição de Desempenho 20 Capítulo 3 Avaliação de Desempenho Este capítulo aborda como medir, informar e documentar aspectos relativos ao desempenho de um computador. Além disso, descreve os principais fatores que influenciam

Leia mais

AULA 5 Sistemas Operacionais

AULA 5 Sistemas Operacionais AULA 5 Sistemas Operacionais Disciplina: Introdução à Informática Professora: Gustavo Leitão Email: gustavo.leitao@ifrn.edu.br Sistemas Operacionais Conteúdo: Partições Formatação Fragmentação Gerenciamento

Leia mais

Introdução. O que vimos. Infraestrutura de Software. (cont.) História dos Sistemas Operacionais. O que vimos 12/03/2012. Primeira geração: 1945-1955

Introdução. O que vimos. Infraestrutura de Software. (cont.) História dos Sistemas Operacionais. O que vimos 12/03/2012. Primeira geração: 1945-1955 O que vimos Infraestrutura de Software Introdução (cont.) Complexidade do computador moderno, do ponto de vista do hardware Necessidade de abstrações software Sistema computacional em camadas SO como uma

Leia mais

Introdução ao Modelos de Duas Camadas Cliente Servidor

Introdução ao Modelos de Duas Camadas Cliente Servidor Introdução ao Modelos de Duas Camadas Cliente Servidor Desenvolvimento de Sistemas Cliente Servidor Prof. Esp. MBA Heuber G. F. Lima Aula 1 Ciclo de Vida Clássico Aonde estamos? Page 2 Análise O que fizemos

Leia mais

Fundamentos de Sistemas Operacionais

Fundamentos de Sistemas Operacionais Fundamentos de Sistemas Operacionais Professor: João Fábio de Oliveira jfabio@amprnet.org.br (41) 9911-3030 Objetivo: Apresentar o que são os Sistemas Operacionais, seu funcionamento, o que eles fazem,

Leia mais

Arquiteturas RISC. (Reduced Instructions Set Computers)

Arquiteturas RISC. (Reduced Instructions Set Computers) Arquiteturas RISC (Reduced Instructions Set Computers) 1 INOVAÇÕES DESDE O SURGIMENTO DO COMPU- TADOR DE PROGRAMA ARMAZENADO (1950)! O conceito de família: desacoplamento da arquitetura de uma máquina

Leia mais

BRAlarmExpert. Software para Gerenciamento de Alarmes. BENEFÍCIOS obtidos com a utilização do BRAlarmExpert:

BRAlarmExpert. Software para Gerenciamento de Alarmes. BENEFÍCIOS obtidos com a utilização do BRAlarmExpert: BRAlarmExpert Software para Gerenciamento de Alarmes A TriSolutions conta com um produto diferenciado para gerenciamento de alarmes que é totalmente flexível e amigável. O software BRAlarmExpert é uma

Leia mais

LP II Estrutura de Dados. Introdução e Linguagem C. Prof. José Honorato F. Nunes honorato.nunes@ifbaiano.bonfim.edu.br

LP II Estrutura de Dados. Introdução e Linguagem C. Prof. José Honorato F. Nunes honorato.nunes@ifbaiano.bonfim.edu.br LP II Estrutura de Dados Introdução e Linguagem C Prof. José Honorato F. Nunes honorato.nunes@ifbaiano.bonfim.edu.br Resumo da aula Considerações Gerais Introdução a Linguagem C Variáveis e C Tipos de

Leia mais

Unidade Central de Processamento (CPU) Processador. Renan Manola Introdução ao Computador 2010/01

Unidade Central de Processamento (CPU) Processador. Renan Manola Introdução ao Computador 2010/01 Unidade Central de Processamento (CPU) Processador Renan Manola Introdução ao Computador 2010/01 Componentes de um Computador (1) Computador Eletrônico Digital É um sistema composto por: Memória Principal

Leia mais

Ao longo do presente capítulo será apresentada uma descrição introdutória da tecnologia FPGA e dos módulos básicos que a constitui.

Ao longo do presente capítulo será apresentada uma descrição introdutória da tecnologia FPGA e dos módulos básicos que a constitui. 3 Tecnologia FPGA Ao longo do presente capítulo será apresentada uma descrição introdutória da tecnologia FPGA e dos módulos básicos que a constitui. 3.1. FPGA: Histórico, linguagens e blocos Muitos dos

Leia mais

1. CAPÍTULO COMPUTADORES

1. CAPÍTULO COMPUTADORES 1. CAPÍTULO COMPUTADORES 1.1. Computadores Denomina-se computador uma máquina capaz de executar variados tipos de tratamento automático de informações ou processamento de dados. Os primeiros eram capazes

Leia mais

Arquitetura de Computadores. Sistemas Operacionais IV

Arquitetura de Computadores. Sistemas Operacionais IV Arquitetura de Computadores Sistemas Operacionais IV Introdução Multiprogramação implica em manter-se vários processos na memória. Memória necessita ser alocada de forma eficiente para permitir o máximo

Leia mais

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 05 Estrutura e arquitetura do SO Parte 1. Cursos de Computação

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 05 Estrutura e arquitetura do SO Parte 1. Cursos de Computação Cursos de Computação Sistemas Operacionais Prof. M.Sc. Sérgio Teixeira Aula 05 Estrutura e arquitetura do SO Parte 1 Referência: MACHADO, F.B. ; MAIA, L.P. Arquitetura de Sistemas Operacionais. 4.ed. LTC,

Leia mais

Arquitetura de Computadores Paralelismo, CISC X RISC, Interpretação X Tradução, Caminho de dados

Arquitetura de Computadores Paralelismo, CISC X RISC, Interpretação X Tradução, Caminho de dados Arquitetura de Computadores Paralelismo, CISC X RISC, Interpretação X Tradução, Caminho de dados Organização de um Computador Típico Memória: Armazena dados e programas. Processador (CPU - Central Processing

Leia mais

IW10. Rev.: 02. Especificações Técnicas

IW10. Rev.: 02. Especificações Técnicas IW10 Rev.: 02 Especificações Técnicas Sumário 1. INTRODUÇÃO... 1 2. COMPOSIÇÃO DO IW10... 2 2.1 Placa Principal... 2 2.2 Módulos de Sensores... 5 3. APLICAÇÕES... 6 3.1 Monitoramento Local... 7 3.2 Monitoramento

Leia mais

SISTEMAS OPERACIONAIS

SISTEMAS OPERACIONAIS SISTEMAS OPERACIONAIS Tópico 4 Estrutura do Sistema Operacional Prof. Rafael Gross prof.rafaelgross@fatec.sp.gov.br FUNÇÕES DO NUCLEO As principais funções do núcleo encontradas na maioria dos sistemas

Leia mais

Tais operações podem utilizar um (operações unárias) ou dois (operações binárias) valores.

Tais operações podem utilizar um (operações unárias) ou dois (operações binárias) valores. Tais operações podem utilizar um (operações unárias) ou dois (operações binárias) valores. 7.3.1.2 Registradores: São pequenas unidades de memória, implementadas na CPU, com as seguintes características:

Leia mais

7 Processamento Paralelo

7 Processamento Paralelo 7 Processamento Paralelo Yes, of course, who has time? Who has time? But then if we do not ever take time, how can we ever have time? (The Matrix) 7.1 Introdução Classificação de Sistemas Paralelos Diversas

Leia mais

MC-102 Aula 01. Instituto de Computação Unicamp

MC-102 Aula 01. Instituto de Computação Unicamp MC-102 Aula 01 Introdução à Programação de Computadores Instituto de Computação Unicamp 2015 Roteiro 1 Por que aprender a programar? 2 Hardware e Software 3 Organização de um ambiente computacional 4 Algoritmos

Leia mais

Sistemas Operacionais. Roteiro. Hardware. Marcos Laureano

Sistemas Operacionais. Roteiro. Hardware. Marcos Laureano Sistemas Operacionais Marcos Laureano 1/25 Roteiro Estrutura de um sistema operacional Interrupções Proteção do núcleo Níveis de privilégio Chamadas de sistema 2/25 Mono-processadores atuais seguem um

Leia mais

Organização de Computadores 1

Organização de Computadores 1 Organização de Computadores 1 4 SUPORTE AO SISTEMA OPERACIONAL Prof. Luiz Gustavo A. Martins Sistema Operacional (S.O.) Programa responsável por: Gerenciar os recursos do computador. Controlar a execução

Leia mais

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064 Sistemas Distribuídos Professora: Ana Paula Couto DCC 064 Questões Em uma rede de sobreposição (overlay), mensagens são roteadas de acordo com a topologia da sobreposição. Qual uma importante desvantagem

Leia mais

Computação Heterogênea Programação paralela, clusters e GPUs

Computação Heterogênea Programação paralela, clusters e GPUs Computação Heterogênea Programação paralela, clusters e GPUs Profa. Dra. Denise Stringhini (ICT- Unifesp) Primeiro Encontro do Khronos Chapters Brasil Belo Horizonte, 20/09/2013 Conteúdo Computação heterogênea:

Leia mais

Paralelismo. Computadores de alto-desempenho são utilizados em diversas áreas:

Paralelismo. Computadores de alto-desempenho são utilizados em diversas áreas: Computadores de alto-desempenho são utilizados em diversas áreas: - análise estrutural; - previsão de tempo; - exploração de petróleo; - pesquisa em fusão de energia; - diagnóstico médico; - simulações

Leia mais

Notas da Aula 17 - Fundamentos de Sistemas Operacionais

Notas da Aula 17 - Fundamentos de Sistemas Operacionais Notas da Aula 17 - Fundamentos de Sistemas Operacionais 1. Gerenciamento de Memória: Introdução O gerenciamento de memória é provavelmente a tarefa mais complexa de um sistema operacional multiprogramado.

Leia mais

Programação de Sistemas

Programação de Sistemas Programação de Sistemas Introdução à gestão de memória Programação de Sistemas Gestão de memória : 1/16 Introdução (1) A memória central de um computador é escassa. [1981] IBM PC lançado com 64KB na motherboard,

Leia mais

ORGANIZAÇÃO DE COMPUTADORES MÓDULO 8

ORGANIZAÇÃO DE COMPUTADORES MÓDULO 8 ORGANIZAÇÃO DE COMPUTADORES MÓDULO 8 Índice 1. A Organização do Computador - Continuação...3 1.1. Processadores - II... 3 1.1.1. Princípios de projeto para computadores modernos... 3 1.1.2. Paralelismo...

Leia mais

Introdução a Informática. Prof.: Roberto Franciscatto

Introdução a Informática. Prof.: Roberto Franciscatto Introdução a Informática Prof.: Roberto Franciscatto 2.1 CONCEITO DE BIT O computador só pode identificar a informação através de sua elementar e restrita capacidade de distinguir entre dois estados: 0

Leia mais

ULA Sinais de Controle enviados pela UC

ULA Sinais de Controle enviados pela UC Solução - Exercícios Processadores 1- Qual as funções da Unidade Aritmética e Lógica (ULA)? A ULA é o dispositivo da CPU que executa operações tais como: Adição Subtração Multiplicação Divisão Incremento

Leia mais

Arquitetura de Computadores II

Arquitetura de Computadores II Universidade Federal do Rio de Janeiro Informática DCC/IM Arquitetura de Computadores II Sistemas de Troca de Mensagens O Sistema de Comunicação provê tipicamente os seguintes serviços para as aplicações:

Leia mais

Sistemas Operacionais. Andrique Amorim www.andrix.com.br professor@andrix.com.br. Gerência de Arquivos

Sistemas Operacionais. Andrique Amorim www.andrix.com.br professor@andrix.com.br. Gerência de Arquivos Andrique Amorim www.andrix.com.br professor@andrix.com.br Gerência de Arquivos Gerência de Arquivos Um sistema operacional tem por finalidade permitir que o usuários do computador executem aplicações,

Leia mais

Everson Scherrer Borges João Paulo de Brito Gonçalves

Everson Scherrer Borges João Paulo de Brito Gonçalves Everson Scherrer Borges João Paulo de Brito Gonçalves 1 Tipos de Sistemas Operacionais Os tipos de sistemas operacionais e sua evolução estão relacionados diretamente com a evolução do hardware e das

Leia mais

E/S PROGRAMADA E/S PROGRAMADA E/S USANDO INTERRUPÇÃO

E/S PROGRAMADA E/S PROGRAMADA E/S USANDO INTERRUPÇÃO E/S PROGRAMADA QUANDO A CPU FAZ TODO O TRABALHO RELACIONADO A UMA OPERAÇÃO DE E/S, NO CASO DO PROCESSO QUERER IMPRIMIR (NA IMPRESSORA) ABCDEFGH : ESTES CARACTERES SÃO COLOCADOS EM UMA ÁREA DE MEMÓRIA DO

Leia mais

Disciplina: Introdução à Informática Profª Érica Barcelos

Disciplina: Introdução à Informática Profª Érica Barcelos Disciplina: Introdução à Informática Profª Érica Barcelos CAPÍTULO 4 1. ARQUITETURA DO COMPUTADOR- HARDWARE Todos os componentes físicos constituídos de circuitos eletrônicos interligados são chamados

Leia mais

GESTÃO DE SISTEMAS OPERACIONAIS II

GESTÃO DE SISTEMAS OPERACIONAIS II GESTÃO DE SISTEMAS OPERACIONAIS II Servidores Definição Servidores História Servidores Tipos Servidores Hardware Servidores Software Evolução do Windows Server Windows Server 2003 Introdução Windows Server

Leia mais

AUTOR: DAVID DE MIRANDA RODRIGUES CONTATO: davidmr@ifce.edu.br CURSO FIC DE PROGRAMADOR WEB VERSÃO: 1.0

AUTOR: DAVID DE MIRANDA RODRIGUES CONTATO: davidmr@ifce.edu.br CURSO FIC DE PROGRAMADOR WEB VERSÃO: 1.0 AUTOR: DAVID DE MIRANDA RODRIGUES CONTATO: davidmr@ifce.edu.br CURSO FIC DE PROGRAMADOR WEB VERSÃO: 1.0 SUMÁRIO 1 Conceitos Básicos... 3 1.1 O que é Software?... 3 1.2 Situações Críticas no desenvolvimento

Leia mais

Projetos. Universidade Federal do Espírito Santo - UFES. Mestrado em Informática 2004/1. O Projeto. 1. Introdução. 2.

Projetos. Universidade Federal do Espírito Santo - UFES. Mestrado em Informática 2004/1. O Projeto. 1. Introdução. 2. Pg. 1 Universidade Federal do Espírito Santo - UFES Mestrado em Informática 2004/1 Projetos O Projeto O projeto tem um peso maior na sua nota final pois exigirá de você a utilização de diversas informações

Leia mais