8. PROCEDURES TO FOLLOW WHEN ESTIMATED DOSES
8.1. An iterative approach to evaluation
Benchmarks(EPB) criados por Pinto et al. (2016). Os EPBs são benchmarks que servem para aferir o consumo de energia e desempenho de 16 implementações de coleções seguras a threads. Para cada coleção é analisado o perfil de consumo de energia das operações de inserção, remoção e leitura simulando um ambiente concorrente e com intenso uso dessas coleções. Para medir o consumo de energia, foi utilizada a biblioteca jRAPL (LIU; PINTO; LIU, 2015). Como resultado dessas medições, tem-se o perfil de consumo de energia de cada coleção. Esses perfis serão utilizados posteriormente para realizar a recomendação de qual melhor implementação de coleção utilizar. Além disso, esses perfis de consumo de energia são específicos para a máquina em que a aplicação vai rodar. Dessa forma, caso as aplicações rodem em hardware com configurações diferentes, é necessário novamente medir esses perfis de consumo de energia. Os perfis dependem do ambiente devido às diferenças entre plataformas de hardware e variações entre as versões da Máquina Virtual Java (JVM). Já foi demonstrado (PINTO et al., 2016), por exemplo, que o consumo de energia e o tempo de execução da classe ConcurrentHashMap mudaram significativamente entre as versões 7 e 8 da linguagem Java.
3.3 Análise Estática das Aplicações
Essa seção explica a etapa da análise estática, a qual extrai informações das aplicações, como a frequência de uso das operações e o contexto no qual essas operações são chamadas.
Para ilustrar as próximas etapas, a Figura 3.2 apresenta um exemplo de código, retirado da biblioteca Xalan (Xalan - The Apache XML Project, 2016), que realiza inserções e leituras na variável imports do tipo Vector. Esse exemplo será referenciado ao longo do texto como “Exemplo Base”. O código foi simplificado para facilitar o entendimento.
Nesse exemplo, a variável imports (variável coleção) da classe Stylesheet é uma coleção de objetos na qual são realizas inserções e leituras (linhas 6 e 10 respectivamente). A classe Stylesheet possui os métodos getImports e addImports (linhas 13 e 20), que chamam os métodos setImport e getImport para realizar as inserções e leituras na variável imports. Estes dois métodos são chamados dentro de loops. Além disso, a variável imports também pode ser lida a partir do método recomposeImports() (linha 34), que por sua vez é chamado pelo método recompose(). Neste caso a operação de leitura, elementAt, está sendo realizada dentro de dois loops aninhados (linha 28 e linha 35). Logo, para a variável importstem-se (1) um método para inserção sendo chamado dentro de apenas um loop e (2) duas ocorrências de leitura, uma dentro de um loop e outra dentro de dois loops aninhados. Apesar de estarem em métodos diferentes, o aninhamento é levado em consideração por esta abordagem.
3.3. ANÁLISE ESTÁTICA DAS APLICAÇÕES 30
1 public class Stylesheet extends ElemTemplateElement{
2 ...
3 private Vector imports;
4
5 public void setImport(StylesheetComposed v){
6 imports.addElement(v);
7 }
8
9 public StylesheetComposed getImport(int i){
10 return (StylesheetComposed) imports.elementAt(i);
11 }
12
13 protected void getImports(Stylesheet stylesheet){
14 for (int i = 0; i < n; i++)
15 {
16 Stylesheet imported = stylesheet.getImport(i);
17 }
18 }
19
20 public void addImports(Stylesheet stylesheet, ...){
21 for (int i = 0; i < n; i++)
22 {
23 stylesheet.setImport(style);
24 }
25 }
26
27 public void recompose() throws TransformerException{
28 for (int i = 0, j= importList.size() -1; i < importList.size();
i++) 29 { 30 globalImportList[j--].recomposeImports(); 31 } 32 } 33 34 void recomposeImports(){ 35 while (count > 0)
36 m_endImportCountComposed += this.getImport(--count);
37 }
38 ...
39 }
3.3. ANÁLISE ESTÁTICA DAS APLICAÇÕES 31 3.3.1 Estimativa da Frequência de Uso
A estimativa da frequência de uso das operações de inclusão, remoção e consulta é realizada através de análise estática do bytecode das aplicações. Essa análise estática é inter- procedural.
A análise inter-procedural utiliza call graphs e control flow graphs para medir a quanti- dade de ocorrências das operações das coleções, além do obter o nível de aninhamento de loops em que essas operações são chamadas. Para cada método é verificado se existem chamadas a operações das coleções estudadas nesse trabalho (e disponíveis na Tabela 3.1). A quantidade de chamadas a cada uma é armazenada em um contador. Ao final de todo o fluxo de chamadas do call graph, obtém-se o número de vezes que estas operações são chamadas. Como podem existir vários caminhos a serem percorridos para uma determinada chamada, a contagem de ocorrências está relacionada a um determinado contexto de execução. Esse contexto é definido pelas seguintes variáveis:
Tipo da coleção;
Nome da variável que armazena a coleção na qual é realiza a operação;
Número de ocorrências da operação;
Nome da operação;
Classe na qual a operação é realizada;
Método no qual a operação é realizada;
Linha de código da ocorrência da operação;
Informações sobre os aninhamentos dos loops.
A Tabela 3.2 apresenta o contexto para a operação elementAt da variável imports do Exemplo Base, quando essa tem sua ocorrência dentro apenas de um loop. Enquanto a Tabela 3.3 apresenta o contexto para a mesma operação, quando chamada dentro de dois loops aninhados.
Portanto, chamadas à mesma operação podem ser contadas de formas diferentes de acordo com os contextos nos quais se encontram. Como percebe-se no Exemplo Base, a operação elementAt possui dois contextos: um no qual foi chamada dentro de um loop e outra no qual foi chamada dentro de dois loops aninhados.
3.3.2 Análise de Loops
A simples contagem das ocorrências das chamadas das operações não é suficiente para realizar uma boa recomendação, uma vez que essa contagem não representa o real uso das
3.3. ANÁLISE ESTÁTICA DAS APLICAÇÕES 32 Tabela 3.2: Exemplo de contexto aplicado à operação elementAt
Nome da variável imports
Nome da operação elementAt
Número de ocorrências 1
Tipo da estrutura de dados Vector
Classe na qual a operação foi realizada Stylesheet Método no qual a operação foi realizada getImport
Linha de código 10
Nível de Aninhamento do loop 1
Tabela 3.3: Exemplo de contexto aplicado à operação elementAt, dentro de dois loops aninhados
Nome da variável imports
Nome da operação elementAt
Número de ocorrências 1
Tipo da estrutura de dados Vector
Classe na qual a operação foi realizada Stylesheet Método no qual a operação foi realizada getImport
Linha de código 10
Nível de Aninhamento do loop 2
operações devido à quantidade de vezes que as operações podem ser executadas dentro de um loop. Desta forma, quando uma operação é chamada dentro de um loop, uma contagem mais realista seria multiplicar a quantidade de ocorrências pela quantidade de iterações do loop. Porém, na maioria das vezes a quantidade de iterações dos loops não pode ser determinada de forma precisa estaticamente, podendo, inclusive, variar ao longo da execução do programa.
A predição do número exato de iterações do loop é impossível, dado que é equivalente a resolver o problema da parada (RICE, 1953). Por exemplo, o loop foreach, muito comum em aplicações Java (DYER et al., 2013), itera sobre os elementos das estruturas de dados que implementam a interface Iterable. Desta forma ele percorre a estrutura de dados até o seu fim ou encontrar uma condição de saída. Isto torna difícil predizer quantas vezes esse loop executará, uma vez que o tamanho da estrutura de dados não está explicitamente disponível. A Figura 3.3 apresenta um exemplo real retirado da aplicação Tomcat do uso do foreach, no qual ele itera sobre o conjunto de chaves de um mapa. Vários estudos, por exemplo, (RODRIGUES, 2014), (HEALY et al., 1998), (WU; LARUS, 1994) e (BALL; LARUS, 1993), tratam sobre a predição de loops, no entanto, estes trabalhos estão longe de cobrir todos os possíveis usos dos loops de uma linguagem de alto nível, como Java.
Como abordagens existentes para estimar a número de iterações de um loop normalmente exigem a execução do programa (RODRIGUES, 2014), este trabalho não considera estas aborda- gens, uma vez que o objetivo aqui é analisar o consumo de energia de forma estática. Parte destes trabalhos levam em conta o nível de aninhamento dos loops como uma forma de dar peso às operações que são realizadas dentro de loops. Embora esta abordagem não estipule quantas vezes
3.4. RECOMENDAÇÃO 33