Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

8
Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes Eduardo de Lucena Falcão, Alisson Vasconcelos Brito, Alexandre Nóbrega Duarte Departamento de Informática Universidade Federal da Paraíba (UFPB) {eduardolfalcao, alisson.brito, alexandrend}@gmail.com Abstract. Operations of matrix multiplications are widely used in scientific applications, and commonly have huge dimensions. Another problem derived of the first it’s the matrix-chain multiplications, which also has scientific applicability. Thus, efficient algorithms for matrix multiplications (or matrix- chains) are extremely useful, as also are new approaches or even distributed versions of already known algorithms. The present work aims at analyzing some approaches for the solution of the matrix-chain multiplications problem, implement it and measure their runtime to analyse the efficacy of the same. 1. Introdução Desde 1946, com a criação do primeiro computador eletrônico para fins genéricos (o ENIAC), pode-se perceber a rápida evolução dos processadores ao longo de décadas [Koomey et al 2009]. A principal razão para tal é a evolução tecnológica que permite cada vez mais a construção de transistores sempre menores e mais baratos, de tal modo que mais ou menos a cada dois anos seu número dobrasse nos processadores, seguindo uma tendência conhecida como Lei de Moore [Schaller 1997]. A rápida evolução dos processadores já é um fator que constantemente tem tornado a execução de programas mais eficiente. Contudo, o aumento da velocidade dos processadores é apenas um dos fatores que podem elevar o desempenho de um programa e, além disto, é limitado por outros fatores que têm influência direta nesta melhoria, dentre eles, a memória [Alted 2010]. De nada adianta ter processadores super velozes, se a memória não é rápida o suficiente para fornecê-los dados para processamento sem que eles fiquem ociosos. Deste modo, foi preciso adotar uma nova estratégia para diminuir o tempo de espera do processador: a memória cache. A memória cache é um tipo bastante rápido de memória que serve para armazenar os dados mais frequentemente usados pelo processador, evitando na maioria das vezes que ele tenha que recorrer à “lenta” memória RAM (Random Access Memory). É interessante perceber o funcionamento da memória cache com o objetivo de usá-las da maneira mais eficiente possível, e.g. tentar minimizar o cache miss. Além de aspectos de hardware como frequência do processador e tamanho da memória, é importante utilizar as estruturas de dados e técnicas de algoritmos que mais se adéquam ao estilo de problema a ser resolvido. Adicionalmente, podemos perceber que as escolhas de ferramentas, Linguagens de Programação (LP), e APIs (Application Programer Interface), também podem ter certo impacto no desempenho da solução. O objetivo do presente trabalho é tornar evidentes os vários fatores que podem influenciar no desempenho de um programa: utilizar a memória cache de maneira inteligente; estruturas de dados e técnicas de programação mais adequados ao problema;

description

Abstract. Operations of matrix multiplications are widely used in scientific applications, and commonly have huge dimensions. Another problem derived of the first it’s the matrix-chain multiplications, which also has scientific applicability. Thus, efficient algorithms for matrix multiplications (or matrix-chains) are extremely useful, as also are new approaches or even distributed versions of already known algorithms. The present work aims at analyzing some approaches for the solution of the matrix-chain multiplications problem, implement it and measure their runtime to analyse the efficacy of the same.

Transcript of Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

Page 1: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

Estudo e Avaliação do Problema de Otimização da Multiplicação de

Cadeias de Matrizes

Eduardo de Lucena Falcão, Alisson Vasconcelos Brito, Alexandre Nóbrega Duarte

Departamento de Informática – Universidade Federal da Paraíba (UFPB)

{eduardolfalcao, alisson.brito, alexandrend}@gmail.com

Abstract. Operations of matrix multiplications are widely used in scientific

applications, and commonly have huge dimensions. Another problem derived

of the first it’s the matrix-chain multiplications, which also has scientific

applicability. Thus, efficient algorithms for matrix multiplications (or matrix-

chains) are extremely useful, as also are new approaches or even distributed

versions of already known algorithms. The present work aims at analyzing

some approaches for the solution of the matrix-chain multiplications problem,

implement it and measure their runtime to analyse the efficacy of the same.

1. Introdução

Desde 1946, com a criação do primeiro computador eletrônico para fins genéricos (o

ENIAC), pode-se perceber a rápida evolução dos processadores ao longo de décadas

[Koomey et al 2009]. A principal razão para tal é a evolução tecnológica que permite

cada vez mais a construção de transistores sempre menores e mais baratos, de tal modo

que mais ou menos a cada dois anos seu número dobrasse nos processadores, seguindo

uma tendência conhecida como Lei de Moore [Schaller 1997].

A rápida evolução dos processadores já é um fator que constantemente tem

tornado a execução de programas mais eficiente. Contudo, o aumento da velocidade dos

processadores é apenas um dos fatores que podem elevar o desempenho de um

programa e, além disto, é limitado por outros fatores que têm influência direta nesta

melhoria, dentre eles, a memória [Alted 2010]. De nada adianta ter processadores super

velozes, se a memória não é rápida o suficiente para fornecê-los dados para

processamento sem que eles fiquem ociosos. Deste modo, foi preciso adotar uma nova

estratégia para diminuir o tempo de espera do processador: a memória cache.

A memória cache é um tipo bastante rápido de memória que serve para

armazenar os dados mais frequentemente usados pelo processador, evitando na maioria

das vezes que ele tenha que recorrer à “lenta” memória RAM (Random Access

Memory). É interessante perceber o funcionamento da memória cache com o objetivo

de usá-las da maneira mais eficiente possível, e.g. tentar minimizar o cache miss.

Além de aspectos de hardware como frequência do processador e tamanho da

memória, é importante utilizar as estruturas de dados e técnicas de algoritmos que mais

se adéquam ao estilo de problema a ser resolvido. Adicionalmente, podemos perceber

que as escolhas de ferramentas, Linguagens de Programação (LP), e APIs (Application

Programer Interface), também podem ter certo impacto no desempenho da solução.

O objetivo do presente trabalho é tornar evidentes os vários fatores que podem

influenciar no desempenho de um programa:

utilizar a memória cache de maneira inteligente;

estruturas de dados e técnicas de programação mais adequados ao problema;

Page 2: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

ferramentas e LPs a serem utilizadas.

Para tal, será apresentado o problema da Multiplicação de Cadeias de Matrizes

explicado no tópico abaixo, avaliando seu tempo de execução quando projetado de

diferentes modos.

2. O Problema da Multiplicação de Cadeias de Matrizes

No problema da multiplicação de cadeias de matrizes, recebe-se como entrada uma

cadeia de n matrizes, e retorna-se o produto delas. É fácil perceber,

que por motivos da lógica da multiplicação de matrizes que a matriz deve ter número

de linhas igual ao número de colunas da matriz .

O problema em sua essência é trivial, pois se multiplicarmos sequencialmente as

n matrizes obteremos facilmente o resultado esperado. O maior desafio que reside neste

problema é otimizar o desempenho do programa que o resolve. Para tal, podemos

explorar técnicas de programação aplicadas ao problema específico, além de buscar

sempre explorar o processamento máximo do computador de maneira inteligente.

3. O Problema do Cache Miss

O cache miss acontece quando o processador necessita de um dado, e este não

está na memória cache. Tal fato obriga o processador a buscar um bloco de dados –

constituído por um número fixo de palavras - diretamente na memória RAM, trazendo-

os para a memória cache e em seguida fornecendo a palavra requerida ao processador.

Em razão do fenômeno da localidade espacial, quando um bloco de dados é

trazido para a memória cache para satisfazer uma dada referência à memória, é provável

que futuras referências sejam feitas para outras palavras desse bloco (Figura 1)

[Stallings 2010]. Sendo assim, é fácil perceber que quando acontece vários cache miss

na execução de um programa, seu desempenho tenderá a cair. Os programadores podem

utilizar a propriedade da localidade espacial dentre outras relacionadas ao

funcionamento da memória cache no intuito de minimizar os cache miss e tornar a

execução dos programas mais rápidos.

Um exemplo simples que comprova tal fato é o problema da soma de todos os

elementos de uma matriz. Se para somar todos os elementos de uma matriz o algoritmo

percorrer as colunas ao invés das linhas, haverá maior probabilidade de ocorrer o cache

miss. Para comprovar este fato, foi implementado um programa simples na LP Java, que

mede o tempo de execução das duas formas de se percorrer uma matriz quadrada de

tamanho 10000. Ao percorrer as colunas o programa executou em 2886 milissegundos,

e ao percorrer as linhas o programa executou em 53 milissegundos. Podemos perceber

que quando minimizamos o cache miss tivemos um speedup superior a 50 vezes.

A Figura 1 ilustra o fenômeno da localidade espacial ocorrendo no problema da

soma dos elementos de uma matriz. Quando o primeiro elemento da matriz ( ) é

buscado para efetuar a soma, o processador buscará um bloco de dados sequencial (uma

linha do array, ou parte dela) para a memória cache. Se forem percorridas as linhas da

matriz, provavelmente os próximos elementos a serem somados (representados através

da cor verde e amarela na Figura 1) estarão na memória cache, o que ocasiona um cache

hit, tornando o acesso ao dado mais rápido. Se forem percorridas as colunas, há maior

probabilidade de que os próximos elementos a serem somados (vermelho) não estejam

na memória cache, ocorrendo um cache miss.

Page 3: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

Figura 1 – Ilustração do fenômeno da localidade espacial.

4. Análise e Detalhamento de Possíveis Soluções para o Problema de

Multiplicação de Cadeias de Matrizes

Neste tópico serão descritas algumas abordagens possíveis para a solução deste

problema, que em alguns casos são complementares e em outros são antagônicas.

4.1. Abordagem Sequencial

Na abordagem sequencial multiplicam-se sequencialmente as n matrizes para obter seu

produto. Por exemplo, para uma cadeia com quatro matrizes a ordem de multiplicação

seria a seguinte: . O código da Tabela 1 recebe duas matrizes A e B

como entrada, e retorna a matriz C como resultado da multiplicação entre A e B. A

partir da Tabela 1 pode-se perceber que a complexidade para realizar a multiplicação

dessas duas matrizes é , sendo n relativo ao tamanho das matrizes (linhas e

colunas). Logo, para t matrizes tem-se que a complexidade é .

Tabela 1 – Código para multiplicação de duas matrizes. 1 for(int i=0; i<a.length ;i++){

for(int j=0; j<b[0].length ;j++){ c[i][j] = 0; for(int k=0; k<a[0].length ;k++) c[i][j] += a[i][k] * b[k][j]; } }

2

3

4

5

6

7

4.2. Abordagem com Programação Dinâmica

Analisando mais cuidadosamente o código da Tabela 1, pode-se perceber que o

número de multiplicações escalares relacionadas à multiplicação de duas matrizes

respeita o cálculo exibido na Figura 2. O referente à multiplicação pode ser

descrito de maneira mais detalhada por . Sendo assim, pode-se observar que a ordem com que as matrizes são

multiplicadas pode tornar o programa mais eficiente, ou seja, reduzir o número de

multiplicações.

Figura 2 – Quantidade de multiplicações escalares para a multiplicação de duas

matrizes. Adaptado de [Cormen et al 2002].

Page 4: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

Por exemplo, para multiplicar uma cadeia de matrizes contendo três matrizes

existem duas parentizações possíveis. A Tabela 2 exibe as possíveis parentizações para

a seguinte cadeia com três matrizes: .

Tabela 2 – Possíveis ordens de multiplicação para uma cadeira com três matrizes.

1 2

A partir do cálculo fornecido pela Figura 2, é possível verificar que a ordem de

multiplicação das matrizes da linha 1 da Tabela 2 realiza 75000 multiplicações

escalares, ao passo que a ordem da linha 2 resulta em apenas 7500. Deste modo, Pode-

se perceber que para a multiplicação da cadeia já citada, apenas a ordem de

multiplicação das mesmas pode gerar um speedup de 10 vezes. É interessante perceber

que para o exemplo anterior a abordagem sequencial (4.1) é a mais rápida. Mas, deve

ser ressaltado que isso foi apenas uma coincidência, e que a probabilidade da

abordagem sequencial ser a mais rápida torna-se bastante remota quando o número de

matrizes da cadeia aumenta, o que proporciona um ganho expressivo quando se utiliza a

abordagem com a parentização otimizada.

Mas como proceder para encontrar a parentização otimizada? Através da força

bruta, o número de soluções seria exponencial em n ( ), sendo n o número de

multiplicações, tornando essa verificação inviável [Cormen et al 2002]. Uma alternativa

viável para este problema é a utilização da estratégia de Programação Dinâmica.

A Programação Dinâmica (PD) geralmente é aplicada a problemas de

otimização, ou seja, problemas que possuem várias soluções, mas deseja-se encontrar

uma solução ótima, como é o caso do corrente problema. Para aplicar a PD ao problema

da multiplicação de cadeias de matrizes são necessárias quatro etapas: caracterizar a

estrutura de uma solução ótima, que nesse caso é a estrutura de uma colocação ótima

dos parênteses; definir recursivamente o valor de uma solução ótima para o problema;

calcular o valor de uma parentização ótima; construir a solução ótima a partir das

informações coletadas. Mais detalhes sobre descrição e implementação desse problema

através da PD são fornecidos em [Cormen et al 2002].

4.3. Abordagem com Minimização de Cache Miss

O tópico 3 explicou alguns detalhes quanto a ocorrência de cache miss. Com relação ao

problema de multiplicação de matrizes, deve-se primeiramente ressaltar que a própria

natureza de seu algoritmo o faz mais suscetível à ocorrência de cache miss. Pois é

sabido que para multiplicar duas matrizes, deve-se percorrer a linha da primeira e a

coluna da segunda, e quando se percorre a coluna de uma matriz a probabilidade de

ocorrência do cache miss aumenta (vide Tópico 3).

A Tabela 1 exibe o código referente a um algoritmo tradicional para

multiplicação de matrizes. Nesse código o algoritmo sempre preencherá linha por linha

da matriz resultante. Este comportamento do algoritmo requer que o processador faça a

multiplicação de uma linha da primeira matriz com todas as colunas da segunda matriz.

Baseado no funcionamento do algoritmo, observou-se que é possível minimizar

a ocorrência do cache miss invertendo as linhas 1 e 2 da Tabela 1 (quando pertinentes),

com o objetivo de que o programa reutilize dados previamente armazenados na

memória cache. Pensou-se o seguinte (Tabela 3): se a quantidade de linhas da primeira

matriz é maior que a quantidade de linhas da segunda matriz, deve-se manter o código

Page 5: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

da Tabela 1, caso contrário, devem ser invertidas as linhas 1 e 2 da Tabela 1. Com essa

verificação, o algoritmo sempre percorrerá a “maior linha possível”, o que minimiza o

cache miss.

Tabela 3 – Código proposto para minimização do cache miss. if(a.length > b.length) <Código da Tabela 1> else <Código da Tabela 1 com linhas 1 e 2 invertidas>

Outra maneira de se reduzir o cache miss para o problema da multiplicação de

matrizes seria multiplicar a primeira matriz pela matriz transposta da segunda. Pode-se

perceber que para isto seria necessário mudar o código relativo à multiplicação das

matrizes: ao invés de multiplicar linhas da primeira matriz por colunas da segunda,

multiplicar-se-iam linhas da primeira matriz por linhas da segunda (Figura 3), o que

implicaria numa redução significativa do cache miss. Para evitar um custo adicional

para o cálculo da transposta da segunda matriz, poderia apenas ser alterado o método de

criação das mesmas para já criá-las como sendo suas próprias transpostas.

A Figura 3 exemplifica a multiplicação tradicional com uma matriz

convencional, e a multiplicação proposta com a matriz transposta. As linhas coloridas

representam os cálculos relativos à primeira linha da matriz resultante:

Linhas vermelhas: multiplicar linha 1 da primeira matriz com colunas 1, 2, ..., n

da segunda matriz;

Linhas azuis: multiplicar linha 1 da primeira matriz com linhas 1, 2, ..., n da

segunda matriz.

Figura 3 – Multiplicação convencional, e multiplicação com matrizes transpostas.

4.4. Abordagem Distribuída com Threads

A abordagem distribuída utilizando threads visa explorar a potencialidade máxima dos

processadores atuais, que geralmente possuem mais de um núcleo físico e/ou lógico.

Contudo, será explicado adiante o porquê da abordagem distribuída com threads

utilizada no presente trabalho não caracterizar uma solução ótima, apesar de explorar a

capacidade máxima do hardware.

A estratégia utilizada para esta abordagem divide a cadeia

pelo número de threads a serem criadas. Desse modo, cada thread fica encarregada de

realizar a multiplicação de uma sub-cadeia, e ao final, quando todas terminarem sua

execução, a thread principal realizará a multiplicação das matrizes resultantes. Como

exemplo, considere uma cadeia com doze matrizes ( ) utilizando a

abordagem distribuída com duas threads. Esta cadeia seria quebrada ao meio em duas

sub-cadeias e , e depois passaria pelo processo de

se encontrar a parentização ótima através da estratégia de PD. Porém, é fácil perceber

que a parentização ótima para a cadeia muito dificilmente será

Page 6: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

alcançada com a divisão da cadeia. Para alcançarmos tanto a parentização ótima e ainda

explorar a capacidade máxima do processador outra estratégia deve ser utilizada, como

por exemplo, a abordagem distribuída utilizando a biblioteca OpenMP.

4.5. Abordagem Distribuída com OpenMP

A abordagem distribuída utilizando a biblioteca OpenMP (Open Multi-Processing) não

divide a cadeia de matrizes em sub-cadeias, e portanto, mantém a parentização

otimizada encontrada através da abordagem por PD. Ao invés disso, utilizam-se

diretivas em trechos de código que são interpretadas pelo compilador, e que ao executar

o programa, ativam as threads dos processadores de acordo com as mesmas. Portanto, o

trecho de código que se pretende distribuir deve ser minuciosamente estudado para que

nenhuma violação lógica ou física seja cometida.

Para o problema da multiplicação de matrizes, foi percebido que os elementos da

matriz resultante não possuem interdependências, o que possibilita a paralelização desse

trecho de código, mais especificamente do loop for da linha 1 na Tabela 1. Deste modo,

o código ficaria como apresentado abaixo na Tabela 4.

Tabela 4 – Paralelizando com a biblioteca OpenMP. #pragma omp parallel for <Código da Tabela 1>

A biblioteca OpenMP usa o modelo de paralelismo fork-and-join, que ramifica a

thread principal em threads paralelas, e quando estas terminam de executar, a thread

principal reassume o controle do programa [Chandra et al 2001]. Existem diversas

diretivas da biblioteca OpenMP que permitem os programadores tornarem seus

programas distribuídos. A diretiva que foi utilizada na Tabela 4 atua de forma simples,

ela subdivide a quantidade total de iterações no loop for pelo número de threads a serem

utilizadas, e cada thread executará uma parte das iterações que antes eram executadas

por apenas uma thread. No problema da multiplicação de matrizes, se a matriz

resultante tiver 10 linhas, e for especificado que o processador deve trabalhar com duas

threads, a primeira computará o produto das 5 primeiras linhas da matriz resultante, e a

segunda computará as linhas restantes (Figura 4).

Figura 4 – Multiplicação com abordagem não-distribuída e distribuída.

5. Resultados

Para avaliar algumas das abordagens supracitadas pensou-se em realizar cinco

experimentos, aliando diferentes algoritmos e técnicas de programação. As cinco

estratégias são citadas abaixo, visando a cada novo experimento implementado

melhorar o desempenho do programa.

1. Linguagem de Programação Java:

a) Abordagem sequencial, maximizando o cache miss;

Page 7: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

b) Utilizando técnicas de PD, maximizando o cache miss;

c) Utilizando técnicas de PD, minimizando o cache miss;

d) Utilizando técnicas de PD, minimizando o cache miss, além de tornar a lógica do

algoritmo distribuída:

i. Em 2 Threads;

ii. Em 4 Threads;

“Maximizar o cache miss” tem o objetivo de tornar mais evidente a ocorrência

do mesmo, e prover uma noção de tempo desperdiçado quando o mesmo ocorre. Para

tal, as linhas 1 e 2 da Tabela 1 devem ser invertidas com a lógica inversa da Tabela 3.

Para minimizar o cache miss foi utilizada o código presente na Tabela 3. E para tornar a

lógica do algoritmo distribuído foi utilizada a abordagem explanada no tópico 4.4. Para

fins de padronização as matrizes foram preenchidas com 1’s.

Para tais experimentos, foi utilizado um notebook Dell Vostro 3450, Sistema

Operacional Windows 7 Professional Service Pack 1 de 64 Bits, 4 GB de memória

RAM, processador Intel® Core™ i5-250M de 2.5 GHz.

A cadeia de matrizes escolhida para os experimentos foi a seguinte:

<7000,50,30,5000,32,40,5000,80,20,4000,20,10,1000,50>, o que equivale às matrizes

. Pode-se perceber que todas as matrizes da

cadeia possuem o número de linhas muito superior ao número de colunas, ou justamente

o inverso, pois a ocorrência de cache miss torna-se mais acentuada nesses casos.

A Figura 5 exibe o gráfico com o resultado dos experimentos propostos. A

legenda na direita corresponde aos experimentos descritos anteriormente.

Figura 5 – Resultados do experimento na LP Java.

Pode-se observar que a diferença de desempenho da abordagem que usa PD para

a sequencial é deveras significante (25.8 segundos), um fato esperado e descrito nos

tópicos 4.1 e 4.2 do tópico. Analisando o resultado dos experimentos 1.b e 1.c, pode-se

notar que quando é utilizada uma estratégia de minimização de cache miss (descrita no

tópico 4.3) para o problema, obteve-se 4.7 segundos de speedup, que representa uma

evolução de quase 50%. É importante ressaltar que quando o tamanho da cadeia

aumenta, essa disparidade também tende a aumentar. Por último, ao analisar a estratégia

distribuída (descrita no tópico 4.4), pode-se perceber que tanto a 1.d quanto 1.e,

utilizando 2 e 4 threads respectivamente, apresentaram perda de desempenho. No tópico

4.4, já se havia cogitado a possibilidade dessa ocorrência devido à perda de parentização

ótima. Porém, é de se esperar que esta abordagem apresente ganho de desempenho para

determinadas cadeias de matrizes, principalmente quando o número de matrizes cresce.

0

20

40 36,7

10,9 6,2 6,6 6,4

1.a

1.b

1.c

1.d

Page 8: Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de Matrizes

5. Conclusão

O objetivo do presente trabalho foi mostrar a importância de dominar técnicas

relacionadas a projeto de algoritmos, e também conceitos de arquitetura de

computadores para obter melhor desempenho do software e hardware. Ao utilizar a

técnica de PD aliada à minimização de cache miss, obteve-se um speedup de 6x em

relação ao desempenho da solução mais simples (sequencial). É interessante perceber

que todos os algoritmos estão na mesma ordem de complexidade de tempo ( ), e

mesmo assim foram obtidos ganhos de desempenho.

Destes resultados, pode-se colher como aprendizado, que ao receber um

problema é necessário estudá-lo, visualizá-lo de diferentes perspectivas para adotar a

estratégia de solução mais eficiente. Para tal, devem ser analisados os algoritmos

pertinentes, ferramentas (APIs, bibliotecas, etc.) e LPs a serem utilizadas, além de

conceitos sobre funcionamento de software/hardware, de sistemas operacionais e da

arquitetura do computador de uma forma generalizada.

7. Trabalhos Futuros

Como possibilidades para trabalhos futuros, pode ser considerada a implementação das

abordagens já citadas na LP C++, uma vez que geralmente os programas escritos nessa

LP tendem a ter melhor desempenho que em Java. Além disso, a abordagem descrita no

tópico 4.5 parece ser a mais eficiente. O fato de aproveitar o desempenho da LP C++,

realizando a distribuição do algoritmo em quantas threads desejar, mantendo a

parentização ótima alcançada pela PD, e minimizar a ocorrência de cache miss com a

abordagem da matriz transposta, aparenta ser a melhor solução analisada no presente

trabalho. Outra tarefa interessante seria implementar os algoritmos para multiplicação

de matrizes desenvolvidos por Strassen , que possui complexidade de , ou o

desenvolvido por Coppersmith e Winograd com ordem de complexidade , aliando à alguma abordagem de minimização de cache miss para comparação dos

resultados [Robinson 2005].

Referências

Koomey, J. G., Berard, S., Sanchez, M., Wong, H. (2009) “Assessing Trends in the

Electrical Efficiency”. Final report to Microsoft Corporation and Intel Corporation.

Schaller, R. R. “Moore's law: past, present and future”. (1997) IEEE Spectrum, Vol 34,

pp. 52-59.

Alted, F. “Why Modern CPUs are Starving and what can be done about it”. (2010)

Computing in Science & Engineering, pp. 68–71.

Stallings, W. “Arquitetura e Organização de Computadores”. (2010) Editora Prentice

Hall, pp. 122-144.

Cormen, T. H., Leiserson, C. E., Rivest, R. L., Stein, C. “Algoritmos: Teoria e Prática”.

(2002) Editora Campus, pp. 259-295.

Chandra, R., Dagum, L., Kohr, D., Maydan, D., McDonald, J., Menon, R. “Parallel

Programming in OpenMP”. (2001) Editora Morgan Kaufmann.

Robinson, S. “Toward an Optimal Algorithm for Matrix Multiplication”. (2005) Society

for Industrial and Applied Mathematics. Vol. 38, Nº 9.