Benchmark aplicado: Clone Benchmark
Seguindo a linha do post anterior vou mostrar um exemplo de como poderíamos fazer um benchmark. Vamos partir de um problema, depois de algumas premissas, teremos um cenário(contexto) e a duvida(?).
Problema: Preciso saber se a operação de clonar beans atual utilizada pela aplicação está sendo eficiente em questão de performance.
Premisas: A aplicação já está com 70% desenvolvida e testada, o tempo de alteração é escasso.
Contexto: Existem 2.000 beans(pojos) com a implementação default. Esses beans não implementação a interface Clonnable e por seqüencia não tem um método clone.
Dúvida: Estou clonando beans através do framework beans utils e tenho duvidas se o método clone da classe Object não seria mais rápido?
Eis os códigos do benchmark
package org.diegopacheco.clonebenchmark.model;
import java.util.Date;
public class Pessoa {
private String id;
private String nome;
private String telefone;
private boolean casada;
private Date nasc;
public Pessoa() {}
public Pessoa(String id, String nome, String telefone, boolean casada, Date nasc) {
super();
this.id = id;
this.nome = nome;
this.telefone = telefone;
this.casada = casada;
this.nasc = nasc;
}
public boolean isCasada() {
return casada;
}
public void setCasada(boolean casada) {
this.casada = casada;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getNasc() {
return nasc;
}
public void setNasc(Date nasc) {
this.nasc = nasc;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
}
package org.diegopacheco.clonebenchmark.model.clonable;
import java.util.Date;
public class PessoaClonable implements Cloneable {
private String id;
private String nome;
private String telefone;
private boolean casada;
private Date nasc;
public PessoaClonable() {}
public PessoaClonable(String id, String nome, String telefone, boolean casada, Date nasc) {
super();
this.id = id;
this.nome = nome;
this.telefone = telefone;
this.casada = casada;
this.nasc = nasc;
}
public boolean isCasada() {
return casada;
}
public void setCasada(boolean casada) {
this.casada = casada;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getNasc() {
return nasc;
}
public void setNasc(Date nasc) {
this.nasc = nasc;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package org.diegopacheco.clonebenchmark.test;
public interface Closure {
Object run();
String getDescription();
}
package org.diegopacheco.clonebenchmark.test;
import java.util.Date;
import org.apache.commons.beanutils.BeanUtils;
import org.diegopacheco.clonebenchmark.model.Pessoa;
import org.diegopacheco.clonebenchmark.model.clonable.PessoaClonable;
public class MainClass {
private static void benchmark(Closure c,Integer times){
long init = System.currentTimeMillis();
Object ret = null;
for(int i=0;i<=times;i++){
ret = c.run();
}
if (ret!=null)
System.out.println("Tempo de execução: " +
"[ " + c.getDescription() + "] " +
(System.currentTimeMillis() - init));
}
@SuppressWarnings("deprecation")
public static void main(String[] args) {
final Pessoa p = new Pessoa("1","Diego","666",false,new Date(10,10,210));
final PessoaClonable pc = new PessoaClonable("-1","Ogedi","999",false,new Date(2,2,2020));
final int times = 100000;
benchmark(
new Closure(){
public String getDescription() {
return "Sem Clonnable";
}
public Object run() {
try {
Object ret = BeanUtils.cloneBean(p);
return ret;
} catch (Exception e) {
return null;
}
}
}
,times);
benchmark(
new Closure(){
public String getDescription() {
return "Com Clonnable";
}
public Object run() {
try {
Object ret = pc.clone();
return ret;
} catch (Exception e) {
return null;
}
}
}
,times);
// RESULTADOS
// 10 000 000
// Tempo de execução: [ Sem Clonnable] 163608
// Tempo de execução: [ Com Clonnable] 4501
// 1 000 000
// Tempo de execução: [ Sem Clonnable] 16567
// Tempo de execução: [ Com Clonnable] 453
// 100 000
// Tempo de execução: [ Sem Clonnable] 1844
// Tempo de execução: [ Com Clonnable] 47
// 10 000
// Tempo de execução: [ Sem Clonnable] 359
// Tempo de execução: [ Com Clonnable] 0
// 1 000
// Tempo de execução: [ Sem Clonnable] 204
// Tempo de execução: [ Com Clonnable] 0
// 100
// Tempo de execução: [ Sem Clonnable] 156
// Tempo de execução: [ Com Clonnable] 0
// 10
// Tempo de execução: [ Sem Clonnable] 110
// Tempo de execução: [ Com Clonnable] 0
}
}
A classe Pessoa: Representa um desses 2.000 beans que não implementam clonnable.
A classe PessoaClonable: Representa um bean, no caso se ele fosse implementador de clonnable.
A interface Closure: Representa um closure rustico usado apenas para facilitar o desenvolvimento.
A classe MainClass: Fáz o benchmark de fato.
Como vocês podem ter percebido nas linhas comentadas(verde) os tempos variam conforme os registros.
Solução: Supondo que no máximo vou ter entre 1000 e 10000 objetos de uma vez só sendo executados em seqüencia, o esforço de alterar 2.000 pojos não se justifica pois o ganho de 400ms em media não justifica tal esforço de código. Agora se tivéssemos na media de: 10 000 000 objetos sendo clonados um após o outro na seqüencia o ganho de tempo como o algoritmo utilizando clonnable é absurdo, então mais do que justifica alterar os 2.000 pojos.
Sempre temos de nos fazer essas perguntas para tomarmos a melhor decisão. :)
Problema: Preciso saber se a operação de clonar beans atual utilizada pela aplicação está sendo eficiente em questão de performance.
Premisas: A aplicação já está com 70% desenvolvida e testada, o tempo de alteração é escasso.
Contexto: Existem 2.000 beans(pojos) com a implementação default. Esses beans não implementação a interface Clonnable e por seqüencia não tem um método clone.
Dúvida: Estou clonando beans através do framework beans utils e tenho duvidas se o método clone da classe Object não seria mais rápido?
Eis os códigos do benchmark
package org.diegopacheco.clonebenchmark.model;
import java.util.Date;
public class Pessoa {
private String id;
private String nome;
private String telefone;
private boolean casada;
private Date nasc;
public Pessoa() {}
public Pessoa(String id, String nome, String telefone, boolean casada, Date nasc) {
super();
this.id = id;
this.nome = nome;
this.telefone = telefone;
this.casada = casada;
this.nasc = nasc;
}
public boolean isCasada() {
return casada;
}
public void setCasada(boolean casada) {
this.casada = casada;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getNasc() {
return nasc;
}
public void setNasc(Date nasc) {
this.nasc = nasc;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
}
package org.diegopacheco.clonebenchmark.model.clonable;
import java.util.Date;
public class PessoaClonable implements Cloneable {
private String id;
private String nome;
private String telefone;
private boolean casada;
private Date nasc;
public PessoaClonable() {}
public PessoaClonable(String id, String nome, String telefone, boolean casada, Date nasc) {
super();
this.id = id;
this.nome = nome;
this.telefone = telefone;
this.casada = casada;
this.nasc = nasc;
}
public boolean isCasada() {
return casada;
}
public void setCasada(boolean casada) {
this.casada = casada;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getNasc() {
return nasc;
}
public void setNasc(Date nasc) {
this.nasc = nasc;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package org.diegopacheco.clonebenchmark.test;
public interface Closure {
Object run();
String getDescription();
}
package org.diegopacheco.clonebenchmark.test;
import java.util.Date;
import org.apache.commons.beanutils.BeanUtils;
import org.diegopacheco.clonebenchmark.model.Pessoa;
import org.diegopacheco.clonebenchmark.model.clonable.PessoaClonable;
public class MainClass {
private static void benchmark(Closure c,Integer times){
long init = System.currentTimeMillis();
Object ret = null;
for(int i=0;i<=times;i++){
ret = c.run();
}
if (ret!=null)
System.out.println("Tempo de execução: " +
"[ " + c.getDescription() + "] " +
(System.currentTimeMillis() - init));
}
@SuppressWarnings("deprecation")
public static void main(String[] args) {
final Pessoa p = new Pessoa("1","Diego","666",false,new Date(10,10,210));
final PessoaClonable pc = new PessoaClonable("-1","Ogedi","999",false,new Date(2,2,2020));
final int times = 100000;
benchmark(
new Closure(){
public String getDescription() {
return "Sem Clonnable";
}
public Object run() {
try {
Object ret = BeanUtils.cloneBean(p);
return ret;
} catch (Exception e) {
return null;
}
}
}
,times);
benchmark(
new Closure(){
public String getDescription() {
return "Com Clonnable";
}
public Object run() {
try {
Object ret = pc.clone();
return ret;
} catch (Exception e) {
return null;
}
}
}
,times);
// RESULTADOS
// 10 000 000
// Tempo de execução: [ Sem Clonnable] 163608
// Tempo de execução: [ Com Clonnable] 4501
// 1 000 000
// Tempo de execução: [ Sem Clonnable] 16567
// Tempo de execução: [ Com Clonnable] 453
// 100 000
// Tempo de execução: [ Sem Clonnable] 1844
// Tempo de execução: [ Com Clonnable] 47
// 10 000
// Tempo de execução: [ Sem Clonnable] 359
// Tempo de execução: [ Com Clonnable] 0
// 1 000
// Tempo de execução: [ Sem Clonnable] 204
// Tempo de execução: [ Com Clonnable] 0
// 100
// Tempo de execução: [ Sem Clonnable] 156
// Tempo de execução: [ Com Clonnable] 0
// 10
// Tempo de execução: [ Sem Clonnable] 110
// Tempo de execução: [ Com Clonnable] 0
}
}
A classe Pessoa: Representa um desses 2.000 beans que não implementam clonnable.
A classe PessoaClonable: Representa um bean, no caso se ele fosse implementador de clonnable.
A interface Closure: Representa um closure rustico usado apenas para facilitar o desenvolvimento.
A classe MainClass: Fáz o benchmark de fato.
Como vocês podem ter percebido nas linhas comentadas(verde) os tempos variam conforme os registros.
Solução: Supondo que no máximo vou ter entre 1000 e 10000 objetos de uma vez só sendo executados em seqüencia, o esforço de alterar 2.000 pojos não se justifica pois o ganho de 400ms em media não justifica tal esforço de código. Agora se tivéssemos na media de: 10 000 000 objetos sendo clonados um após o outro na seqüencia o ganho de tempo como o algoritmo utilizando clonnable é absurdo, então mais do que justifica alterar os 2.000 pojos.
Sempre temos de nos fazer essas perguntas para tomarmos a melhor decisão. :)