Sunteți pe pagina 1din 20

Anlise de Desempenho Usando Tcnicas de Vetorizao, Blocagem e Programao Concorrente

Jaguaraci Batista Silva Instituto de Cincia e Tecnologia, Universidade Federal de So Paulo Campus So Jos dos Campos, So Paulo-SP jaguaracisilva@gmail.com

Resumo: A busca pela melhoria de performance pode ser alcanada por diversas
tcnicas, cuja a tcnica de programao concorrente j bastante consolidada e utilizada no mercado. Entretanto, adicionar concorrncia possui um custo adicional de conhecimento e a escolha da tecnologia pode ser benfica ou um entrave na utilizao dessa estratgia. Diversos estudos reveleram que o OpenMp um dos frameworks mais fceis de serem utilizados, porm ainda h dvidas sobre a sua performance em relao aos outros. Este trabalho apresenta (1) os resultados atravs da mudana do paradigma sequencial para a programao concorrente, considerando o aumento do nmero de threads e diferentes formas de balanceamento de carga entre os processadores. Por fim, para conhecer de fato a eficincia do OpenMP, este estudo realizou uma (2) implementao hbrida utlizando trs estratgias de melhoria de perfomance: (i) programao concorrente, (ii) vetorizao intrsica usando extenses SSE3 do processador Intel e (iii) tcnicas de blocagem para medir o speedup e eficincia em comparao aos frameworks Pthreads e Java Threads.

Palavras-chaves: Performance, Programao Concorrente, OpenMP, Pthreads, Java.

1 - Introduo
A dificuldade de se escrever programas representa uma grande barreira para a explorao de diversas arquiteturas. A programao difcil especialmente por no se conseguir contornar os problemas de acesso memria (e.g. localidade), uma vez que as linguagens de programao atuais, ferramentas e arquiteturas esto evoluindo em direes menos adequadas para esses cdigos, por explorar, principalmente, as barreiras da portabilidade (e.g. Java). Por isso, importante, quando se pensa em alta performance dos sistemas computacionais, saber escolher um paradigma de programao consonante as arquiteturas atuais de multiprocessadores com memria compartilhada ou multicores [1]. O paralelismo, atualmente, est presente em todos os nveis da computao indo desde o paralelismo de instrues em um pipeline, passando pelos processadores multicore, colocados como soluo para o problema de limitao no crescimento do clock, at sistemas distribudos de larga escala como as grades computacionais. Nos ltimos 20 anos, os fabricantes de microprocessadores exploraram altos graus de paralelismo no nvel de instruo ou Instruction Level Parallelism (ILP), onde muitas

geraes de processadores foram construdas aumentando-se cada vez mais a frequncia do clock e com pipelines cada vez mais profundos. Como benefcio, as aplicaes atingiram um maior desempenho por delegar aos compiladores a tarefa de explorar eficientemente o ILP. Porm, devido a vrios fatores, em especial s limitaes fsicas, tornou-se necessrio alterar o foco do paralelismo do ILP para o TLP (Thread Level Parallelism). Nesse, o ganho de desempenho alcanado atravs da replicao das unidades de execuo (cores) ao passo que se mantm os clocks em uma faixa na qual o consumo de energia e dissipao de calor so menores e, portanto, menos problemticos. Ao longo dos anos, possvel encontrar diversas tentativas direcionadas aos compiladores [3], entretanto, esses se demonstraram teis apenas em algumas classes restritas de problemas. Assim, no contexto multicore, no possvel fundamentar-se apenas nos compiladores para obter ganhos de desempenho e extremamente necessrio reescrever as aplicaes de modo a tirar proveito do paralelismo de granularidade fina, onde a granularidade fina dos cores promovem uma melhoria no acesso as suas memrias locais (cache L1), o que resulta na reorganizao das tarefas para que estas operem sob pequenas pores de dados para reduzir o trfego no barramento e aumentar a localidade de dados, evitando entre outros problemas o cache miss [4]. O objetivo deste trabalho comparar os resultados atravs da mudana do paradigma sequencial para a programao concorrente, considerando o aumento do nmero de threads e diferentes formas de balanceamento de carga entre os processadores. Com esse fim, o estudo selecionou dois algoritmos: um de ordenao e um clculo simples de verificao de nmero primos para mostrar se possvel obter desempenho em funes auxiliares. Na segunda parte do estudo foi feita uma anlise para conhecer qual a melhor implementao hbrida entre as estratgias de memria compartilhada em conjunto com as tcnicas de vetorizao [3] e blocagem [4] aferindo medies de speedup e eficincia quando da adoo do OpenMP, Pthreads e Java Threads em uma aplicao que realiza o clculo de produtos de matrizes.

2 Soluo
De um modo geral, os paradigmas paralelos podem ser classificados como: memria compartilhada com tpicos explcitos (Pthreads, Java Threads) ou de memria compartilhada com paralelismo de dados (OpenMP). A abordagem de memria compartilhada usando Pthreads (POSIX threads) um modelo de programao onde o paralelismo utiliza invocaes das funes paralelas. Um corpo de uma funo quando paralelizado executado por muitas threads, que podem acessar dados compartilhados de forma global. Pthreads se consolidou como uma implementao subjacente de paralelismo, enquanto Java Threads, por utilizar uma linguagem de programao de propsito geral, promove o suporte ao paralelismo na forma de tpicos. Os programas paralelos construdos em Java so SMPs (Symmetric Multiprocessing), e assim, se assemelham aos programas implementados com a abordagem utilizada com Pthreads. Desta forma, Pthreads e Java esto disponveis apenas em SMPs, enquanto a bliblioteca OpenMP um modelo de memria compartilhada onde o paralelismo definido usando directivas paralelas nos loops e funes, cujas directivas especificam quais laos ou iteraes devem ser executadas

em paralelo, bem como as funes. Essas diretrizes adicionais especificam tambm quais dados devem ser compartilhados ou privados para cada segmento. Os compiladores traduzem os programas OpenMP em um cdigo que se assemelham aos programas construdos usando Pthreads, onde o cdigo dos loops paralelos so executados atravs de funes paralelas [1]. Para comparar a eficincia das abordagens de memria compartilhada, este estudo foi divido em duas partes: (2.1) medio de performance atravs da mudana do paradigma sequencial para concorrente e (2.2) implementao hbrida das estratgias de vetorizao, blocagem e programao concorrente. 2.1 Medio de Performance Atravs da Mudana do Paradigma Sequencial para o Concorrente O algoritmo utilizado no exprimento realiza uma ordenao de nmeros do tipo float atravs de 3 loops. Conforme a listagem 1, possvel perceber que os loops mais internos realizam uma ordenao separando a tarefa entre nmeros pares e mpares. Cdigo Sequencial
void ordenaVetor(int n, float *a) { int i, j, nHalf, lastEven, lastOdd; nHalf = n/2; if (n%2 == 0) { lastEven=nHalf-1; lastOdd=nHalf-2; } else { lastEven=nHalf-1; lastOdd=nHalf-1; } for (i=0; i<n-1; i++) { for (j=0; j<=lastOdd; j++) &a[2*j+2]); /* odd */ for (j=0; j<=lastEven; &a[2*j+1]); /* even */ }} j++) #pragma omp parallel private(i) ce(&a[2*j+1], for (i=0; i<n-1; i++) { omp_set_num_threads(nthreads); ce(&a[2*j], #pragma omp parallel for for (j=0; j<=lastOdd; j++) ce(&a[2*j+1], &a[2*j+2]); /* odd */ omp_set_num_threads(nthreads); #pragma omp parallel for for (j=0; j<=lastEven; j++) ce(&a[2*j], &a[2*j+1]); /* even */}}
Listagem1 Mudana de Paradigma para Melhoria de Performance na Ordenao.

Cdigo com Diretivas do OpenMp


void ordenaVetorOpenMP(int n, float *a, int nthreads) { int i, j, nHalf, lastEven, lastOdd; nHalf = n/2; if (n%2 == 0) { lastEven=nHalf-1; lastOdd=nHalf-2; } else { lastEven=nHalf-1; lastOdd=nHalf-1; }

O cdigo utilizado na verso serial do programa (Listagem 1 - lado esquerdo) promoveu uma certa facilidade na identificao dos pontos que deveriam ter as diretivas de paralelizao do OpenMP (Listagem 1 lado direito). A primeira estrutura de repetio foi modificada com a diretiva #pragma omp parallel private(i), pois o

objetivo neste caso foi garantir que o ndice que controla o lao no fosse compartilhado entre as threads. Nos laos internos a granularidade de cada operao permitiu, atravs da diretiva #pragma omp parallel for, um incremente do nmero de threads, cujo os grficos podem ser vistos na seo de resultados para comparao do desempenho entre o tempo da verso serial e a execuo do programa paralelo (Figura 1). A insero das diretivas do OpenMP no prejudica o reuso do cdigo para fins de execuo serial, pois no foi necessrio qualquer modifcao para alcanar tal benefcio. Alm disso, as diretivas podem ser ignoradas quando da compilao, para executar o trecho paralelo usando o GCC basta inserir a seguinte linha de compilao: gcc -O2 -fopenmp -I /papi/include primo.c -o primo -L /papi/lib -lpapi lm, onde a incluso da flag fopenmp, torna-se possvel a insero da diretiva e sem a flag a opo de no transformar o cdigo serial em segmentos paralelizveis. O segundo programa (Listagem 2 Lado Esquerdo) utiliza a gerao de nmeros para verificao se cada nmero gerado primo, neste caso foram analisadas diferentes formas de balanceamento de cargas entre os processadores. Da mesma forma que na situao anterior, um lao foi paralelizado com a diretiva #pragma omp parallel for, a novidade foi a incluso das clasulas de escalonamento [5] quando do incremente do nmero de threads (Listagem 2 Lado Direito). As anlises de desempenho tambm podem ser vistas na seo de resultados (Figura 2 e 3). Cdigo com Diretivas do OpenMP para Escalonamento
A = alocaMemoria(n); s = PAPI_get_real_usec(); #pragma omp parallel for for (i=0; i<n; i++) { numero = rand_r(&semente)%1000000000 +1; A[i] = ehPrimo(numero,n); } e = PAPI_get_real_usec(); setPapiFinal(e, retval, EventSet, s); A = liberaMemoria(n, A); int ehPrimo(int i, int n){ long int max; int ret = 1; max = (long int) sqrt(n); if(n%2==0) ret = 0; for(i=3; i<=max && ret==1; i+=2) { if(n%i==0) ret = 0; } return ret; }

Execuo do Programa Usando Clusula de Escalonamento


export OMP_SCHEDULE="static,10" ./primo 1 > static10/primo_1.csv ./primo 2 > static10/primo_2.csv ./primo 4 > static10/primo_4.csv ./primo 6 > static10/primo_6.csv ./primo 8 > static10/primo_8.csv export OMP_SCHEDULE="dynamic,3" ./primo 1 > dynamic3/primo_1.csv ./primo 2 > dynamic3/primo_2.csv ./primo 4 > dynamic3/primo_4.csv ./primo 6 > dynamic3/primo_6.csv ./primo 8 > dynamic3/primo_8.csv export OMP_SCHEDULE="guided,3" ./primo 1 > guided3/primo_1.csv ./primo 2 > guided3/primo_2.csv ./primo 4 > guided3/primo_4.csv ./primo 6 > guided3/primo_6.csv ./primo 8 > guided3/primo_8.csv

Listagem 2 Implementao com OpenMP Utilizando Clusulas de Escalonamento.

As clasulas de esclalonamento descrevem como as iteraes do lao so divididas entre as threads e existem trs tipos no OpenMP: esttica, dinmica e guiada. Na esttica as iteraes do lao so divididas em pedaos e depois atribudas

estaticamente as threads. Se um tamanho no for especificado, as iteraes so executadas uniformemente, se possvel, dividindo-as entre as threads de forma contgua. Na dinmica as iteraes so divididas em pedaos a serem distribudos sistematicamente entre as threads, quando uma thread termina um pedao atribudo um outro. O tamanho do bloco padro no escalonamento dinmico 1 e mximo a ser atribudo depende da quantidade de linhas que podem ser endereadas na memria, j que o chunk size est ligado diretamente ao tamanho da memria cache. J na clusula guiada as iteraes so atribudas dinamicamente para os segmentos em forma de blocos, at que sejam solicitados, os blocos continuam a ser atribudos para cada thread. O escalonamento guiado semelhante ao dinmico, exceto pelo fato que o tamanho do bloco diminui cada vez que uma pedao do trabalho atribudo a uma thread. 2.2 Implementao Hbrida das Tcnicas de Vetorizao, Blocagem e Programao Concorrente A multiplicao de cada elemento de uma matriz de resultados gerada por um produto escalar, onde uma sequncia de nmeros de ponto flutuante so multiplicados e acumulados, exigindo assim duas operaes de ponto flutuante para cada par de nmeros com operando de produto escalar. Uma tcnica de multiplicao padro para mquinas com hierarquia de memria comum (e.g. Registradores, Cache L1, Cache L2 e Memria Principal) a blocagem, onde o objetivo fundamental quebrar uma multiplicao de matrizes grandes em uma srie de sub-matrizes menores, cujo os dados necessrios cabem inteiramente na cache (Figura 1).

Figura 1 Multiplicao de Matrizes usando a Tcnica de Blocagem [8].

Primeiro so definidas duas matrizes A e B com dimenses M x K e K x N, linhas e colunas de cada matriz, respectivamente, ento C = A x B, onde M x N ser o resultado. Se todos os elementos de A, B e C no couberem inteiramente na cache L1 simultaneamente, cada vez que for necessrio obter esses elementos estes sero retirados seja da cache L2 ou pior, da memria principal. A estratgia de blocagem visa melhorar o desempenho para que as obtenes dos dados permaneam no mais baixo nvel hierrquico possvel, sendo A M/m x B N /n, ou seja pequenas matrizes, c = a x b

com uma escala a~( m x k), b~(k x n) e c~(m x n). Os blocos m, n e k de a, b e c so pequenos e por isso cabem na cache, assim todos os dados necessrios para a multiplicao corrente estaro no mesmo nvel da cache e no mais baixo nvel a ser acessado. Para realizar a multiplicao, por exemplo, o bloco c2,2 gerado a partir da operao c2,2 = a,2,1 x b1,2 + a2,2 x b2,2 + a2,3 x b2,3 e assim sucessivamente [8]. Com objetivo de melhorar a performance da multiplicao de matrizes ainda possvel realizar a multiplicao dos blocos simultaneamente utilizando unidades de vetorizao dos registradores, ou seja, o nvel mais baixo da hierarquia de memria. Existem trs formas principais de se fazer uso de unidades de vetorizao: (i) pela programao em cdigo de montagem, (ii) atravs da programao com funes intrnsecas em linguagens de alto nvel (e.g. C/C++) ou (iii) usar um compilador que traduz automaticamente as operaes escalares em vetoriais. Seja atravs da programao em linguagem de montagem ou usando funes intrnsecas, o programador quem deve possuir o controle completo dos detalhes da sua implementao, que geralmente especfica para uma arquitetura atravs de compiladores tais como GCC e ICC da Intel (ICC) que podem gerar automaticamente o cdigo vetorizado. O speedup mximo que pode ser obtido por meio de extenses de vetorizao dos processadores pode ser dado em funo da largura dos seus registradores e unidades de vetorizao. A maioria das mquinas hoje possuem unidades de vetorizao de 128 bits e as operaes de vetorizao em relao as sequenciais podem ser equivalentes at 2, 4, 8 at 16 vezes mais rpida que a sua congnere escalar, dependendo do tipo de dados. Assim, a vetorizao uma das transformaes do compilador que pode ter um impacto significativo no desempenho das aplicaes [3]. Conforme visto em [3] nem sempre o compilador consegue vetorizar automaticamente o lao, no estudo de Silva, JB foi possvel ver alguns entraves por conta da dependncia de dados nos laos e uma alternativa foi implementar a vetorizao ntrisica, que no estudo de caso em uma aplicao de clculo de mnimos quadrados realizado no estudo obteve sppedup 2,7x em relao a verso escalar. Outras dificuldades encontradas para alcanar a vetorizao automtica podem se vistas detalhamente em [9]. Neste experimento o compilador utilizado (GCC) no conseguiu vetorizar automaticamente os laos da multiplicao, por isso, adiante sero apresentados os detalhes necessrios para que a vetorizao pudesse ser realizada nos laos do algoritmo de blocagem. Algoritmo de Multiplicao Utilizando Blocagem
int ii,jj,kk,i,j,k; for (ii=0; ii<n; ii+=tamBloco){

Algoritmo de Multiplicao Utilizando Blocagem, Vetorizao e OpenMP


__m128 v1,v2,v3,v4,total; float soma __attribute__((aligned(16))); int ii,jj,kk,i,j,k;

for (jj=0; jj<n; jj+=tamBloco){ for (kk=0; kk<n; kk+=tamBloco){ for (j=ii; j<min(ii+tamBloco,n); j++){ for (k=jj; k<min(jj+tamBloco,n); k++){ for (i=kk; i<min(kk+tamBloco,n); i++){ mr[i][j] += a[i][k] * b[k][j]; } } } } } }

for (ii=0; ii<n; ii+=tamBloco){ for (jj=0; jj<n; jj+=tamBloco){ for (kk=0; kk<n; kk+=tamBloco){ omp_set_num_threads(nthreads); #pragma omp parallel for collapse(2) for (j=ii; j<min(ii+tamBloco,n); j++){ for (k=jj; k<min(jj+tamBloco,n); k++){ for (i=kk; i<min(kk+tamBloco,n); i++){ soma = 0; v1 = _mm_loadu_ps(&X[i][k]); v2 = _mm_loadu_ps(&Y[k][j]); v3 = _mm_mul_ps(v1,v2); v4 = _mm_loadu_ps(&Z[i][j]); total =_mm_add_ps(v4,v3); _mm_store_ps(&soma,total); Z[i][j] = soma; }}}}}}

Listagem 3 Multiplicao usando Blocagem e Vetorizao com OpenMP.

A Listagem 3 exibe o algoritmo de blocagem na sua forma serial e sua verso com vetorizao intrsica utilizado no experimento. Segundo [4], dentre as implementaes comuns para obteno do produto de matrizes, foi a implementao que obteve o menor nmero de L2 cache misses, ou seja conseguiu o melhor o desempenho em relao as outras verses (e.g. Naive e Strassem) por explorar eficazmente o acesso memria cache usando a tcnica de multiplicao em blocos. Na verso implementada com vetorizao intrsica possvel notar tambm a existncia de duas diretivas OpenMP: omp_set_num_threads(nthreads) e #pragma omp parallel for collapse(3) que sero explicadas adiante. A verso implementada com a tcnica de vetorizao utiliza variveis que acessam diretamente os registradores e so defindas com o tipo __128, onde a leitura dos dados da cache para obteno dos dados das matrizes sendo alinhada (_mm_load_ps) ou no (_mm_loadu_ps), no causa prejuzos em relao a performance. O algoritmo realiza a leitura dos blocos e cada operao de multiplicao realizada no nvel dos registradores (mm_mul_ps) e (_mm_store_ps(&soma,total)), por conseguinte, os dados so colocados no endereo de memria da cache para atualizar os valores obtidos do registrador (Z[i][j] = soma). Performando desta maneira a
multiplicao em blocos, teoricamente, esperado uma reduo no tempo de processamento do clculo, uma vez que evita-se obter e colocar os dados em todas as operaes do clculo na memria cache (Seo 2.2). Ainda para melhorar a performance da multiplicao foram utilizadas 3 implementaes da tcnica de blocagem e vetorizao com: (i) OpenMp, (ii) Pthreads e (iii) Java Threads, adicionando a tcnica de programao concorrente, conforme as Listagens 3 e 4.

Algoritmo de Multiplicao Utilizando Blocagem, Vetorizao e Pthreads


void *runner(void *param) { struct v *data = param; int i,j,k; __m128 v1,v2,v3,v4,total; float x_, y_, z_, soma

Algoritmo de Multiplicao Utilizando Blocagem e Java Threads


public class MatrixMultiple implements Runnable{ public void run() {

__attribute__((aligned(16))); for (j=data->ii; j<min(data->ii+data>tamBloco,data->n); j++){ for (k=data->jj; k<min(data->jj+data>tamBloco,data->n); k++){ for (i=data->kk; i<min(data->kk+data>tamBloco,data->n); i++){ soma = 0; x_ = data->X[i][k]; y_ = data->Y[k][j]; z_ = data->Z[i][j]; v1 = _mm_loadu_ps(&x_); v2 = _mm_loadu_ps(&y_); v3 = _mm_mul_ps(v1,v2); v4 = _mm_loadu_ps(&z_); total =_mm_add_ps(v4,v3); _mm_store_ps(&soma,total); data->Z[i][j] = soma; }}} pthread_exit(0); } for (ii=0; ii<n; ii+=tamBloco){ for (jj=0; jj<n; jj+=tamBloco){ for (kk=0; kk<n; kk+=tamBloco){ pthread_t thread[nthreads]; struct v *data = (struct v *) malloc(sizeof(struct v)); data->ii=ii; data->jj=jj; data->kk=kk; data>n=n; data->X=X; data->Y=Y; data->Z=Z; data->n=n; data->tamBloco=tamBloco; for(t=0; t<nthreads; t++) { rc = pthread_create(&thread[t], NULL,runner, data); if (rc) { printf("ERROR:create=%d\n", rc); exit(-1); } } for(t=0; t<nthreads; t++) { rc = pthread_join(thread[t], &status); if (rc) { printf("ERROR:join code=%d\n", rc); exit(-1);

for (int j=ii; j<Math.min(ii+tamBloco,n); j++){ for (int k=jj; k<Math.min(jj+tamBloco,n); k++){ for (int i=kk; i<Math.min(kk+tamBloco,n); i++){ C[i][j] += A[i][k] * B[k][j]; }}} } ... public static void main(String[] args) { int n = 1000; int tamBloco = 200; long MAX_THREADS=8; for (int ii=0; ii<n; ii+=tamBloco){ for (int jj=0; jj<n; jj+=tamBloco){ for (int kk=0; kk<n; kk+=tamBloco){ for(icount=0; icount<MAX_THREADS; icount++) { s = "Thread " + ((char) (65+icount)); new Thread(new MatrixMultiple(MAX_COUNT), s).start(); }}}} }

} } }}}
Listagem 4 Multiplicao usando Blocagem e Vetorizao com Pthreads e Java Threads.

O experimento utilizou matrizes de tamanho 100 e 1000 variando o nmero de threads (1, 2, 4, 6 e 8) e tamanho de blocos 25 e 200, respectivamente, de acordo com o tamanho das matrizes. O estudo infelizmente no conseguiu alocar dinamicamente memria suficiente para tamanhos maiores (e.g. 10^4 e 10^5) utilizando alocao dinmica na arquitetura de teste com a linguagem C/C++. Neste caso o estudo poderia utilizar estratgias avanadas de alocao de memria, porm para no distorcer as comparaes entre as estratgias, o estudo preferiu comparar apenas esse conjunto de valores. O estudo tambm preferiu no adicionar as vantagens provenientes da configurao de escalonamento analisadas na parte 1 com o OpenMP e o algoritmo implementado usando blocagem e vetorizao seguiu a mesma padronizao de cdigo, aps analisar a, dentre as possveis configuraes das diretivas do OpenMP, a que obteve maior speedup na fase de planejamento do experimento. Dependo das verses do OpenMP, trabalhar com a paralelizao de laos internos e externos pode ser um problema, por isso durante a fase de planejamento do experimento foram vistas algumas informaes importantes, cuja a soluo adotada para habilitar paralelizao do algoritmo de blocagem exibida na Listagem 5. Cdigo com Erro em OpenMP
#pragma omp parallel for for (j=ii; j<min(ii+tamBloco,n); j++){ #pragma omp parallel for for (k=jj; k<min(jj+tamBloco,n); k++){ #pragma omp parallel for for (i=kk; i<min(kk+tamBloco,n); i++){ ... }}}

Cdigo Vlido em OpenMP


#pragma omp parallel for collapse(2) for (j=ii; j<min(ii+tamBloco,n); j++){ for (k=jj; k<min(jj+tamBloco,n); k++){ for (i=kk; i<min(kk+tamBloco,n); i++){ ... }}}

Listagem 5 Multiplicao usando Blocagem com OpenMP Erro e Acerto.

A multiplicao usando a tcnica de blocagem necessita de 3 laos (Listagem 5), nesse caso as verses anteriores a 3.0 do OpenMP aceitam a incluso da diretiva #pragma omp parallel for, no entanto, os laos mais internos sero executados de forma sequencial, isso porque a biblioteca libgomp do GCC, includa nas verses inferiores a 4.4, detecta que j existe um grupo de threads sendo executado no lao externo e no cria novas threads para os laos mais internos. A partir da verso 3.0 do OpenMP possvel contornar esse problema com a diretiva collapse (Listagem 5). Alm dessa, podem ser encontradas diversas prticas de performance para utilizao do OpenMP na literatura [10][11]. A partir da primeira implementao com OpenMP, as outras estratgias assemelhamse por executar de forma paralela os 3 laos mais internos do algoritmo de blocagem

com vetorizao (Listagem 3 - Esquerda), apenas se diferenciam na forma particular de implementao das suas prprias tecnologias. O OpenMP foi executado com: gcc -O2 msse3 -ftree-vectorize -ftree-vectorizer-verbose=2 -funsafe-math-optimizations std=c99 -fopenmp -I /papi/include multiplicacao_omp.c -o multiplicacao_omp -L /papi/lib lpapi, apenas a incluso das diretivas e o flag de compilao tornaram a estratgia a mais fcil de ser implementada dentre as analisadas. A utilizao da biblioteca Pthreads requereu criar adicionalmente uma estrutura com os dados a serem passados por referncia para as threads (struct v *data), invocar a criao e execuo das threads usando o modelo fork-join (pthread_create e pthread_join) com: gcc -O2 -msse3 -ftree-vectorize -ftree-vectorizer-verbose=2 -funsafe-mathoptimizations -I /papi/include multiplicacao_pthreads.c -o multiplicacao_pthreads -L /papi/lib -lpapi pthread, por isso foi uma das mais trabalhosas. A programao de threads na linguagem Java mais simples, apenas a criao de uma classe que implementa a interface Runnable e o mtodo run() com bloco de cdigo a ser executado pelas threads. De forma que a programao concorrente na linguagem Java tambm utiliza o modelo fork-join, tambm foi necessrio invocar a criao e executao das threads, porm a unio aps o processamento (join) realizado automaticamente, por isso no preciso invocar as threads para este fim, como feito na implementao com Pthreads.

3 Resultados
O estudo utilizou um notebook de 32 bits, com processador Intel Core2 Duo Processor T5550 (2048KB L2 Cache, 1.83 GHz, 667 MHz FSB) e memria de 3GB (667 MHz DDR2), sistema operacional Microsoft Windows Vista Business (Verso 6.0, Compilao 6002, Service Pack 2) o ambiente de desenvolvimento Eclipse 3.5.1 (Build 20090920-1017), com o CDT Development Kit para C/C++, MingW32 e a bilbioteca PAPI 5.0.0. Alm das linguagens de programao C e Java 7, com o GCC verso 4.6.2 e OpenMP 3.1 no Linux Ubuntu 12.4.1 embarcado em um pendrive via porta USB como ambiente de experimentao, com a finalidade exclusiva de execuo do programa construdo nesse trabalho para os testes com OpenMP e Pthreads (Seo de soluo). A coleta das mtricas: tempo de execuo, L2 Cache Miss, MFLOPS e CPI foram realizadas atravs das classes instrumentadas com a biblioteca PAPI [6] para a linguagem C/C++ e no caso do Java, apenas foi possvel obter o tempo de execuo com instrumentos de medio da prpria linguagem.

Figura 2 Anlise de Desempenho do Algoritmo de Ordenao.

A Figura 2 exibe as mtricas coletas nas classes que foram instrumentas com o PAPI [6]. Foram analisados: o tempo de execuo do programa com o incremento do nmero de threads (1, 2, 4, 6 e 8), a quantidade de MFLOPS (milhes de instrues de ponto flutuante por segundo), a quantidade de cache misses [4], o CPI, o speedup [7] e a eficincia [7]. O menor tempo de execuo do programa foi obtido quando foram utilizadas apenas 2 threads, isso se justifica pelo ao fato da arquitetura utilizada no experimento possuir apenas 2 cores. Como consequncia, o speedup alcanado tambm foi superior em 2,4x a verso serial e a quantidade de MFLOPS foi a maior entre os testes realizados. Entretanto, no foi possvel reduzir a quantidade de cache misses com apenas 2 threads em relao ao nmero de threads 1, 6 e 8 e tambm o nmero de ciclos por instruo (CPI), porm em todos os casos em houve uma melhoria em todas as mtricas em relao a verso serial do programa (s).

Figura 3 Anlise de Desempenho do Algoritmo de Nmeros Primos.

Aps a avaliao do experimento com o programa de ordenao ser satisfatrio em relao a verso serial de um programa, alm de incrementar o nmero de threads (Figura 3) o experimento tambm analisou o desempenho com e sem o uso das 3 clusulas de escalonamento e diferentes tamanhos de blocos. Foi implementado um programa simples para verificao de nmeros primos, onde o tamanho do lao foi definido por cada elemento do conjunto N={10 , 10 , 10 , 10 }, perfazendo assim 4 execues para cada nmero de thread. A primeira comparao (Figura 3) refere-se ao incremento dos valores de N e anlise das mtricas de tempo de execuo, speedup e eficincia sem o uso de clasulas de escalonamento. possvel notar, que variando-se o tamanho do problema e o nmero de threads, o speedup no linear, exceto no primeiro grfico (Figura 3 Esquerda superior) e em todos os grficos a melhor eficincia foi obtida com 2 threads .

Figura 4 Anlise de Desempenho das Clasulas de Escalonamento.

Ainda analisando o experimento com nmeros primos (Figura 4) possvel visualizar que o maior speedup foi alcanado, na maior parte das vezes, pela figura em verde que representa o escalonamento esttico. O motivo da melhora na performance se deve ao uso da clusula de escalonamento esttica com chunk ou bloco de tamanho 5. posvel notar que o escalonamento esttico tambm se destaca pelo maior speedup em todos os momentos do experimento para um nmero de threads igual a 4 que o dobro da quantidade de cores disponveis na arquitetura, com exceo ao primeiro e penltimo momento do estudo (Figura 4 - Esquerda). O maior speedup entre as execues para tamanhos muitos grandes (Figura 4 Direita Inferior) foi obtido com a clasula esttica com chunk de tamanhos 10 e 5, porm a eficincia foi piorando com o aumento do nmero de threads (Figura 5).

Figura 5 Anlise de Eficincia das Clasulas de Escalonamento.

As Figuras 6, 7 e 8 exibem os resultados da multiplicao utilizando a estratgia hbrida de blocagem, vetorizao e programao concorrente usando OpenMP, Pthreads e Java Threads (Seo 2.2). Todos os grficos realizam uma comparao entre a verso serial do programa, que utiliza apenas tcnica de blocagem, em relao as outras que utilizam blocagem, vetorizao e programao concorrente. Para que o experimento pudesse ser comparado de forma justa em relao a plataforma forma, o estudo procurou interferir o mnimo possvel na conveno de cdigo e realizou comparaes entre as verses seriais dos programas em suas respectivas plataformas, C/C++ com Linux e Java 7 com Windows.

Figura 6 Multiplicao de Matrizes usando Vetorizao, Blocagem e Threads N=100.

A Figura 6 exibe os grficos com os resultados coletados para a multiplicao de matrizes 100 x 100 de acordo com o aumento do nmero de threads para OpenMP, Pthreads e Java Threads. O primeiro grfico (Figura 6 Esquerda Superior) demonstra que no houve uma reduo no tempo de execuo do programa em relao a verso serial, apenas que a implementao com OpenMP foi mais rpida que Pthreads a partir da incluso de 4 ou mais threads. No segundo grfico (Figura 6 Esquerda Superior) possvel conhecer o motivo do alto desempenho da verso com OpenMP em relao a verso Pthreads, pois o algoritmo com OpenMP obteve uma quantidade menor de MFLOPS que a implementao com OpenMP e tambm de cache misses L2, inclusive em relao a verso serial do programa (Figura 6 Direita Superior). No entanto, o speedup em comparao as verses seriais dos programas e as implementaes especficas de cada plataforma, a verso com Java obteve o maior speedup em relao ao aumento do nmero de threads at igualar-se com as outras solues com o nmero mximo de 8 threads, o que pode signifcar que a plataforma forma Java no escalvel quando existe uma quantidade muito elevada de threads acima do nmero de processadores (Figura 6 - Direita Inferior).

Figura 7 Multiplicao de Matrizes usando Vetorizao, Blocagem e Threads N=1000.

A Figura 7 exibe os resultados da mesma forma que a Figura 6, com a diferena do tamanho das matrizes serem 1000 x 1000. Da mesma forma que a anlise anterior, a verso serial executou a multiplicao em tempo inferior as estratgias concorrentes (Figura 7 Esqueda Superior) e a implementao com Pthreads se mostrou superior a OpenMP quando o nmero de threads igual ao nmero de cores. Porm a implementao com Pthreads foi piorando com o aumento do nmero de threads. Esse fato pode ser explicado por conta do OpenMP traballhar com o modelo de memria compartilhada com dados ao invs de tpicos explcitos (Seo 2.2), assim mantm o desempenho mesmo quando da incluso de mais threads que nmero de cores. A verso serial tambm obteve maior nmero de MFLOPS em relao as estratgias concorrentes (Figura 7 Esquerda Inferior) quando o nmero de threads igual ao nmero de cores e mais uma vez a estratgia OpenMP se manteve constante com o aumento do nmero de threads o que no aconteceu com Pthreads, entretanto em relao ao nmero de L2 cache misses a melhor estratgia foi a implementao hbrida com Pthreads (Figura 7 Direita Superior). Por fim, o speedup obtivo entre as verses hibridas implementadas foi igual a anlise anterior, sendo que a plataforma Java conseguiu ser at 1.190x mais rpida com a execuo de uma thread e o desempenho foi caindo, mostrando mais uma vez que a estratgia pode no ser escalvel quando do aumento do nmero de threads (Figura 7 - Direita Inferior).

Figura 8 Multiplicao de Matrizes usando Vetorizao, Blocagem e Threads - Eficincia.

A Figura 8 mostra a quantidade de cache misses L2 das estratgias concorrentes e a verso serial do programa de multiplicao de matrizes 1000 x 1000 com o uso da linguagem C/C++ no grfico superior, o que certifica que a verso utilizando blocagem, vetorizao e Pthreads a melhor estratgia para reduzir o nmero de cache miss L2 entre as verses analisadas. Em relao a eficincia das estratgias (Figura 8 - Inferior) a linguagem Java obteve o melhor desempenho sem dvida quando da execuo de uma nica thread e se manteve superior as demais estratgias, porm nota-se que o incremento do nmero de threads piora a sua eficincia 3x em relao ao nmero de threads anterior.

4 - Concluso
Este trabalho apresentou uma anlise dos resultados obtidos com a mudana do paradigma sequencial para a programao concorrente usando o OpenMP, considerando o aumento do nmero de threads (1-8) e diferentes formas de balanceamento de carga entre os processadores (e.g. esttica, dinmica e guiada). Tambm o estudo realizou uma implementao do jogo da vida proposto por Martin Gardner com objetivo de analisar as duas verses do programa: serial e concorrente para saber sobre a eficincia da programao concorrente em conjunta com outras tcnicas (e.g. vetorizao intrsica e tcnicas de blocagem) atravs da anlise das

mtricas de speedup e eficincia coletas quando do uso dos frameworks analisados no estudo: OpenMp, Pthreads e Java Threads. Na primeira parte do trabalho foram analisados os laos paralelizados nos programas de ordenao e nmeros primos com o OpenMP e tiveram um CPI prximo de 1, o que significa que a computao de nmeros floats foi mais intensificada, melhorando a performance da aplicaes. A aplicao de ordenao teve um speedup superior a 2x em relao a verso serial reduzindo drasticamente o tempo de execuo do programa e em todos os grficos a melhor eficincia foi obtida com 2 threads, que o nmero fsico de cores da arquitetura. J o programa de nmero de primos quando variou-se o tamanho do problema e o nmero de threads, o desempenho no foi linear. O maior speedup entre as execues para tamanhos muitos grandes (100.000.000) foi obtido com a clasula esttica com chunk de tamanhos 10 e 5, porm a eficincia foi piorando com o aumento do nmero de threads, o que suscita afirmar que a melhor estratgia de escalonamento foi deixar que o sistema operacional a gerencie. A segunda parte do estudo analisou-se a multiplicao de matrizes utilizando uma estratgia hbrida com tcnicas de blocagem, vetorizao e programao concorrente usando OpenMP, Pthreads e Java Threads. O estudo procurou interferir o mnimo possvel na conveno de cdigo e realizou comparaes entre as verses seriais dos programas em suas respectivas plataformas, C/C++ com Linux e Java 7 com Windows, sendo que a implementao com Java threads no foi possvel implementar vetorizao intrsica com SSE3. As verses implementadas com OpenMP e Pthreads no conseguiram reduzir o tempo de execuo em relao a verso serial, j a implementao com Java Threads conseguiu ser at 1.190x mais rpida com a execuo de uma thread, porm o seu desempenho foi decaindo. Foi constatado no experimento que o incremento do nmero de threads piora a sua eficincia 3x em relao ao nmero de threads anterior. O estudo ainda conclui que a melhor estratgia para reduzir o nmero de cache misses L2 entre as verses analisadas na linguagem C/C++ foi a implementao hbrida envolvendo as tcnias de blocagem, vetorizao e Pthreads, j na plataforma Java no foi possvel coletar essa mtrica. Este estudo sugere como trabalhos futuros analisar porque a mquina virtual Java no escalvel quando do aumento do nmeros de threads e explorar tambm um catlogo de melhores prticas de codificao com OpenMP e Pthreads com o objetivo de paralelizao de laos aninhados. Por conta do estudo realizar comparaes entre as plataformas Java e C/C++ no foi possvel variar os algoritmos, pois a plataforma Java no suporta vetorizao intrsica e o estudo no aprofundou a comparao entre OpenMP e Pthreads para melhorar o speedup alterando a conveno de cdigo para no distorcer ainda mais as variveis de controle do experimento. Tambm outra sugesto de trabalho futuro investigar formas de coletar as mtricas de MFLOPS, CPI e L2 cache miss na plataforma Java, porque a bilbioteca PAPI, utilizada no

experimento, permite apenas a instrumentao de programas em C/C++ para coletar essas mtricas.

Referncias
[1] Berlin, K., Huan, J., Jacob, M., Kochhar, G., Prins, J., Pugh, B., Sadayappan, P. , Spacco, J., Tseng, C.. (2012) Evaluating the Impact of Programming Language Features on the Performance of Parallel Applications on Cluster Architectures. Acesso em 07 de novembro de 2012, http://www.cs.umd.edu/Honors/reports/lcpc03.pdf. [2] Milane, R. C.. (2010) Computao Verificada Aplicada Resoluo de Ssistemas Lineares Intervalares Densos em Arquiteturas Multicore. Dissertao de Mestrado PUC-RS. [3] Silva, J. B.. (2012) Uma Anlise de Vetorizao Automtica do Compilador GCC. Relatrio de Pesquisa, Instituto de Cincia e Tecnologia, Universidade Federal de So Paulo, Campus So Jos dos Campos, So Paulo-SP. Acesso em 07 de novembro de 2012,http://www.jaguaracisilva.blogspot.com.br/2012/10/uma-analise-devetorizacao-automatica.html. [4] Silva, J. B.. (2012) Anlise de Performance na Obteno de Produtos de Matrizes. Relatrio de Pesquisa, Instituto de Cincia e Tecnologia, Universidade Federal de So Paulo, Campus So Jos dos Campos, So Paulo-SP. Acesso em 07 de novembro de 2012,http://www.jaguaracisilva.blogspot.com.br/2012/10/analise-de-performance-naobtencao-de.html. [5] OpenMp..(2012) OpenMP Tutorial. Acesso em 07 de novembro de 2012, https://computing.llnl.gov/tutorials/openMP/. [6] PAPI...(2012) The Performance API. Acesso em 07 de novembro de 2012, http://icl.cs.utk.edu/papi/overview/index.html. [7] Oliveira, O..(2012) Computao Paralela - Medidas de Performance. Acesso em 07 de novembro de 2012, http://nautilus.fis.uc.pt/personal/orlando/aulas/CParalela2005/Performance.pdf. [8] D., Aberdeen, J., Jonathan..(2000) Emmerald: A Fast Matrix-Matrix Multiply Using Intels SSE Instructions. Acessado em 10 de novembro de 2012, http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.23.6754&rep=rep1&type= pdf. [9] Maleki, Seeed, Gao, Yaoqing, Garzaran, M. J., Wong, T., Padua, D. A.. (2011) An Evaluation of Vectorizing Compilers. International Conference on Parallel Architectures and Compilation Techniques (PACT 2011).

[10] Helsgaun, K.. (2012) How to get Good Performance by Using OpenMP. Acesso em 10 de novembro de 2012, http://www.akira.ruc.dk/~keld/teaching/IPDC_f10/Slides/pdf4x/4_Performance.4x.pdf [11] Chandra, R., Dagum, L., kohr, D., Maydan, D., McDonald, J., Menon, R..(2001) Parallel Programming in OpenMP. Captulos 3 e 4. Elsevier, San Diego.

S-ar putea să vă placă și