Arquivo da categoria: Avançado

Groovy e o seu selo de NERD!

Você já brincou de [Pedra, Papel e Tesoura]? …

db2e_lizard_spock

Se sua infância foi normal, provavelmente a resposta será sim… No entanto, se você já jogou a sua versão extendida [Pedra, Papel, Tesoura, LAGARTO e SPOCK] não sei se podemos dizer o mesmo!

Para quem não sabe, estamos falando da versão criada por Sam Kass, que ganhou popularidade quando foi apresentado no The Big Bang Theory.

Pois bem, não estamos aqui para falar de seriados e sim de programação, programação com Groovy!
Há um tempo atrás, participei de um dojo de programação onde o problema a ser resolvido foi exatamente esse… Pouco tempo depois, só por curiosidade fui ver como Groovy poderia facilitar ou complicar na resolução deste problema.

Em um primeiro momento decidi programar normalmente (de uma maneira que um ser humano possa ler >> ler código)

def play(play1, play2){
    warning = {i, n -> (i + n) % 5}
    position = plays.indexOf(play1)
    play1 == play2 ? 'draw' : play2 in [plays[warning(position, 1)] , plays[warning(position, 3)]] ? 'play1' : 'play2'
}

Mas não demorou muito tempo para eu tentar incurtar isso, já que o objetivo também era ver maneiras diferentes de resolver o mesmo problema e o resultado foi segundo script:

Como podemos ver abaixo, usando os recursos nerds do Groovy, o mesmo algorítimo está com apenas 88 caracteres! Se é que podemos chamar isso de algorítimo!
p={p1,p2,c={a,b->l[(l.indexOf(a)+b)%5]}->p1==p2?'eq':p2 in [c(p1, 1),c(p1,3)]?'p1':'p2'}
Segue gist completo dos exemplos com seus respectivos testes:

OK OK!! Não é bonito, mas de vez em quando é legal dar uma descontraida!

Se ficou alguma dúvida ou se você sabe como deixar o código menor ainda, é só deixar um comentário.

Anúncios
Etiquetado

Método X Closure: Melhorando o desempenho da aplicação – Parte 3

Salve Groovy Geeks de plantão!

Chegamos ao final dessa jornada sobre métodos e closures groovy.

Pois bem, reta final apenas para essa serie dividida em três episódios, lembrando que nada nos impede de fazer como os Simpson e dar continuidade em outras temporadas.

Nos últimos artigos vimos como as closures podem ser uteis no dia a dia do desenvolvedor groovy. Porém, o foco deste post em questão é trazer a tona detalhes não tão claros sobre o mundo das closures, nos levando a uma análise um pouco mais profunda antes de substituirmos métodos por closures.

Para começar temos um exemplo de um calculo de soma simples, feito no eclipse com o plugin do groovy, onde o JUnit forneceu as métricas.

Exemplo 1

Comparação Metodo X Closure em cálculos matemáticos:

public class PerfmTests {

	@Test
	public void testMetodos() {
		MetClos mc = new MetClos()
		1000.times{
		mc.m1()
		// depois com
		// mc.m2(10,20)
		}
		Assert.assertNotNull('')
	}

	@Test
	public void testClosures() {
		MetClos mc = new MetClos()
		1000.times{
		mc.c1()
		// depois com
		// mc.c2(10,20)
		}
		Assert.assertNotNull('')
	}

}

class MetClos {
	def c1 = {
		def s = 1 + 1
		println s
	}

	def c2 = { n1,n2 ->
		def s = n1 + n2
		println s
	}

	void m1() {
		def s = 1 + 1
		println s
	}

	void m2(n1,n2) {
		def s = n1 + n2
		println s
	}
}

Neste exemplo os testes com closures foram cerca de 10x mais rápidos que os métodos.

Cuidado

Um ponto negativo de trocar todos os métodos por closures é que cada closure vira um .class distinto e isso pode inchar bastante o perm space da JVM.

Exemplo 2

Comparação Método X Closure em comparações de elementos:

def numberCount = 10000
def random = new Random()
def unorderedList1 = (1..numberCount).collect{random.nextInt()}
def unorderedList2 = (1..numberCount).collect{random.nextInt()}

def timeit = {String message, Closure cl->
    def startTime = System.currentTimeMillis()
    cl()
    def deltaTime = System.currentTimeMillis() - startTime
    println "$message: \ttime: $deltaTime"
}

timeit("compare using closure") {
    def comparator= [ compare: { a,b -> return b <=> a }] as Comparator
    unorderedList1.sort(comparator)
}

timeit("compare using method") {
    Comparator comparator = new MyComparator()
    unorderedList2.sort(comparator)
}

class MyComparator implements Comparator {
    int compare(a, b) {return b <=> a}
}

Neste caso, temos exatamente o contrário. O desempenho da closure foi bem abaixo do esperado.

Para o algoritimo descrito as closures levaram 270ms diante dos métodos que levaram 50ms.

Exemplo 3

Melhorando o desempenho das closures em loops:

Pois bem… Um recurso muito interessante e que pode nos proporcionar o ganho considerável de desempenho, claro quando utilizado com sabedoria, é o groovy .memoize().

Este recurso está disponível a partir do groovy 1.8+.

Explicação do exemplo a seguir

Suponha uma função “distance” que calcula a distância entre dois pontos.

Closure distance tradicional:

def distance = {x1, y1, x2, y2 ->
    sleep(200)  //just to add a delay for demo purposes
    Math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
}

Chamada 5 vezes utilizando uma closure tradicional:

5.times {
    def t1 = System.currentTimeMillis()
    println distance(100, 20, 400, 10)
    println "took: ${System.currentTimeMillis() - t1} milliseconds to execute"
 }
[/sourcecode ]
Após executarmos este trecho de código teremos a seguinte saída:


300.1666203960727
took: 201 milliseconds to execute
300.1666203960727
took: 200 milliseconds to execute
300.1666203960727
took: 201 milliseconds to execute
300.1666203960727
took: 201 milliseconds to execute
300.1666203960727
took: 201 milliseconds to execute
[/sourcecode ]

Sempre que chamamos a closure todos seus itens são chamados novamente, exigindo reprocessamento.

Vamos fazer o mesmo, porém utilizando o <em>memoize</em>

def distance = {x1, y1, x2, y2 ->
    sleep(200)  //just to add a delay for demo purposes
    Math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
}.memoize() //Note now closure is memoized

//To Call It 5 Times
5.times {
    def t1 = System.currentTimeMillis()
    println distance(100, 20, 400, 10)
    println "took: ${System.currentTimeMillis() - t1}"
}

Após executar o código utilizando o memoize temos o seguinte resultado:

300.1666203960727
took: 202 milliseconds to execute
300.1666203960727
took: 1 milliseconds to execute
300.1666203960727
took: 1 milliseconds to execute
300.1666203960727
took: 0 milliseconds to execute
300.1666203960727
took: 0 milliseconds to execute

Pronto! Vemos claramente o quanto podemos ganhar desempenho conhecendo os recursos oferecidos pela linguagem.

Gostaria de salientar que, devemos tomar cuidado para não julgar precipitadamente o que é melhor, levando em consideração que desempenho também tem haver com a forma implementada e recursos oferecidos pelas linguagens.

Como dito anteriormente, estes três posts foram baseados nas respostas postadas no fórum do grails Brasil. Logo, gostaria de agradecer aqueles que contribuíram diretamente para a finalização deste post:

  • José Yoshiriro: Exemplo 1;
  • Raphael Miranda: Observação 1;
  • Pedro Henrique: Exemplo 2,  Exemplo 3;
  • Henrique Lobo Weissmann e Raphael Miranda: Considerações.

Gostaria também de agradecer aqueles que contribuíram indiretamente, os responsáveis pelo conteúdo dos links utilizados no fórum:

  • Michael Pollmeier:  closures significantly slower than methods?
  • Kushal Likhi: memoize sample

Segue links utilizados:

Por: Jonatas Emidio