You are on page 1of 11

Anlise de Performance na Obteno de Produtos de Matrizes

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: O produto de matrizes um tema profundamente pesquisado, pois sugere


uma maior facilidade na comparao de performance de programas com a utilizao da algebra linear e algumas mtricas para esse fim. Neste trabalho so apresentadas 4 estratgias de implementao: (i) multiplicao normal de matrizes, (ii) multiplicao utilizando blocos, (iii) implementao do algoritmo de Strassen e (iv) utilizao da biblioteca ATLAS. O objetivo do estudo comparar a eficincia entre os algoritmos diante das barreiras advindas da conveno de cdigo e estratgias de acesso memria cache.

Palavras-chaves: Performance, Algoritmo, Matrizes, Algebra Linear, Cache, ATLAS.

1 - Introduo
As Unidades Centrais de Processamento (CPU) so hoje muito mais sofisticadas do que eram h apenas 30 anos atrs. Naquele tempo a sua freqncia era equivalente ao do barramento de memria de acesso dados e o acesso memria era apenas um pouco mais lento do que o acesso aos registradores e isso mudou drasticamente no incio dos anos 90, quando os fabricantes aumentaram a freqncia do Core, assim, superando a freqncia do barramento de memria e, consequentemente, aumentado o desempenho das CPUs. Em contrapartida os chips de memria RAM no aumentaram a sua frequncia, o que proporcionalmente caracteriza-se um desequilbrio e impe certas barreiras na performance das aplicaes. Do ponto de vista das estratgias para contornar esse problema e em virtude das novas tecnologias de programao, este artigo apresenta brevemente os conceitos relacionados com o problema de limitao de acesso memria e como utilizar a memria cache de forma eficiente atravs das tcnicas de programao na seo 2. Apresenta um experimento nas sees 3 e 4 sobre multiplicao de matrizes o qual implementa as 3 principais abordagens encontradas na literatura: (i) multiplicao normal de matrizes, (ii) multiplicao de matrizes utilizando blocos, (iii) multiplicao de matrizes utilizando o algoritmo de Strassen, alm da utilizao da biblioteca ATLAS. Tambm, inclui uma anlise dos resultados na seo 4, alm da concluso e as referncias utilizadas neste trabalho.

2 Mmoria Cache

Um computador pode ter uma alta velocidade no acesso SRAM alm da grande quantidade de DRAM. Uma implementao possvel seria dedicar uma certa rea do espao de endereo do processador contendo a SRAM e o resto da DRAM. A tarefa do sistema operacional ento otimizar a distribuio de dados para fazer uso da SRAM. Basicamente, a SRAM serve nesta situao como uma extenso do registro do processador. Embora esta seja uma possvel implementao isto no vivel e ignorar o problema de mapeamento dos recursos fsicos de memria SRAM para um endereo virtual exigiria administrar cada processo de atribuio nesta regio da memria, onde o tamanho da regio poderia variar de processador para processador. Cada mdulo que faz parte de um programa iria reivindicar a sua quota na memria rpida, o que introduziria custos adicionais de sincronizao. Em suma, os ganhos na obteno do acesso memria rpida seriam consumidos completamente pela sobrecarga de gesto dos recursos. Assim, em vez de colocar a SRAM, sob o controle do sistema operacional ou do usurio, o recurso transparentemente utilizado e administrado pelos processadores. Deste modo, a SRAM usada para fazer cpias temporrias de uma cache de dados na memria principal, sendo susceptivelmente utilizada em breve pelo processador. Isto possvel porque o cdigo de um programa e dos seus dados possuem localidades temporal e espacial, que ao longo de curtos perodos de tempo, h uma boa chance do mesmo cdigo do programa ou dos seus dados serem reutilizados. Para a utilizao do cdigo, o que significa que existe provavelmente loops ou iteraes no cdigo para que o mesmo cdigo seja executado diversas vezes (o caso perfeito para localidade espacial), enquanto o acesso dados limitado a pequenas regies. Apesar da memria ser utilizada em perodos curtos de tempo no so feitos juntos h uma grande chance de que os mesmos dados sejam reutilizados antes do tempo (localidade temporal). Para o cdigo do programa isto significa, por exemplo, que em um ciclo de processamento a chamada a funo esteja localizada em outras partes do endereo de memria e a funo pode estar distante da memria, necessitar de mais chamadas para essa funo da prxima vez. Assim os dados requerem uma quantidade de memria adicional para serem utilizados de uma s vez (o tamanho do conjunto de trabalho) que logicamente limitada. Por isso o conceito de localidade a chave para utilizao eficiente de caches da CPU hoje. Um clculo simples pode demostrar o quo eficaz a cache pode ser: suponha que o acesso memria principal leva 200 ciclos e o acesso memria cache 15. Um cdigo de aplicao que usa 100 elementos de dados por 100 vezes cada um vai gastar 2.000.000 ciclos em operaes de memria, porm se houver uma cache ser apenas 168.500 ciclos, se todos os dados forem armazenados em cache, isso significa uma melhoria de 91,5%.

O problema o tamanho limitado da cache e isso se faz necessrio ter boas estratgias para determinar o que deve estar nela em um dado momento. Uma vez que nem todos os dados do conjunto de trabalho pode ser utilizado exatamente ao mesmo tempo, podemos utiizar tcnicas para substituir temporariamente alguns dados em cache por outros. Uma tcnica, por exemplo, chama-se prefetching, cuja idia remover alguns custos de acesso memria principal, j que acontece de forma assncrona em relao a execuo do programa. Assim, estas tcnicas podem ser usadas para fazer a cache parecer maior do que realmente [2]. Outra se d atravs da programao, quando do emprego de algumas tcnicas, cujo o objetivo desse trabalho demonstr-las atravs das prximas sees: suas implementaes e resultados.

3 Soluo
Para demonstrar na prtica os problemas encontrados no acesso a memria cache, este estudo construiu trs algoritmos que sero apresentados a seguir de forma breve. 3.1 Multiplicao Normal de Matrizes
for (i = 0 ; i < n; i++ ) for (j = 0; j < n; j++) for (k = 0; k < n; k++) mr[i][j] = mr[i][j] + a[i][k] * b[k][j];

Listagem 1. 3.2 Multiplicao de Matrizes Usando Bloco for (ii=0; ii<n; ii+=tamBloco) for (jj=0; jj<n; jj+=tamBloco) for (kk=0; kk<n; kk+=tamBloco) for (i=ii; i<min(ii+tamBloco,n); i++) for (j=jj; j<min(jj+tamBloco,n); j++) for (k=kk; k<min(kk+tamBloco,n); k++) mr[i][j] = mr[i][j] + a[i][k] * b[k][j]; Listagem 2. 3.3 Multiplicao de Matrizes Usando o Algoritmo de Strassen float **multMatrizUsandoStrassen(int n, float **a, float **b, float **mr){ if (n == 1) { mr[0][0] = a[0][0] * b[0][0]; return mr; } int nsub = n/2; // tamanho das submatrizes

for (i = 0; i < nsub; i++) { // Divide cada matriz em 4 matrizes for (j = 0; j < nsub; j++) { A[i][j] = a[i][j]; B[i][j] = a[i][j + nsub]; C[i][j] = a[i + nsub][j]; D[i][j] = a[i + nsub][j + nsub]; E[i][j] = b[i][j]; F[i][j] = b[i][j + nsub]; G[i][j] = b[i + nsub][j]; H[i][j] = b[i + nsub][j + nsub]; } } // P0 = (A + D)*(E + H) ADD(nsub, A, D, T); ADD(nsub, E, H, U); multMatrizUsandoStrassen(nsub, T, U, P0); // P1 = (C + D)*E ADD(nsub, C, D, T); multMatrizUsandoStrassen(nsub, T, E, P1); // P2 = A*(F - H); SUB(nsub, F, H, T); multMatrizUsandoStrassen(nsub, A, T, P2); // P3 = D*(G - E) SUB(nsub, G, E, T); multMatrizUsandoStrassen(nsub, D, T, P3); // P4 = (A + B)*H ADD(nsub, A, B, T); multMatrizUsandoStrassen(nsub, T, H, P4); // P5 = (C - A) * (E + F) SUB(nsub, C, A, T); ADD(nsub, E, F, U); multMatrizUsandoStrassen(nsub, T, U, P5); // P6 = (B - D) * (G + H) SUB(nsub, B, D, T); ADD(nsub, G, H, U); multMatrizUsandoStrassen(nsub, T, U, P6); // J = P2 + P4 ADD(nsub, P2, P4, J);

// K = P1 + P3 ADD(nsub, P1, P3, K); // I = (P0 + P3) - (P4 + P6) ADD(nsub, P0, P3, T); ADD(nsub, T, P6, U); SUB(nsub, U, P4, I); // L = (P0 + P2) - (P1 + P5) ADD(nsub, P0, P2, T); ADD(nsub, T, P5, U); SUB(nsub, U, P1, L); for (i=0; i<nsub; i++) { //converge as submatrizes em uma for (j=0 ; j<nsub; j++) { mr[i][j] = I[i][j]; mr[i][j + nsub] = J[i][j]; mr[i + nsub][j] = K[i][j]; mr[i + nsub][j + nsub] = L[i][j]; } } return mr; } Listagem 3 Construdo Baseado em [1]. 3.4 Trecho de Execuo do Programa printf("Multiplicacao normal IJK\n"); s = PAPI_get_real_usec(); Z = multIJK(n, X, Y, Z); e = PAPI_get_real_usec(); setPapiFinal(e, retval, EventSet, s); printf("Usando Strassen \n"); /* chama a funcao pra fazer multiplicacao das matrizes usando Strassen */ Z = Liberar_matriz_real (n, Z); Z = Alocar_matriz_real(n); s = PAPI_get_real_usec(); Z = multMatrizUsandoStrassen(n, X, Y, Z); e = PAPI_get_real_usec(); setPapiFinal(e, retval, EventSet, s); printf("Usando blocos IJK\n"); /* chama a funcao pra fazer multiplicacao das matrizes usando bloco */ Z = Liberar_matriz_real (n, Z); Z = Alocar_matriz_real(n);

s = PAPI_get_real_usec(); Z = multUsandoBlocoIJK(tamBloco, n, X, Y, Z); e = PAPI_get_real_usec(); setPapiFinal(e, retval, EventSet, s); Listagem 4.

4 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, PAPI 5.0.0 e ATLAS 3.8.3, alm da linguagem de programao C e o 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 (Seo de Soluo) para coleta das mtricas, atravs das classes instrumentadas com a biblioteca PAPI: Tempo de Execuo, CPI, MFLOPS e Cache Misses.
70000000 60000000 50000000 40000000 30000000 20000000 10000000 0 100 200 300 400 500 600 700 800 900 1000 Tempo L2 Cache Miss MFLOPS CPI

Figura 1 - Multiplicao Normal (IJK).


70000000 60000000 50000000 40000000 30000000 20000000 10000000 0 100 200 300 400 500 600 700 800 900 1000 Tempo L2 Cache Miss MFLOPS CPI

Figura 2 - Multiplicao Normal (JIK).

70000000 60000000 50000000 40000000 30000000 20000000 10000000 0 100 200 300 400 500 600 700 800 900 1000 Tempo L2 Cache Miss MFLOPS CPI

Figura 3 - Multiplicao Normal (JKI).


80000000 70000000 60000000 50000000 40000000 30000000 20000000 10000000 0 100 200 300 400 500 600 700 800 900 1000

Tempo L2 Cache Miss MFLOPS CPI

Figura 4 - Multiplicao Normal (KIJ).


70000000 60000000 50000000 40000000 30000000 20000000 10000000 0 100 200 300 400 500 600 700 800 900 1000 Tempo L2 Cache Miss MFLOPS CPI

Figura 5 - Multiplicao Normal (KJI).

35000000 30000000 25000000 20000000 15000000 10000000 5000000 0 100 200 300 400 500 600 700 800 900 1000 Tempo L2 Cache Miss MFLOPS CPI

Figura 6 Multiplicao com o Algoritmo de Strassen. As Figuras 1-5 mostram a execuo do algoritmo construdo (Listagem 1) para multiplicao de matrizes N x N com tamanhos de 100-1000, tambm o preenchimento dessas matrizes foi dado aleatoriamente, onde C = A x B. Nas figuras possvel notar que durante a execuo foram feitos arranjos para verificar a diferena de peformance entre eles (e.g. Tempo, Cache Miss, MFLOPS, CPI), onde a configurao KIJ obteve o maior desempenho, por executar a multiplicao em menor tempo. A Figura 6 demonstra a eficincia do algoritmo de Strassen, que apesar de reduzir a complexidade assinttica conforme [1], no reduziu o tempo da multiplicao em relao ao algoritmo normal com a configurao KIJ. Entretanto, um detalhe chama bastante a ateno, o fato de haver reduzido drasticamente o nmero de ausncia de dados no cache L2. Isso se deve ao fato que o algoritmo de Strassen divide uma matriz em submatrizes e ao realizar os clculos sobre essa poro de cdigo e dados no cache L2, provavelmente ambos encontram-se mais vezes dentro do cache durante essas operaes.
200000 150000 100000 50000 0 100 200 10 20 50 CPI Tempo L2 Cache Miss MFLOPS 0 1 10 20 50 100 200 CPI 1 10 20 50 100 200 CPI 1 Tempo L2 Cache Miss MFLOPS 200000 150000 100000 50000 0 1 10 20 50 100 200 CPI Tempo L2 Cache Miss MFLOPS L2 Cache Miss MFLOPS Tempo

200000 150000 100000 50000 0

150000 100000 50000

Figura 7 Multiplicao com Blocos para Tamanho 200.


6000000 5000000 4000000 3000000 2000000 1000000 0 1 10 20 50 100 200 Tempo L2 Cache Miss MFLOPS CPI 7000000 6000000 5000000 4000000 3000000 2000000 1000000 0 1 10 20 50 100 200 Tempo L2 Cache Miss MFLOPS CPI

8000000 6000000 4000000 2000000 0 1 10 20 50 100 200

Tempo L2 Cache Miss MFLOPS CPI

5000000 4000000 3000000 2000000 1000000 0 1 10 20 50 100 200 CPI Tempo L2 Cache Miss MFLOPS

Figura 8 Multiplicao com Blocos para Tamanho 600.


80000000 60000000 40000000 20000000 0 1 10 20 50 100 200 CPI 80000000 60000000 L2 Cache Miss MFLOPS CPI 40000000 20000000 0 1 10 20 50 100 200 1 10 20 50 100 200 CPI L2 Cache Miss MFLOPS L2 Cache Miss MFLOPS Tempo 80000000 60000000 40000000 20000000 0 1 10 20 50 100 200 CPI Tempo L2 Cache Miss MFLOPS

70000000 60000000 50000000 40000000 30000000 20000000 10000000 0

Tempo

Tempo

Figura 9 Multiplicao com Blocos para Tamanho 1000. Conforme constatado quando da utilizao do algoritmo de Strassen, o nmero de ausncia de dados no Cache L2 pode ser contornado utilizando uma estratgia de

buffer: dividindo o tamanho das matrizes e delimitando os dados e cdigo da aplicao em poroes que caibam no Cache L2. Neste caso, a implementao do algoritmo que utiliza blocos (Listagem 2) poder ser uma grande estratgia de conveno de cdigo que aumenta a performance no acesso aos dados. Em todos os casos apresentados (Figuras 7-9) o experimento demonstra que preciso ter cautela na definio do tamanho do bloco, porque em casos onde o bloco ultrapassaria a largura do Cache (2048KB) ou a definio de um buffer com tamanho muito pequeno, o benefcio da estratgia no seria alcanado. Em todos os casos a medida em que se definiu melhor o tamanho dos blocos (e.g. 10, 20, 50, 100) houve um melhoria no acesso a memria Cache, como consequncia, o tempo de execuo do algoritmo tambm foi melhor. Comparando os arranjos da mesma forma apresentada anterioremente, tambm possvel perceber que uma das configuraes destaca-se pelo menor tempo de execuo, neste caso, a configurao JKI (abaixo e a direita nas Figuras 7-9).
1000 900 800 700 600 500 400 300 200 100 0 20000000 40000000 60000000 80000000

L2 CM - Bloco L2 CM - ATLAS L2 CM - Strassen L2 CM - Normal

Figura 10 Eficincia no Acesso Memria Cache L2. A Figura 10 apresenta a eficincia dos algoritmos de multiplicao em relao ao acesso memria Cache L2, neste caso a eficincia foi medida pelo algoritmo que conseguiu a menor quantidade de Cache Miss. possvel notar claramente que a biblioteca ATLAS [4] implementa um algoritmo de multiplicao com maior desempenho. Para valores de N superiores a 600 o algoritmo de Strassen tambm obteve uma boa performance em relao ao algoritmo de multiplicao normal, o que pode suscitar que a biblioteca ATLAS tambm utiliza a tcnica de blocagem para alcanar melhores resultados. Entretanto, em comparao ao algoritmo implementado com blocos de tamanho 200 e melhor configurao (JKI) a estratgia de acesso aos dados deste ltimo foi melhor em relao a biblioteca ATLAS para valor de N=1000.

5 - Concluso
Por conta dos entraves encontrados na diferena de velocidades entre as CPUs modernas e o acesso a memria, este estudo analisou a eficincia no clculo do

produto de matrizes para comparao de 4 mtricas de performance: Tempo, MFLOPS, CPI e Cache L2 Miss durante a execuo de um programa. Foram apresentadas 4 estratgias de implementao: (i) multiplicao normal de matrizes, (ii) multiplicao utilizando blocos, (iii) implementao do algoritmo de Strassen e (iv) utilizao da biblioteca ATLAS. O estudo comparou a eficincia entre os algoritmos diante das barreiras advindas da conveno de cdigo e estratgias de acesso memria cache e pode-se concluir que a utilizao do algoritmo de Strassen, apesar de diminuir a complexidade assinttica em relao ao algoritmo comumente utilizado, no reduziu o tempo de execuo do clculo, porm serviu de base para investigao de outras estratgias, como a multiplicao em blocos. A estratgia de multiplicao em blocos reduziu drasticamente o nmero de ausncias do cdigo de aplicao e dados no Cache L2 (Cache Miss) e o tempo de soluo tambm foi o melhor entre as estratgias comumente encontradas na literatura, entretanto, em comparao ao tempos de soluo, a biblioteca ATLAS foi imbatvel. Tambm importante salientar que o algoritmo de Strassen, apesar de obter uma maior eficincia no acesso memria Cache L2, no quesito tempo obteve o pior desempenho entre as solues implementadas. Por ocorrncia do aumento de acesso aos dados que no esto no Cache o nmero de instrues ir aumentar (MFLOPS) e consequentemente os ciclos necessrios para realizar cada operao (CPI). Embora o Cache L2 esteja sob controle da CPU, ns podemos melhorar as convenes de cdigo para obter uma melhoria de performance [3]. O uso das configuraes KIJ e JKI foram as melhores nas duas abordagens implementadas, Listagens 1 e 2, respectivamente, porque utilizaram esse artifcio. A organizao dos dados na memria Cache L2 segue um ordenamento pelas linhas e colunas da matrizes (e.g. A[0][0], A[0][1]...A[10][9], A[10][10] ) e ao buscar os dados contidos em linhas e colunas que no esto no Cache L2, ocorre o Cache Miss e isso pode ser minimizado pela melhoria da conveno de cdigo, conforme visto neste trabalho.

Referncias
[1] Cormen, T. H., Leiserson, C.E., Rivest, R.L., Stein, C.. "Introduction to Algorithms", MIT Press, Cambridge, Massachusetts, London, 2nd Ed, 2001. [2] Drepper, U.. What Every Programmer Should Know About Memory, Red Hat Inc, 2007. [3] Pacheco, P. S.. An Introduction to Parallel Programming. Elsevier, 2011. Chapter 2, pg. 1925. [4] ATLAS. Automatically Tuned Linear Algebra Software, http://math-atlas.sourceforge.net/. Acesso em 12 de outubro de 2012.