29 de abr. de 2010

Primitivos vs Wrappers, profiling ...


Intro

A linguagem Java, na verdade, não é totalmente orientada a objetos, como pode ser visto pela existência de tipos primitivos. São eles:

boolean, char, byte, short, int, long, float, double


Sempre que um variável primitiva é declarada, é inserida uma posição na pilha com o valor atribuído, ex:

int numero = 5;


E existem as classes que representam estes tipos primitivos, os Wrappers. Existe um Wrapper para cada tipo primitivo, são eles:

Boolean, Character, Byte, Short, Integer, Long, Float, Double


Até o Java 1.4, eu deveria declarar assim:

Integer numero = new Integer(5);


Este tipo de declaração abaixo causava um erro:

Integer numero = 5;


Então sempre era necessário, até o Java 1.4, fazer o Boxing ou Unboxing, como abaixo:

int numero = 3;

Integer outroNumero = new Integer(numero); //Boxing

int outroPrimitivo = outroNumero.intValue(); //Unboxing


A partir do Java 1.5 (Java 5 se preferir), foi introduzido o recurso de autoboxing-unboxing, liberando o desenvolvedor deste trabalho. As instruções abaixo são válidas a partir do Java 1.5:

Integer numero = 2; //O boxing é implícito

int numeroPrimitivo = numero; //O unboxing é implícito



Mito

Usar Wrappers ao invés de primitivos impacta na performance

Fazer o boxing (ou unboxing) tem um custo, todos sabem que há um overhead, mas não quanto. A questão é simples, usar Wrappers ou tipos primitivos causa muito impacto na performance?

Bem, resolvi testar usando a classe abaixo:

import java.util.*;

public class BoxingProfile {

private static int loop = 0;

public static void main (String[] args) {
long totalTime = 0;
for (int i = 0; i < 5; i++) { 
totalTime += test(); 
}
System.out.println("\nTempo total: " + totalTime + "ms");
System.out.println("\nTempo médio: " + (totalTime/5) + "ms");
}
private static long test() {
long startTime = System.currentTimeMillis();
List lista = new ArrayList();
FaixaRemuneracao f = null;
for (int i = 0; i < 1000000; i++) {
f = new FaixaRemuneracao();
f.setCodigo(i); //aqui é feito o Boxing
f.setRemuneracao(i * 2.0); //aqui também
lista.add(f);
}
int    codigo      = 0;
double remuneracao = 0.0;
for (FaixaRemuneracao faixa : lista) {
codigo = faixa.getCodigo(); //aqui é feito o Unboxing
remuneracao = faixa.getRemuneracao(); //aqui também
}
long endTime = System.currentTimeMillis();
System.out.println("loop #" + ++loop + ", tempo decorrido: " + (endTime - startTime) + "ms");
return (endTime - startTime);
}

}

// classe modelo
class FaixaRemuneracao {
private Integer codigo;
private Double  remuneracao;
public Integer getCodigo() { return codigo; }
public void setCodigo(Integer novoCodigo) { codigo = novoCodigo; }
public Double getRemuneracao() { return remuneracao; }
public void setRemuneracao(Double novaRemuneracao) { remuneracao = novaRemuneracao; }
}


É um exemplo bem simples, onde o foco está na classe modelo, onde são usados ou Wrappers (Objetos) ou primitivos. Executando como está acima (se alguém quiser testar o código está anexo) deu o seguinte resultado (na minha máquina):

mtorres@independiente:~/temp/sparring$ java BoxingProfile 
loop #1, tempo decorrido: 431ms
loop #2, tempo decorrido: 760ms
loop #3, tempo decorrido: 626ms
loop #4, tempo decorrido: 821ms
loop #5, tempo decorrido: 542ms

Tempo total: 3180ms

Tempo médio: 636ms

Alterando a classe modelo para utilizar tipos primitivos, ou seja, sem necessidade de (un)boxing:

// classe modelo
class FaixaRemuneracao {
private int codigo;
private double  remuneracao;
public int getCodigo() { return codigo; }
public void setCodigo(int novoCodigo) { codigo = novoCodigo; }
public double getRemuneracao() { return remuneracao; }
public void setRemuneracao(double novaRemuneracao) { remuneracao = novaRemuneracao; }
}


Deu o seguinte resultado:

mtorres@independiente:~/temp/sparring$ java BoxingProfile 
loop #1, tempo decorrido: 228ms
loop #2, tempo decorrido: 268ms
loop #3, tempo decorrido: 260ms
loop #4, tempo decorrido: 215ms
loop #5, tempo decorrido: 256ms

Tempo total: 1227ms

Tempo médio: 245ms



Conclusão

Nos testes, usar Wrapper aumentou em torno de 150% o tempo para popular dois campos de um objeto.

Usar tipos primitivos ou Wrapper é um assunto polêmico, e depende do caso, tipo, como quando existe a necessidade de atribuir NULL a uma campo (classe de entidade Objeto-Relacional por exemplo). 


mythbusters_plausible_spray.png Plausible! picture by Xinkz

Não é possível criar uma aplicação usando só primitivos ou só Wrappers, é necessário o melhor dos dois mundos, buscando onde é melhor usar cada estratégia.

Nenhum comentário: