JavaTips 3.0 – Performance Generics + AutoBox/UnBox
Março 19, 2008
AutoBox/UnBox é a conversão de forma automática entre objetos e tipos primitivos, dicas:
- Use somente quando houver “descasamento” entre tipos primitivos e objetos empacotadores
- Não abuse: um Integer não substitui um int (a performance é pior)
- Cuidado, unboxing com objetos null lança exceção
Probolema:
- Generics não trabalha com tipos primitivos
Após executar o seguinte teste de performance, constatou-se uma diferênça em média de 25% com uso do autobox. O algoritimo de teste baseou-se em comparar as duas execuções:
- faça 1000 vezes
- varra um ArrayList<Integer>, capturando o valor sem fazer autobox
- faça 1000 vezes
- varra um ArrayList<Integer> usando enhanced-loop fazendo automaticamente unbox para int, capturando o valor e fazendo autbox novamente para um objeto Integer.
Resultado da execução:
___________________________________
autoBoxUnBox()…java.lang.NullPointerException
Sem autoboxing = 6531ms
Com autobox/unbox= 8734ms
Diferença de 26%
___________________________________
Conclusão:
Se sua aplicação é “Performance Driven”, estruturas como o IntHashMap visto no blog anterior entre outras coleções da API que não trabalhem com Generics podem ser a melhor saída para escovar milisegundos de processamento.
Abaixo segue a classe usada no teste:
Java Tips 2.0: Ganhe até 70% performance com IntHashMap
Março 17, 2008
O java.util.HashMap como o próprio nome diz utiliza-se do hash code do objeto “chave” para mapear as entradas do mapa. Funciona basicamente assim:
HashMap map = new HashMap();
map.put("matricula.12345", "Fabricio");
A essência do funcionamento do método put() é :
public void put(Object key, Object value){
int hash = geraHashCode(key);
int posicao = geraPosicaoNaTabela(hash);
adicionaNaTabelaDeEntradas(posicao, value);
}
Quando porém estamos trabalhando com um mapa onde as chaves já são inteiros muitas operações do mapa podem ser simplificadas, solução: IntHashMap.
Algumas coporações como a Oracle, Apache, etc… possuem em suas “foundation classes” alguma implementações que trababalhem desta forma para obterem melhor performance. Afim de comparar o desempenho com HashMap executei dois teste simples
- Povoando o mapa com 1000000 chaves sequenciais (Puts)
- Recuperando todos os objetos do mapa (Gets), utilizou-se string como objeto para evitar autobox/unbox.
O resultado foram ganhos em média de 70% para os puts, e 30% para os gets efetuados no mapa. Veja o resultado de uma excução:
Main.testSunMap()…
Main.testIntMap()…
‘Puts’ execution time (milliseconds):
sunMap: 3328 intMap: 922, Percentage of gain: 73%
‘Gets’ execution time (milliseconds):
sunMap: 234 intMap: 140, Percentage of gain: 41%
Conclusão
Se suas chaves são inteiros use IntHashMap. Os resultados foram obtidos usando java 5, o mesmo exemplo foi executado com 1.4 e obteve em média os mesmos valores, no entanto se em sua aplicação você não tomar os devidos cuidados no uso do autoboxing/unboxing de valores primitivos para objetos, a diferença de ganho vai ser ainda em muito melhor para o IntHashMap devido a perda de performance executando o boxing.
Você já se perguntou como algumas IDE’s configuram o classpath da sua aplicação dinamicamente em tempo de execução?
Aqui vai uma solução técnica alternativa (that means GDD – Gambiarra Driven Development
).
A classe java.net.URLClassLoader é o classloader default do sistema para carregar o jars do classpath quando o aplicativo java é iniciado.
Uma maneira de adicionar jars extras seria invocando no classloder o método addURL(URL url), o que faz o jar localizado por está URL ser carregado na JVM em execuçao. O problema é que este método é protegido (protected) e portanto, a menos que você extenda o URLClassLoader, sobreescreva o método mudando a visibilidade para público e configure seu classloder como default do sistema você não poderá invocá-lo na instância do classloader.
Mas como nem tudo é impossível na computação, você pode contornar o problema usando reflexão e acessando o método protegido. Para isso basta recuperar o método “addURL” declarado e invocar setAccessible(true).
A classe abaixo mostra como você mata os dois coelhos com uma única cajadada:
public class ClassPathHacker {
private static final Class[] parameters = new Class[]{URL.class};
public static void addFile(String s) throws IOException {
File f = new File(s);
addFile(f);
}//end method
public static void addFile(File f) throws IOException {
addURL(f.toURL());
}//end method
public static void addURL(URL u) throws IOException {
URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL",parameters);
method.setAccessible(true);
method.invoke(sysloader,new Object[]{ u });
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}//end try catch
}//end method
}//end class
Baseado no Fórum:
http://forum.java.sun.com/thread.jsp?forum=32&thread=300557&tstart=0&trange=15