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. :)

Popular posts from this blog

Kafka Streams with Java 15

Rust and Java Interoperability

HMAC in Java