Estimação Estática de Métricas para Distribuir Aplicações Java

Propaganda
Estimação Estática de Métricas para Distribuir Aplicações Java
Filipe Matos, António J. Esteves e João L. Sobral
Departamento de Informática, Universidade do Minho, Braga, Portugal
[email protected], [email protected], [email protected]
Resumo
1). Quando aparece uma instrução new no código
Este trabalho apresenta um conjunto de ferramentas
desenvolvidas para auxiliar a distribuição das classes
de uma aplicação Java pelos recursos de uma arquitectura heterogénea, baseada em microprocessadores genéricos e dispositivos de lógica recongurável. Para poder
tomar decisões relacionadas com essa distribuição, foi
preciso identicar as diferentes formas de uma classe
se relacionar com outras (fuga de referências) e obter
uma estimativa estática para a complexidade dos métodos de cada classe. Os resultados obtidos com o caso de
estudo RayTracer mostraram que a métrica de complexidade, estimada estaticamente, segue o mesmo padrão
que o tempo de execução medido dinamicamente.
Uma classe também se relaciona com outras quando
inclui explicitamente referências para essas classes nas
suas variáveis de classe (tipo 2) ou quando acede a variáveis de classe de outra instância (tipo 3). O facto de
uma classe aceder a variáveis de classe de uma instância da classe diferente desta, pode ser identicado, ao
nível dos bytecodes, através das instruções putfield,
getfield, putstatic e getstatic.
1 Introdução
O presente trabalho, integrado no projecto PPCVM [1] 1 , tem por objectivo auxiliar a distribuição de
aplicações Java de elevada dimensão por uma arquitectura distribuída e heterogénea. A arquitectura será
composta por diversas máquinas baseadas em microprocessadores genéricos e por uma ou mais plataformas
reconguráveis baseadas em FPGAs Virtex-4.
Como parte integrante do processo de análise de aplicações JAVA, a análise estática permitirá estimar métricas associadas à complexidade dos métodos e à relação entre classes. Tratando-se de uma análise estática
sobre a linguagem de programação JAVA, o objecto de
análise pode ser o código fonte ou o código compilado
(bytecodes), tendo-se optado neste caso pela análise
dos bytecodes. Ao optar-se por uma análise ao nível
dos bytecodes, restringiu-se as alternativas de análise
ao conjunto de instruções da JVM, ou seja, a aproximadamente duas centenas de possibilidades. Contudo,
esta escolha implicou uma maior diculdade na identicação de blocos código especícos.
2 Fuga de referências
Um estudo prévio sobre diversos prolers open-source
de Java mostrou que eles não resolvem a questão da
fuga de referências. Deste modo, foi desenvolvido um
algoritmo para identicar as sete maneiras de uma
classe se poder relacionar com outra.
A maneira mais explícita de se obter uma referência
para uma instância de uma classe, é criando-a (tipo
1 Trabalho parcialmente suportado pelo projecto PPC-VM
(POSI / CHS / 47158 / 2002) nanciado pela FCT e por fundos
europeus (FEDER).
da aplicação, esta associa-se obrigatoriamente a uma
classe, para que a maquina virtual consiga obter uma
referência para uma nova instância desta.
Como a análise é efectuada por método, é possível,
a partir da sua assinatura, saber quais as referências
que este recebe como parâmetro (tipo 4) ou que passa
como retorno (tipo 5). Para tal, foi desenvolvido um
parser que identica tanto o retorno associado a um
método, como os possíveis argumentos deste. Deste
modo, para obter todas as dependências, é percorrida
toda a lista de argumentos, identicando aqueles que
se referem a classes diferentes da classe analisada.
Por m, quando um método faz uma invocação a
um método externo à sua classe, este pode passar referências como argumentos (tipo 6) ou receber uma referência como retorno do método invocado (tipo 7). No decorrer da análise dos bytecodes são interceptadas todas as instruções que efectuem qualquer tipo de invocação (invokeinterface,
invokespecial, invokevirtual, invokestatic).
3 Métrica de complexidade
A questão da dependência entre classes foi solucionada desenvolvendo uma aplicação baseada em prolers analisados [2] [3] [4]. Para a análise car completa, é preciso determinar a complexidade dos métodos. Como a maioria dos métodos segue uma linha
de execução com saltos, desenvolveu-se uma aplicação
que identica (i) os saltos no código e (ii) se estes dependem directa ou indirectamente dos argumentos do
método, ao qual o código se refere [5].
Para identicar os blocos de código referentes a condições ou a saltos, não foi necessário interceptar todas
as instruções de um método. Aos blocos de código
pretendidos, relativos a ciclos ou a construtores condicionais, está sempre associada uma ou mais condições
que permitirão que uma nova iteração seja executada
ou que as instruções referentes a um bloco de código
sejam executadas. Este tipo de instrução é codicada
juntamente com um valor com sinal (oset) que re-
presenta o número de bytes a incrementar ao endereço
actual, por forma a encontrar a instrução à qual o salto
se refere. É a partir deste salto, considerando determinados padrões de codicação, que é possível concluir
acerca do tipo de código fonte que está associado ao
bloco de código analisado. Os padrões de codicação
dos ciclos/construtores condicionais são normalmente
denidos pelo sinal do oset e/ou pela instrução anterior à instrução destino do salto. Como resultado
desta análise, obtém-se uma estrutura de dados hierárquica contendo todos os blocos de código acima referidos organizados hierarquicamente, juntamente com
o número de instruções exclusivas de cada um.
Após identicar o tipo de bloco de código e a condição de salto associada, foi preciso concluir acerca da
sua possível dependência em relação aos argumentos
do método em questão. Para concretizar esta tarefa,
foi necessário emular a pilha da JVM para se poder
seguir a propagação das várias dependências de cada
um dos argumentos, durante a execução do método.
Esta fase obriga a tratar de forma distinta a pilha de
dados e a pilha dos operandos. Qualquer instrução
consome zero ou mais elementos da pilha de dados e
pode produzir elementos nela. Deste modo chega-se
a uma expressão de dependência em que cada um dos
elementos produzidos depende da soma de todas as dependências dos elementos consumidos. As instruções
em que é necessário considerar os operandos são as
operações de load/store, incluindo aquelas que acedem a variáveis/campos de classe. No nal, a informação resultante consiste num relatório que exibe todos
os blocos de códigos importantes na decisão do controlo de uxo do método, bem como a dependência
das suas condições de salto para com os argumentos
do método.
Pretende-se que a estimativa estática da complexidade
apresente o mesmo padrão de comportamento que o
tempo de execução medido dinamicamente. Deste
modo, é esperado que quanto maior for a estimativa
da complexidade para um determinado método, maior
será o seu tempo de execução. Por forma a denir
uma métrica ável, foi desenvolvido um algoritmo (i)
que contempla a recursividade no código e os saltos
na linha de execução, causados por condições de salto
ou ciclos e (ii) que penaliza a recursividade e os blocos mencionados. O valor da penalização é multiplicado pelo valor da complexidade previamente calculado para o bloco de código ao qual a penalização se
refere. Basicamente, o algoritmo considera que todas
as instruções demoram o mesmo tempo de processamento, excepto no caso de uma invocação, em que é
calculado o valor da complexidade da transitividade
associada ou no caso de haver uma penalização.
4 Resultados
Como exemplo, apresenta-se a classe RayTracer do
benchmark RayTracer versão MPJ [6], ao qual foi aplicada a metodologia de cálculo da métrica de complexidade. O valor calculado estaticamente para a complexidade dos métodos da classe RayTracer é apresentado na tabela 1. Para validar a estimativa da complexidade, apresenta-se na mesma tabela o tempo de
execução destes métodos, medido por uma ferramenta
de instrumentação dinâmica desenvolvida no âmbito
do projecto PPC-VM. Com a excepção de um caso,
os métodos com uma complexidade maior apresentam
um tempo de execução superior aos restantes.
Método
render
trace
shade
createScene
TransDir
SpecularDirection
Shadow
setScene
intersect
setHeight
setWidth
Complexidade
7382
4563
4329
1430
162
127
97
95
87
4
4
Texec (ms)
9397.0
0.121966
3.0
19.0
0.001608
0.001383
0.006936
0.0
0.005184
0.0
2.0
Tabela 1: Complexidade e tempo de execução dos métodos da classe RayTracer.
5 Conclusões
Ao optar pela análise de bytecodes, em detrimento de
código fonte Java, o presente trabalho mostrou que a
análise estática das aplicações ca facilitada. Outro
contributo do presente trabalho foi o de solucionar a
questão da fuga de referências, que não era tratada
de forma adequada nos prolers open-source de Java.
Os resultados obtidos com o exemplo RayTracer mostraram que a métrica de complexidade desenvolvida,
calculada estaticamente, segue o mesmo padrão que o
tempo de execução medido dinamicamente. Concluiuse assim que, embora a estimativa da complexidade
dos métodos não seja precisa em termos de valor absoluto, como possui uma boa delidade em relação ao
tempo de execução, permite tomar decisões correctas
aquando da distribuição da aplicação Java.
Referências
[1] João L. Sobral, António J. Esteves, João M. Fernandes, and Luís P. Santos. Projecto PPC-VM: Parallel
Computing Based on Virtual Machines. Dep. Informática, Universidade do Minho, March 2004 - March 2007.
http://gec.di.uminho.pt/ppc-vm.
[2] JeanTessier.
The
Dependency
Finder
User
Manual
(Version
1.1.1).
Google
Inc,
Mountain
View,
California,
2005.
http://depfind.sourceforge.net/Manual.html.
Jrefactory project,
2004.
[3] Mike Atkinson.
http://jrefactory.sourceforge.net.
[4] Diomidis Spinellis.
Chidamber and Kemerer Java Metrics (ckjm).
Department of Management
Science
and
Technology,
Athens
University of Economics and Business, 2005.
http://www.spinellis.gr/sw/ckjm/doc/indexw.html.
[5] André Spiegel. Automatic Distribution of ObjectOriented Programs. PhD thesis, FB Mathematik und
Informatik, Frei Universitat Berlin, July 2002.
Edinburgh
[6] Java Grande Benchmarking Project.
Parallel Computing Centre, University of Edinburgh, 2005. http://www.epcc.ed.ac.uk/computing/
research_activities/java_grande/.
Download