Eloiza Helena Sonoda
description
Transcript of Eloiza Helena Sonoda
OOPSOOPSObject-Oriented Parallel Object-Oriented Parallel
SystemSystemUm framework de classes para programação científica
paralela.
Eloiza Helena Sonoda
Orientador: Prof. Dr. Gonzalo Travieso
Apoio financeiro
Introdução
O desenvolvimento da tecnologia dos microprocessadores tem resultado em grande desempenho dos computadores.
O processamento paralelo tem se mostrado bastante apropriado para satisfazer a demanda crescente de desempenho computacional.
Aumento na disponibilidade de sistemas paralelos.
Introdução
Aplicações com alta demanda de poder computacional tendem a ser desenvolvidas usando programação concorrente para execução paralela.
A programação concorrente é bastante complexa e a responsabilidade de desenvolver aplicações paralelas eficientes acaba sendo do programador da aplicação.
Tornam-se necessárias ferramentas apropriadas para auxiliar no desenvolvimento de aplicações paralelas.
Abordagens para o desenvolvimento de
ferramentasCompiladores paralelizantes.
Diretivas de paralelização.
Linguagens.
Bibliotecas.
Motivação do trabalho
Argumentamos que uma abordagem promissora é baseada em ferramentas
convencionais de programação seqüencial acrescida de uma biblioteca
de classes que forneça abstrações adequadas de concorrência.
OOPS Object-Oriented Parallel
SystemProposta.
Conceitos básicos.
Apresentação de classes e exemplos.
Framework OOPS
Biblioteca MPI
Programa Usuário
Proposta
Projeto e implementação do framework de classes OOPS para apoiar a programação científica paralela.
Características:
Voltado a aplicações científicas com necessidade de alto desempenho.
Técnicas de orientação a objetos para organizar o desenvolvimento do programa e encapsular detalhes de paralelismo.
Proposta
Memória distribuída e passagem de mensagens.
Utiliza somente ferramentas convencionais de programação acrescida de uma biblioteca de classes.
Envolvimento do programador com aspectos paralelos de projeto.
Fornecimento de abstrações próximas aos modelos matemáticos.
Proposta
Processadores virtuais.
Grupos de processadores virtuais.
Topologias.
Contêineres distribuídos.
Componentes paralelos.
Composições dos componentes paralelos:
Seqüencial.
Concorrente.
Classes básicas
OOPS::Main
Componente paralelo principal, realiza inicialização e finalização da máquina virtual paralela.
OOPS::Group
Grupo de processadores virtuais.
OOPS::Sendable
Classe básica para objetos que serão comunicados.
Classes básicas
OOPS::Partner
Parceiro em uma comunicação.
OOPS::Workgroup
Grupo de trabalho para comunicações.
OOPS::Topology
Estrutura de comunicação entre os processadores de um grupo.
OOPS::Distribution
Modos de distribuição de contêineres.
Topologias
Derivadas de OOPS::Topology.Fornece comunicação ponto a ponto entre os processadores de um grupo em um arranjo e comunicação coletiva em subgrupos.OOPS::TopologyPlainOOPS::TopologyPipeOOPS::TopologyPipeRingOOPS::TopologyLinearOOPS::TopologyRingOOPS::TopologyGridOOPS::TopologyTorus
#include <iostream>#include <mpi++.h>
int main(int argc, char **argv) { MPI::Init(argc, argv); int size, myID; size = MPI::COMM_WORLD.Get_size(); myID = MPI::COMM_WORLD.Get_rank(); int x = 0, tag = 99; if (!myID) { std::cout << "I have x = " << x << std::endl; x++; MPI::COMM_WORLD.Send(&x, 1, MPI::INT, 1, tag); } else { MPI::COMM_WORLD.Recv(&x, 1, MPI::INT, myID - 1, tag); std::cout << "I have x = " << x << std::endl; x++; if (myID != (size - 1)) MPI::COMM_WORLD.Send(&x, 1, MPI::INT, myID + 1, tag); } MPI::Finalize(); return 0;}
#include <iostream>#include <mpi++.h>
int main(int argc, char **argv) { MPI::Init(argc, argv); int size, myID; size = MPI::COMM_WORLD.Get_size(); myID = MPI::COMM_WORLD.Get_rank();
int next = myID + 1; int previous = myID - 1; if (!myID) previous = MPI::PROC_NULL; if (myID == size - 1) next = MPI::PROC_NULL; int x = 0, tag = 99;
MPI::COMM_WORLD.Recv(&x, 1, MPI::INT, previous, tag); std::cout << "I have x = " << x << std::endl; x++; MPI::COMM_WORLD.Send(&x, 1, MPI::INT, next, tag); MPI::Finalize(); return 0;}
#include <iostream>#include <oops>
void OOPS::Main::executeOn(const OOPS::Group *allProcs) { OOPS::TopologyPipe topo(allProcs); int x = 0; topo.fromPrevious(x); std::cout << "I have x = " << x << std::endl; x++; topo.toNext(x);}
Modos de distribuição dos contêineres
Derivadas de OOPS::Distribution.
Tamanhos locais.
Conversões entre índices locais e globais.
OOPS::DistributionBlocked
OOPS::DistributionCyclic
OOPS::DistributionNone
Contêineres Distribuídos
Coleções de dados parametrizados no tipo do elemento armazenado.
Particionamento de dados determinado pela topologia e modo de distribuição.
OOPS::Vector<T>
OOPS::VectorRepl<T>
OOPS::Matrix<T>
const int dim = 10000;void OOPS::Main::executeOn(const OOPS::Group *allProcs) { try { OOPS::TopologyLinear topo(allProcs); OOPS::DistributionBlocked block; OOPS::Vector<double> v1(dim, block, topo), v2(dim, block, topo), v(dim, block, topo); std::ifstream a1("vector1.dat"), a2("vector2.dat"); a1 >> v1; a2 >> v2; v = v1 * v2; std::ofstream saida("result.dat"); saida << v; double s = v.sum(); if (topo.group()->myID() == 0) std::cout << "Produto escalar = " << s << std::endl; } catch(Error_NotPositive e) { ...código... } catch(...) { std::cout << "\nOOPS!!\n " << std::endl; }}
const int N = 1000;void OOPS::Main::executeOn(const OOPS::Group *allProcs) { try { OOPS::TopologyLinear topo_linear(allProcs); OOPS::TopologyGrid topo_grid(allProcs, allProcs->size()); OOPS::DistributionBlocked block; OOPS::DistributionNone none; OOPS::VectorRepl<double> a(N, topo_linear); OOPS::Vector<double> b(N, block, topo_linear); std::ifstream file_vec("vector.dat"); file_vec >> a; OOPS::Matrix<double> m(N, N, block, none, topo_grid); std::ifstream file_mat(“matrix.dat"); file_mat >> m;
b = m * a;
std::ofstream saida("result.dat"); saida << b; }}
Estêncil
tji
tji
tji
tji
tji uuuuu 1,,11,,11, 4
1
Atualização de bordas fantasmas
void OOPS::Main::executeOn(const OOPS::Group *allProcs) { try { OOPS::TopologyGrid grid(allProcs); OOPS::DistributionBlocked block; int M, N; double Ts, Ti, diff_limit; readArgs(M, N, Ts, Ti, diff_limit); int ghost = 1; OOPS::Matrix<double> a(M, N, block, block, grid, ghost), b(M, N, block, block, grid, ghost); ... inicialização das matrizes a e b ... double max_diff; do { b.syncGhostsCart(); for (int i = 0; i < b.localRowSize(); i++)
for (int j = 0; j < b.localColSize(); j++) a.local(i,j) = (b.local(i,j-1) + b.local(i,j+1) +
b.local(i-1,j) + b.local(i+1,j)) / 4.0; a.syncGhostsCart(); max_diff = 0;
for (int i = 0; i < b.localRowSize(); i++)for (int j = 0; j < b.localColSize(); j++) { b.local(i,j) = (a.local(i,j-1) + a.local(i,j+1) +
a.local(i-1,j) + a.local(i+1,j)) / 4.0; double diff = fabs(b.local(i,j) - a.local(i,j)); if (diff > max_diff) max_diff = diff;}
grid.max(max_diff); } while (max_diff > diff_limit);
std::ofstream result("result.dat"); result << b; }}
Intertopologias
Derivadas de OOPS::InterTopology.
Função parametrizada no tipo de componente OOPS::Execute<T>(), com T apresentando um método estático T::executeOn().
Composição seqüencial
void OOPS::Main::executeOn(const OOPS::Group *allProcs) { try{ OOPS::TopologyGrid topo_grid(allProcs); OOPS::DistributionBlocked block; int N; OOPS::Matrix<double> m(N, N, block, block, topo_grid); foo(m); bar(m); std::ofstream result("result.dat"); result << m; }}
Composição concorrenteclass InterTopologyPipe : public OOPS::InterTopology { OOPS::Partner *next, *prev;public: InterTopologyPipe(const OOPS::Group *g) : OOPS::InterTopology(g) { int n = g->myID() + 1; int p = g->myID() - 1; if (g->isFirst()) p = OOPS::PARTNER_NULL; if (g->isLast()) n = OOPS::PARTNER_NULL; next = new OOPS::Partner(g, n); prev = new OOPS::Partner(g, p); } void toNext(int &x) const { next->send(x); } void fromPrevious(int &x) const { prev->recv(x); }};
class foo {public: static void executeOn(const OOPS::Group *g, const InterTopologyPipe *it);};
class bar {public: static void executeOn(const OOPS::Group *g, const InterTopologyPipe *it);}; void foo::executeOn(const OOPS::Group *g, const InterTopologyPipe *it) { int x; ... continuação do código ... it->toNext(x);}
void bar::executeOn(const OOPS::Group *g, const InterTopologyPipe *it) { int x; it->fromPrevious(x); ... continuação do código ...}
void OOPS::Main::executeOn(const OOPS::Group *allProcs) { try{ int n = 2; int *IDs; IDs = new int[n]; IDs[0] = 0; IDs[1] = 2; OOPS::Group *sub1 = allProcs->subGroup(n, IDs); IDs[0] = 1; IDs[1] = 3; OOPS::Group *sub2 = allProcs->subGroup(n, IDs); delete IDs;
InterTopologyPipe *it = new InterTopologyPipe(allProcs); OOPS::Execute<foo>(sub1, it); OOPS::Execute<bar>(sub2, it); }}
Desempenho
Programa para o cálculo do fractal de Mandelbrot.
Cluster Beowulf com 8 nós de processamento.
Testes realizados com matrizes 5000x5000.
int compute_mandel(const double x, const double y) { // retorna o valor de mandelbrot para o complexo (x + iy)}
void OOPS::Main::executeOn(const OOPS::Group *allProcs) { try{ OOPS::TopologyGrid grid(allProcs); OOPS::DistributionCyclic cyclic; int n_real, n_imag; double br, ur, bi, ui; readArgs(br, ur, bi, ui, n_real, n_imag); double rdelta = (ur - br) / n_real; double idelta = (ui - bi) / n_imag;
OOPS::Matrix<int> mandel(n_imag, n_real, cyclic, cyclic, grid); for (int i = 0; i < mandel.localRowSize(); i++) for (int j = 0; j < mandel.localColSize(); j++) {
OOPS::Matrix<int>::Index gi = mandel.localToGlobal(i,j);mandel.local(i,j) = compute_mandel(br+(gi.col()*rdelta)+rdelta/2,
ui-(gi.row()*idelta)-idelta/2); } std::ofstream outFile("result.dat"); outFile << mandel; }}
int compute_mandel(const double x, const double y) {/* retorna o valor de mandelbrot para o complexo (x + iy)*/}
int main(int argc, char *argv[]) { double lr, ur, li, ui, dx, dy; int rr, ir; FILE *arq; int **result; int i, j; double x, y; int ix, iy; int NP, p, NPr, NPi, pr, pi, lrr, lir;
MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &NP); MPI_Comm_rank(MPI_COMM_WORLD, &p);
dx = (ur - lr) / rr; dy = (ui - li) / ir; /* Compute process grid dimensions */ NPr = (int)sqrt(NP); while (NP % NPr != 0) NPr--; NPi = NP/NPr; /* Compute position of the process on the grid */ pi = p / NPr; pr = p % NPr;
/* Compute local sizes */ lir = ir/NPi + (pi < (ir % NPi)); lrr = rr/NPr + (pr < (rr % NPr)); /* Allocate local pixels */ result = (int**)malloc(lir * sizeof(int*)); for (i = 0; i < lir; i++) result[i] = (int*)malloc(lrr*sizeof(int));
/* Compute distributed mandelbrot (cyclic distribution) */ for (iy = 0; iy < lir; iy++) { y = ui - (iy*NPi+pi) * dy; for (ix = 0; ix < lrr; ix++) { x = lr + (ix*NPr+pr) * dx; result[iy][ix] = compute_mandel(x + dx/2, y - dy/2); } }
if (p > 0) { MPI_Request rq[lir]; MPI_Status st; /* Send result to process 0 */ for (iy = 0; iy < lir; iy++) MPI_Isend(result[iy], lrr, MPI_INT, 0, 0, MPI_COMM_WORLD, &rq[iy]); for (iy = 0; iy < lir; iy++) MPI_Wait(&rq[iy], &st); }
else { /* Process 0: collect and write results */ int **finalresult; int partner; MPI_Status st; finalresult = (int**)malloc(ir * sizeof(int*)); for (i = 0; i < ir; i++) finalresult[i] = (int*)malloc(rr*sizeof(int)); for (iy = 0; iy < lir; iy++) for (ix = 0; ix < lrr; ix++) finalresult[iy*NPi][ix*NPr] = result[iy][ix]; for (partner = 1; partner < NP; partner++) { int ppi = partner / NPr; int ppr = partner % NPr; int plir = ir/NPi + (ppi < (ir % NPi)); int plrr = rr/NPr + (ppr < (rr % NPr)); for (iy = 0; iy < plir; iy++) MPI_Recv(result[iy], plrr, MPI_INT, partner, 0, MPI_COMM_WORLD, &st); for (iy = 0; iy < plir; iy++) for (ix = 0; ix < plrr; ix++) finalresult[iy*NPi+ppi][ix*NPr+ppr] = result[iy][ix]; } /* Create output file */ }
MPI_Finalize(); return 0;}
Sobrecarga adicionada pelo OOPS
Sugestões de trabalhos futuros
Extensão das classes desenvolvidas.
Entrada e saída paralela.
Balanceamento de cargas e criação dinâmica de tarefas.
Estruturas de dados irregulares.