Stopwatch – Como controlar a performance do seu código.    

Boa noite pessoal, hoje vou com um post pequeno, porém, de muita importância para as pessoas. Na minha experiência, vi que muitas pessoas, inclusive eu algumas vezes, não sabem muito bem como medir desempenho dos códigos. Muitas vezes, quando as pessoas querem saber quanto tempo dura uma execução de um código, acabam utilizando as classes DateTime e/ou TimeSpan.

Na realidade é comum armazenar um DateTime.Now antes do método a ser verificado e subtrair o valor de um novo DateTime.Now do final do código. Da seguinte Forma:

	static void Main(string[] args)
        {

            var ini = DateTime.Now;

            DoWork();

            var fim = DateTime.Now.Subtract(ini);

            Console.WriteLine("Tempo total: {0}", ini.TimeOfDay.Milliseconds);

            Console.ReadKey();
        }

        private static void DoWork()
        {
            foreach(string dir in Directory.GetDirectories(@"C:\")){

                Console.WriteLine(dir);
            }
           
        }

Até parece funcionar, porém, utilizando este código para verificar desempenho, você já está perdendo desempenho, porque o DateTime é uma estrutura um pouco complexa para uma simples validação de tempo decorrido, além da necessidade de fazer uma subtração também ter um certo custo para o trabalho.

Agora, se utilizarmos a classe Stopwatch, muita coisa pode mudar. Além de ser uma classe muito mais simples, o seu intuito é realmente controlar tempos decorridos. Sua performance é muito melhor, e se alterarmos o código acima para utilizar Stopwatch, veremos que o tempo decorrido é bem menor. Na realidade o tempo exibido é bem mais correto, porque utilizando DateTime, muito do tempo perdido é para a criação da estrutura do DateTime.

Com o Stopwatch ficaria assim:

	static void Main(string[] args)
        {

            var tempo = new Stopwatch();

            tempo.Start();

            DoWork();

            tempo.Stop();

            Console.WriteLine("Tempo total: {0}", tempo.Elapsed.Milliseconds);

            Console.ReadKey();
        }

Se você executar os códigos acima, verá que o tempo decorrido com o Stopwatch é muito inferior. Além da utilização ser mais simples.

A classe Stopwatch, localizada no namespace System.Diagnostics, tem métodos muitos simples. Basicamente você começa a controlar o tempo invocando o método Start e quando desejar parar de controlar o tempo decorrido invoca o método Stop.

Para validar o tempo decorrido, você utiliza as propriedades Elapsed, que retorna um TimeSpan, ou a propriedade ElapsedMilliseconds, que retorna os milissegundos decorridos, ou a propriedade ElapsedTicks, que, adivinhem só, retorna os Ticks decorridos desde o Start.

O Stopwatch pode ser utilizado para o controle de vários intervalos de tempo. Para utilizar dessa forma, você pode simplesmente invocar o método Start novamente após o Stop, e o contador do tempo decorrido continuará sendo contado a partir do ponto onde parou, ou seja, o Stop funciona como uma pausa na contagem do tempo decorrido. Se você realmente quiser reiniciar o contador, deve invocar o método Reset.

Além do que já foi falado, o Stopwatch tem apenas mais uma propriedade que não comentei, a propriedade IsRunning, que retorna um booelano indicando se o contador está rodando ou não.

Para finalizar, é possível simplificar ainda mais o código, utilizando o método estático StartNew da classe Stopwatch para criar a estância da classe e já invocar o método Start automaticamente, o código fica um pouco mais simples, repare:

	static void Main(string[] args)
        {
	    //A linha abaixo substitui a invocação do new() e do Start()
            var tempo = Stopwatch.StartNew();
            
            DoWork();

            tempo.Stop();
            
            Console.WriteLine("Tempo total: {0}", tempo.Elapsed.Milliseconds);

            Console.ReadKey();
        }

Bom, fica ai a dica de quando você precisar controlar a performance do seu código. É muito mais simples, mais rápido e mais correto utilizar o Stopwatch ao invés do DateTime ou outra técnica qualquer para controlar tempos decorridos.

Espero que possa ajudá-los.

Até o próximo post.

7. outubro 2010 08:11 by Frederico | Comments (1) | Permalink

O que são Lambda Expressions?    

Lambda Expression para muitos pode ser um assunto simples, uma pratica simples, porém, sempre que começo a usá-las em meu código, vejo muitas interrogações nas pessoas em minha volta, e participando do TechEd neste ano, pude perceber que isso é muito comum, porque quando estava saindo de uma palestra do Otavio Pecego Coelho, sobre paralelismo, ouvi muitas pessoas comentando: “Putz, muito louco, mas quando começa a entrar um monte de ‘Lambda’ começa a ficar meio complicado.” .Bom, não sei como isso pode ser possível, visto que Lambda Expression só veio para facilitar, como veremos ao longo desse post.

Pensando nisso, e conversando com um amigo meu, ele me deu a sugestão de escrever sobre isso aqui, e lá vai. Caso tenham dúvidas ou sugestões, please let me know!

Um pouco do histórico

As expressões Lambdas não são uma grande inovação do time do .Net, na realidade, como a maioria das novidades que surgiram entre o .Net 3.0 e 3.5, elas são evoluções naturais dos rumos que a C# vinha tomando desde a sua criação. Vamos ver como isso aconteceu.

Delegates

Delegate é o princípio de tudo. Delegate resumidamente é um tipo por referência, que armazena ponteiros para métodos. Com a criação de uma Delegate, você defini a assinatura de um método que pode ser armazenado em uma variável do tipo dessa Delegate, ou seja, os métodos “apontados” nas variáveis de tipo Delegate devem seguir as regras definidas na Delegate.

Podemos usar Delegates para “delegar” a responsabilidade de execução de uma lógica específica a um outro método, que pode ser implementado depois.

A definição de uma delegate é a seguinte:

[modificador] Delegate [tipo de retorno [Nome da Delegate] ([parâmetros de entrada])

public delegate int Conta(int v1,int v2);

Definindo essa Delegate, qualquer método que tenha essa assinatura pode ser delegado a uma variável do tipo Conta. Os eventos basicamente utilizam Delegates para definir qual deve ser a assinatura de um método que implementa um evento. A Delegate do evento basicamente é a seguinte:

public delegate void Evento(object sender, EventArgs args)

E como podemos utilizar? O exemplo padrão sempre é o da calculadora. Imagina que queiramos delegar ao usuário a opção de escolher qual operação ele deseja executar. Vamos criar dois método, Somar e Subtrair. O código completo de uma aplicação console está abaixo:

    delegate int Conta(int v1,int v2);
    class Program
    {
        static int Soma(int valor1, int valor2)
        {
            return valor1 + valor2;
        }
        static int Subtrair(int valor1, int valor2)
        {
            return valor1 + valor2;
        }

        static void Main(string[] args)
        {
            Console.Write("Valor 1:");
            var v1 = Console.ReadLine();
            Console.Write("Valor 2:");
            var v2 = Console.ReadLine();
            Console.Write("Operação(+ ou -):");
            var op = Console.ReadLine();

            //Operação padrão é soma
            Conta operacao = Soma;

            switch (op)
            {
                case "+":
                    operacao = Soma;
                    break;
                case "-":
                    operacao = Subtrair;
                    break;
            }

            //Chamo o método que o usuário selecionou, sem me importar qual é
            var ret = operacao(Convert.ToInt32(v1), Convert.ToInt32(v2));

            Console.WriteLine("Resultado: {0}", ret);
            Console.ReadKey();
        }
    }

Assim estamos delegando ao usuário qual método utilizar. Com esse entendimento, podemos já perceber, como a equipe do C# percebeu, vários momentos onde utilizar ponteiros de métodos por parâmetro.

Obs.: Como Delegate obriga a implementação de uma assinatura, por isso os tipos e a quantidade de parâmetros são importantes, porém não o nome dos parâmetros.

Em Collections podemos ver o uso desta técnica em muitos métodos, por exemplo, o Sort, Find, etc. A equipe do .Net teve o segundo dialogo (obviamente o dialogo seguinte é inventado):

“Por que não podemos ter um Collection qualquer, e deixar que o programador se preocupe em como ordenar? “

“Sim, e para isso ele passa por parâmetro o método, desde que ele implemente a assinatura, respeitando uma Delegate”

Certo, então da mesma forma que podemos criar variáveis de tipos de Delegate, podemos utilizar parâmetros de tipos Delegate. Por exemplo, para utilizar o método Find, implementamos uma Delegate chamada Predicate, inclusive a utilização dos métodos de Collection são de predicados por esse motivo.

public delegate bool Predicate(T obj);

Onde T é o tipo da sua lista, ou seja, uma lista List<int> o T seria int. A implementação do método abaixo seria a seguinte:

	static bool Buscar(int valor)
        {
            return valor == 1;
        }

Sua chamada seria a seguinte:

           List<int> lista = new List<int>();
            //alimenta a lista
            
            var itemEncontrado = lista.Find(Buscar);

Certo, com isso entendemos como funciona uma Delegate, então vamos evoluir até chegar a Lambda Expression.

Métodos Anônimos

A equipe do C# percebeu que a adoção de Delegates era muito boa, porém, é um tanto trabalhosa, pois você precisa criar métodos sempre que precisar utilizar Delegate, o que te faz ter que digitar mais, e o seu código pode ficar mais confuso.

Imagine que você precisa realizar a busca citada acima apenas em um trecho especifico de código, você teria a necessidade de criar um método, mesmo que sua utilização fosse extremamente restrita, e o pessoal da Microsoft pensou: “Por que não criarmos apenas o código do método, e não o método em si? Ficaria mais simples e mais rápido”.

Ótimo idéia, e assim entra o método anônimo no jogo.

Métodos anônimos são métodos, como o próprio nome diz, sem nomes. São métodos criados dentro de outros métodos, adicionando seu conteúdo diretamente em uma variável ou parâmetro do tipo Delegate.

Por exemplo, em vez de criarmos o método Busca acima, poderíamos colocar o seu código diretamente na chamada do método:

	var item = lista.Find(delegate (int valor){
                return valor == 1;
            });

O resultado seria o mesmo, com a diferença de não precisarmos criar um novo método na classe. Essa técnica pode ser utilizada em qualquer situação onde usaríamos o método ponteiro para a Delegate, ou seja, variáveis, parâmetros, eventos, etc.

Finalmente Lambda Expression

Bom, diante de tudo que vimos acima, percebemos que codificar “on the fly” dessa forma agiliza muito o processo de desenvolvimento, além de não deixar códigos muito específicos por toda parte da classe.

Mas ai ainda percebemos que digitamos muito além do necessário. Pensa comigo:

“Para que a palavra chave “delegate”? Afinal, eu já sei que é uma delegate.”

“Para que ter que definir o tipo do parâmetro?” Afinal, se o compilador já sabe qual é a Delegate a ser utilizada, logo ele já sabe quais são os os tipos e a quantidade dos parâmetros.”

“E as vezes o método é tão simples, que a única linha já é o retorno, então para que utilizar o return?”

Aí mais uma vez o time do C# deu uma forcinha, dizendo: “Vamos simplificar tudo criando um novo operador que serve para separar os parâmetros do método do corpo do método e pronto!”

E o Find da Collection ficou assim:

var item = lista.Find(valor=>valor == 1);

Agora sim o método ficou sucinto, o que está antes do => (igual maior) são os parâmetros de entrada, e o que está depois é o retorno do método caso tenha só uma linha, ou o corpo do método, caso tenha mais de uma linha.

Se quisermos criar uma Lambda Expression para nossa delegate Conta criada acima, seria o seguinte:

 
operacao  = (valor1, valor2)=> valor1 + valor2;
var ret = operacao(Convert.ToInt32(v1), Convert.ToInt32(v2));

Muito simples! Perceba que para mais de um parâmetro, adicionamos parênteses. Caso o corpo do método seja mais complexo, com mais de uma linha, teremos que utilizar chaves e no final utilizar a palavra chave return.

Conclusão

Neste post vimos como Lambda Expression nada mais é que uma evolução natural das técnicas utilizadas pelo C# desde sua primeira versão.

Por ser uma evolução, isso só pode surgir para ajudar e simplificar nossa vida. Caso não esteja sendo claro em nossas cabeças as evoluções que a linguagem vem tomando, é importante que entendamos o porquê das mudanças.

Muitos já criticaram o “dynamic” do C#, sem ainda entender o porque de se utilizá-lo. Vou tentar abordar isso em um momento oportuno.

Bom, espero que tenham gostado desse post.

Até o próximo!

24. setembro 2010 06:51 by Frederico | Comments (4) | Permalink

Sobre mim

Minha Imagem

Meu nome é Frederico Batista Emídio, trabalho com desenvolvimento de sistemas profissionalmente a oito anos, porém, já faço sites pessoais a pelo menos dez anos.

Para saber mais clique aqui.

Páginas

Calendário

<<  novembro 2017  >>
seteququsedo
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET