Dec 11 2011

Aumentando a Performance com o C# Yield

Category: C#Suzuki @ 18:25

Olá pessoal,
Está é uma dica pra você que quer sempre performance na execução de seus programas, uma sitaxe enxuta e simples.
Antes de apresentar-mos alguns exemplos vamos entender um pouco mais da definição do Yield.
A palavra-chave yield sinaliza ao compilador que o método em aparece é um bloco iterator. O compilador gera uma classe para implementar o comportamento que é expresso no bloco iterador. No bloco iterador, a palavra-chave yield é usada junto com a palavra-chave return para fornecer um valor para o objeto enumerador. Esse é o valor retornado, por exemplo, em cada loop de uma intrução foreach. A palavra-chave yield também é usada com break para sinalizar o final da iteração. Para obter mais informações sobre os iteradores, consulte Iterators (C# Programming Guide).

Exemplos de utilização:

  1. yield return <expression>;   
  2. yield break;  

Em uma instrução yield return, a expressão é avaliada e retornada como um valor para o objeto enumerador; a expressão deve ser implicitamente conversível para o tipo de yield do iterador.
Em uma instrução yield break, o controle é incondicionalmente retornado ao chamador do iterador, que é o método IEnumerator.MoveNext (ou seu genérico System.Collections.Generic.IEnumerable(Of T) contraparte) ou o método Dispose do objeto enumerador.
A instrução yield apenas pode aparecer dentro de um bloco iterador, que pode ser implementado como o corpo do método, operador ou o acessador. O corpo de tais métodos, operadores ou acessadores é controlado pelas seguintes restrições:

  • Blocos não seguros não são permitidos.
  • Parâmetros para o método, operador ou assessor não podem ser ref ou out.
  • Uma instrução yield return não pode ser utilizada em qualquer lugar dentro de um bloco try-catch.Pode ser utilizada em um bloco try se o bloco try é seguido por um bloco finally.
  • Uma instrução yield break pode ser utilizada em um bloco catch ou de um bloco try, mas não um bloco finally.

A instrução yield não pode aparecer em um método anônimo.Para obter mais informações, consulte Métodos anônimos (Guia de programação do C#).
Quando usado com expression, uma instrução yield return não pode aparecer em um bloco catch ou em um bloco try que possui uma ou mais cláusulas catch. Para obter mais informações, consulte Exceção tratamento instruções (referência C#).

Para mais informações a respeito do yield visite o MSDN.

Até o momento fizemos nossa lição de casa e por consequencia aprendemos a nossa boa e velha teoria.
Teoria boa é aquela que colocamos em prática. Portanto, agora vamos fazer uma comparação.

Criando um método para retornar um lista com inteiros, repare no processo instanciamos uma lista e vamos adicionado o nosso valor a esta lista e ao final retornamos esta lista.

  1. public static IEnumerable<long> GetList()   
  2. {   
  3.     var list = new List<long>();   
  4.     for (long i = 0; i < 10000000; i++)   
  5.         list.Add(i);   
  6.   
  7.     return list;   
  8. }  

Agora vamos usar o C# yield, repare que não é necessário instanciar a lista e a sintaxe fica muito mais simples.

  1. public static IEnumerable<long> GetListYield()   
  2. {   
  3.     for (long i = 0; i < 10000000; i++)   
  4.         yield return i;   
  5. }  

Até o momento visualmente falando os métodos são iguais e fazem a mesma coisa. Mas qual deles é o melhor?
O que você faria para verificar? Instanciar uma variavel de tempo e contar o tempo antes e ao final da execução dos métodos?
É uma boa, mas se a execução for tão rapida que não de para ver a diferença?
Neste caso usaremos o System.Diagnostics para nos auxiliar especificamente utilizaremos a classe Stopwatch.

  1. public void Execute()   
  2. {   
  3.     var stopwatch = new System.Diagnostics.Stopwatch();   
  4.     stopwatch.Start();   
  5.     var list1 = GetList();   
  6.     stopwatch.Stop();   
  7.     var elapsedTicks1 = stopwatch.ElapsedTicks;   
  8.   
  9.     stopwatch.Restart();   
  10.     var list2 = GetListYield();   
  11.     stopwatch.Stop();   
  12.     var elapsedTicks2 = stopwatch.ElapsedTicks;   
  13. }  

Ok! Faça o debug ai em sua máquina e verifique o quão rápido é o método que utiliza o Yield.
Apenas pra ilustrar este post pra mim o elapsedTicks1 deu 437031 e o elapsedTicks2 deu 1158.

Até a próxima.

E, naqueles dias, apareceu João o Batista pregando no deserto da Judéia, E dizendo: Arrependei-vos, porque é chegado o reino dos céus. (Mateus 3:1,2)

Tags: , , , , , , , ,

Comments

1.
Oguzhan Eren Oguzhan Eren Turkey says:

this test is wrong because GetListYield() function not call yet.
for correct way you must GetListYield().ToList() or GetListYield().ToArray() functions.

2.
Gustavo Gustavo Brazil says:

Muito bom, com isso da para fazer um limpa no codigo.

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading