• Nenhum resultado encontrado

Simulando Atualiza¸c˜ao “ In-place”

No documento Integrando Haskell à Plataforma .NET (páginas 88-90)

5.6 Otimiza¸c˜oes

5.6.2 Simulando Atualiza¸c˜ao “ In-place”

Ao descrever o mecanismo de atualiza¸c˜ao de thunks na S pineless Tagless Machine, Simon Peyton Jones introduziu a t´ecnica da “atualiza¸c˜ao in-place” [Jon92], que consiste em sobrescrever a mem´oria alocada pelo thunk com o seu novo valor. Q uando isso n˜ao fosse poss´ıvel devido ao tamanho do valor ser maior do que o tamanho da closure (thunk ), seria introduzida uma indire¸c˜ao para o mesmo.

A motiva¸c˜ao para a atualiza¸c˜ao in-place estava n˜ao apenas em remover uma indire¸c˜ao, mas, principalmente, liberar a mem´oria alocada por closures j´a atualizadas.

Na CLR, bem como em outros ambiente de c´odigo gerenciado em que o principal foco est´a na seguran¸ca de tipos, n˜ao ´e poss´ıvel sobrescrever um objeto com outro, de forma que esse tipo de otimiza¸c˜ao n˜ao ´e poss´ıvel de ser empregado diretamente. A princ´ıpio, a se- guinte alternativa foi considerada para simular o efeito desejado da atualiza¸c˜ao “in-place”: adicionar ao m´etodoEnter um argumento passado por referˆencia (tipo & ) representando o endere¸co da referˆencia para a closure a ser atualizada. Ao final da computa¸c˜ao, a re- ferˆencia apontada pelo argumento recebido poderia ser atualizada, passando a referenciar o valor rec´em-computado. Isso seria poss´ıvel uma vez que tal valor poderia ser de dois

3

A remo¸c˜ao dos frames de atualiz a¸c˜ao foi uma das primeiras otimiz a¸c˜oes implementadas, quando ainda t´ınhamos menos programas sendo cobertos pelas bibliotecas do GH C. Por esse motivo n˜ao mostramos os resultados para o conjunto total de programas mostrados em outras se¸c˜oes.

5.6 otimizac¸ ˜oes 76 tipos: aplica¸c˜ao parcial ou um objeto avaliado (datatype). Ambos os tipos de objetos herdam da classeClosure, tornando poss´ıvel a atribui¸c˜ao.

Entretanto, a abordagem acima apresentou como principal desvantagem o fato de n˜ao ser mais poss´ıvel invocar o m´etodoEnterpor meio de uma tail-call atrav´es de c´odigo verific´avel. Tal limita¸c˜ao justifica-se pela natureza segura da CLR: em uma situa¸c˜ao em que a referˆencia para uma vari´avel local, por exemplo, fosse passada por referˆencia por meio de uma tail-call, os objetos referenciados pelo frame descartado seriam coletados e, dessa forma, a ´unica referˆencia existente para o objeto em quest˜ao tornar-se-ia inv´alida. Por esse motivo, descartamos tal possibilidade e sugerimos como uma poss´ıvel extens˜ao `a CLR alguma forma de intera¸c˜ao com o garbage collector que torne poss´ıvel “dispar´a-lo” para coletar closures j´a atualizadas e substituir as referˆencias a elas por seus novos valo- res. Tal extens˜ao seria bem espec´ıfica a linguagens funcionais e possivelmente envolveria algum suporte nativo a closures. Caberia tamb´em avaliar se o overhead gerado por uma extens˜ao como essa seria de fato compensado por ganhos significativos de desempenho. Infelizmente ainda n˜ao temos respostas para tais quest˜oes.

O pr´oprio GHC abandonou a id´eia da atualiza¸c˜ao “in-place” [MJ98, JMR99]. Naquele contexto, al´em de aumentar a complexidade do compilador, esse tipo de atualiza¸c˜ao poderia levar a m´ultiplas c´opias de um mesmo objeto. Dessa forma, indire¸c˜oes s˜ao sempre utilizadas, evitando o problema das m´ultiplas c´opias. Por outro lado, vale ressaltar que o GHC tem maior controle sobre o layout e tamanho das closures atualizadas, sendo capaz de manipul´a-los para liberar mem´oria, ao contr´ario do que ocorre com o Haskell.NET na CLR (pelo menos a partir de c´odigo verific´avel).

Finalmente, propomos uma t´ecnica que, apesar de n˜ao resolver, visa a atenuar o problema da coleta de closures j´a atualizadas, coletando esse tipo de objeto quando poss´ıvel. A t´ecnica aqui proposta consistiu nos seguintes passos:

i) foi adicionado um m´etodo denominado GetValue `a classe Closure. Para closures atualiz´aveis, o que esse m´etodo faz ´e verificar se a closure j´a foi atualizada: caso tenha sido, seu valor ´e retornado e, caso contr´ario, a pr´opria closure ´e retornada (a avalia¸c˜ao propriamente dita n˜ao´e for¸cada);

ii) sempre que um campo “boxed ” de um objeto do tipo Pack (argumento) ou closure (vari´avel livre) ´e acessado, seu m´etodo GetValue ´e invocado e o valor retornado ´e atribu´ıdo ao campo em quest˜ao;

iii) para a constru¸c˜ao de objetos, ´e invocado o m´etodo GetValue para os valores que servir˜ao de campos para o objeto sendo constru´ıdo.

Aplicando o que est´a descrito acima, evita-se construir novos objetos que referenciem closures j´a atualizadas e, em algumas situa¸c˜oes, atualiza-se as referˆencias para closures atualizadas contidas em objetos previamente criados.

Os resultados desta otimiza¸c˜ao podem ser vistos na Tabela 5.10.

Analisando-se os algoritmos de alguns programas que obtiveram pouco ganho ou perda de desempenho, observou-se que esses n˜ao poderiam tirar proveito da atualiza¸c˜ao in-place simulada, uma vez que a atualiza¸c˜ao de closures referenciadas por outros objetos ocorreria

5.6 otimizac¸ ˜oes 77

Programa Antes da otimiza¸c˜ao Ap´os a otimiza¸c˜ao Diferen¸ca

Digits e1 (1500) 16,36 16,09 1,66% Digits e2 (2000) 12,85 13,14 -2,27% Exp3 40,11 42,89 -6,93% Primes (3500) 12,89 12,61 2,21% Queens 23,15 28,44 -22,85% Tak 22,35 22,36 -0,06% Wheel-sieve1 (200000) 8,45 6,49 23,28% Wheel-sieve2 (40000) 7,29 7,41 -1,69%

Tabela 5.10. Simula¸c˜ao de Atualiza¸c˜ao “In-place”

ap´os a cria¸c˜ao dos ´ultimos, e a atualiza¸c˜ao das referˆencias segundo o passo ii) n˜ao poderia ser feita durante a execu¸c˜ao do c´odigo dos programas, e sim por um processo externo aos mesmos, possivelmente acoplado ao garbage collector. J´a para o programaWheel-sieve1, que apresentou o melhor ganho, foi observado, por meio de profiling de mem´oria, que o tempo de vida das closures atualiz´aveis diminuiu, de forma que, ao fim da execu¸c˜ao, esse tipo de closure ocupa o segundo lugar em espa¸co na heap (33,6%), contra o primeiro lugar (39,44%) sem o emprego da otimiza¸c˜ao. Em termos de quantidade de mem´oria isso representou uma diferen¸ca de cerca de 1,2 MB para a entrada analisada.

Foi notado, portanto, que, quando a otimiza¸c˜ao em quest˜ao n˜ao atinge seus objetivos, as chamadas ao m´etodoGetValue – um m´etodo virtual – podem degradar o desempenho

do programa. Pelo fato da simula¸c˜ao de atualiza¸c˜ao in-place n˜ao ter alcan¸cado sua finalidade para a maioria dos programas, devido `a dinˆamica de execu¸c˜ao dos mesmos, ela encontra-se desativada na vers˜ao padr˜ao do gerador de c´odigo.

No documento Integrando Haskell à Plataforma .NET (páginas 88-90)

Documentos relacionados