none
Performance com coleções RRS feed

  • Pergunta

  • Olá pessoal,

     

             No loading da minha pagina principal carrego 1 coleção em memoria ( List<> ), após isso uso ela para fazer alguns selects em sua coleção e jogar a informação buscada pelo usuário em um grid. Até então essa rotina funciona muito bem, o problema que após algumas buscas, ela fica muito lenta. O que pode estar havendo? E como solucionar isso?

                var query = from r in Bridge.supItems where r.DescricaoInterna.ToUpper().Contains(txtBusca.Text.ToUpper()) select r;
                objColSuprimento.Clear();
               
                foreach (Suprimento item in query)
                {
                    Suprimento sup = new Suprimento();
                    sup.DescricaoInterna = item.DescricaoInterna;
                    sup.SuprimentoID = item.SuprimentoID;

                    objColSuprimento.Add(sup);
                }
                gridSuprimento.DataSource = objColSuprimento;
                if (gridSuprimento.GetGridValue(gridSuprimento.FocusedRowVisibleIndex, "DescricaoInterna") != null)
                {
                    txtInfo.Text = gridSuprimento.GetGridValue(gridSuprimento.FocusedRowVisibleIndex, "DescricaoInterna").ToString();
                }

    Codigo acima que fica dentro do evento de busca!

     

    Obrigado!


    kaneda182
    segunda-feira, 28 de junho de 2010 20:43

Respostas

  • Olá Kaneda,

    Se o seu post fica durante muito tempo sem atividade, normalmente sinalizamos a ultima resposta como resposta final.

    Agora quanto ao seu problema, de quantos registros estamos falando?

    Se a sua quantidade de registros na base for relativamente grande, esse seu foreach vai dar picos de processamento mesmo.

    Agora tente uma coisa:

    Se seu Bridge aí é um List<Suplemento> em vez de fazer esse foreach aí sete o datasource do seu Grid direto com sua query assim:

     gridSuprimento.DataSource = (from r in Bridge.supItems where r.DescricaoInterna.ToUpper().Contains(txtBusca.Text.ToUpper()) select r);

    Você provavelmente vai eliminar os picos de processamento.


    Ricardo Dorta
    Arquiteto de Software
    MCP,MCAD,MCSD
    Ajudou? Marque como reposta!!!.
    blog: http://blogs.makesys.com.br/dorta
    twitter : http://twitter.com/dortaway
    • Marcado como Resposta Daniel Ferreira quarta-feira, 14 de julho de 2010 21:47
    quarta-feira, 14 de julho de 2010 20:55
    Moderador

Todas as Respostas

  • Hi,

    suspeito que o list seja o objColSuprimento certo? e o Bridge.supItems seja sua entity do service correto?

    se for isso voce esta carregando seu service toda vez que faz uma pesquisa, acredito que seja desnecessario. carrege uma unica vez o list e faca o select no list.

    se nao for... quem sao esses dois objetos... porque eu nunca vi um list que tenha uma propriedade supItems

    Att,


    Adriel Codeco Silva
    Email: adriel.silva@uppercase.com.br
    MSN: adrielcodeco@hotmail.com
    Blog: adrielcodeco.wordpress.com
    Uppercase – www.uppercase.com.br

    R. Primeiro de Março, 661 – Centro Barra Bonita - SP - CEP 17340-000
    quarta-feira, 30 de junho de 2010 03:20
    Moderador
  • Opa então.

    No loading do sistema , carrego esse Bridge.supItems que na verdade é uma List<Suprimentos> por usar muito em varios lugares , resolvi carregar todas as informações nessa list para evitar de fazer varios select na base. Então eu faço uma query nessa collection buscando o que preciso e jogo somente a informação necessaria na objColSuprimento que é um ObservableCollection<Suprimentos>.

    Mas infelizmente depois de algum tempo fica muito lento e inviavel o uso.Gostaria de saber onde está o gargalo e como resolver isso.

    Obrigado!


    kaneda182
    quarta-feira, 30 de junho de 2010 14:11
  • Hi,

    Qual a necessidade do objColSuprimento?

    seu select poderia retornar os dois campos que voce usa no grid. assim nao precisaria do objColSuprimento.

    var query =
        (from r in Bridge.supItems
        where r.DescricaoInterna.ToUpper().Contains(txtBusca.Text.ToUpper())
        select new
                 {
                     r.
    DescricaoInterna,
                     r.SuprimentoID
                 }).ToList();

    gridSuprimento.DataSource = query;

    Bom, acho que so de tirar o foreach ja e um bom ganho de performance.

    Att,


    Adriel Codeco Silva
    Email: adriel.silva@uppercase.com.br
    MSN: adrielcodeco@hotmail.com
    Blog: adrielcodeco.wordpress.com
    Uppercase – www.uppercase.com.br

    R. Primeiro de Março, 661 – Centro Barra Bonita - SP - CEP 17340-000
    quarta-feira, 30 de junho de 2010 15:37
    Moderador
  • Então o problema que gostaria de saber qual seria o problema dessa lentidão após 5 ou 6 buscas. Sendo que a primeira, segunda e terceira buscas são muito rápidas. Gostaria de saber porque está ocorrendo isso. Não somente nestá tela, mas em todas as telas de pesquisa que não estão relacionadas a esta, está com o mesmo problema, esse seria só um exemplo do que está havendo.

     

    Obrigado!


    kaneda182
    quarta-feira, 30 de junho de 2010 16:30
  • Hi,

    Debuga entao amigo, e descobre onde esta demorando para processar, nao tem como agente adivinhar onde e porque esta deixando lento.

    Att,


    Adriel Codeco Silva
    Email: adriel.silva@uppercase.com.br
    MSN: adrielcodeco@hotmail.com
    Blog: adrielcodeco.wordpress.com
    Uppercase – www.uppercase.com.br

    R. Primeiro de Março, 661 – Centro Barra Bonita - SP - CEP 17340-000
    • Marcado como Resposta Daniel Ferreira terça-feira, 13 de julho de 2010 17:58
    • Não Marcado como Resposta kaneda182 quarta-feira, 14 de julho de 2010 19:46
    quarta-feira, 30 de junho de 2010 23:30
    Moderador
  • No primeiro post está todo conteudo do evento, é somente aquilo.

    Engraçado que fiquei monitorando a rotina usando o gerenciador de tarefas e percebi que durante este processo é consumido em torno de 50%~60% de CPU, não é muito para uma operação como esta?

     


    kaneda182
    quarta-feira, 14 de julho de 2010 19:50
  • Olá Kaneda,

    Se o seu post fica durante muito tempo sem atividade, normalmente sinalizamos a ultima resposta como resposta final.

    Agora quanto ao seu problema, de quantos registros estamos falando?

    Se a sua quantidade de registros na base for relativamente grande, esse seu foreach vai dar picos de processamento mesmo.

    Agora tente uma coisa:

    Se seu Bridge aí é um List<Suplemento> em vez de fazer esse foreach aí sete o datasource do seu Grid direto com sua query assim:

     gridSuprimento.DataSource = (from r in Bridge.supItems where r.DescricaoInterna.ToUpper().Contains(txtBusca.Text.ToUpper()) select r);

    Você provavelmente vai eliminar os picos de processamento.


    Ricardo Dorta
    Arquiteto de Software
    MCP,MCAD,MCSD
    Ajudou? Marque como reposta!!!.
    blog: http://blogs.makesys.com.br/dorta
    twitter : http://twitter.com/dortaway
    • Marcado como Resposta Daniel Ferreira quarta-feira, 14 de julho de 2010 21:47
    quarta-feira, 14 de julho de 2010 20:55
    Moderador
  • O problema de performance está justamente de vc fazer consultas no List de valores.

    O linq é uma maravilha, mas tem que ser usado com extrema cautela, principalmente quando se faz queries de List com grande volume de dados. Isto é extremamente lento e usa muita memória.

    Como solução para o seu problema, sugiro que vc use o Dictionary ao invés do List. Assim, vc guardaria em cache um Dictionary indexado por DescricaoInterna. Sei que DescricaoInterna pode possuir duplicatas, assim é necessário criar um Dictionary<string, List<Suprimento>> .

    Sugiro vc guardar em cache o Dictionary. Assim, quando vc ler os valores da base de dados, use a lógica descrita abaixo e já crie o Dictionary. A partir daí, toda vez que vc usar o cache, sua busca será instantânea e não usará memória nenhum.

    Pense do seguinte modo:

    Um list é como se fosse uma tabela do banco de dados, e o Dictionary, sua PK.

     

    Coloco um projeto pronto pra vc entender e testar meu raciocínio. Escrevi o projeto pra ficar parecido com as classes que vc usa. Crie um projeto Console e troque todo o texto da classe Program pelo abaixo:

     

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;

    namespace PerfDictionary
    {
        class Program
        {

            static void Main(string[] args)
            {
                // número de repetições
                int count = 10000000;
                List<Suprimento> objColSuprimento;
                long tempoCollection, tempoDictionary, tempoDictionaryJaCriado;

                // gerar massa de testes
                Bridge Bridge = new Bridge();
                for (int i = 1; i <= count; i++)
                {
                    Bridge.supItems.Add(new Suprimento { DescricaoInterna = "Descrição " + (i & count / 1000), SuprimentoID = i });
                }

                string textoBusca = "Descrição " + 10;

                // --------------------------- Usando Collections ---------------------------
                Console.WriteLine("Usando Collections");
                Stopwatch sw = new Stopwatch();
                sw.Start();

                var query = from r in Bridge.supItems where r.DescricaoInterna.ToUpper().Contains(textoBusca) select r;
                objColSuprimento = new List<Suprimento>();

                foreach (Suprimento item in query)
                {
                    Suprimento sup = new Suprimento();
                    sup.DescricaoInterna = item.DescricaoInterna;
                    sup.SuprimentoID = item.SuprimentoID;

                    objColSuprimento.Add(sup);
                }

                sw.Stop();
                tempoCollection = sw.ElapsedMilliseconds;


                // --------------------------- Criando e usando um Dictionary ---------------------------
                // Cria um Dictionary indexado por String e valores de List<Suprimento>
                Console.WriteLine("Criando e usando um Dictionary");
                sw = new Stopwatch();
                sw.Start();
                var dict = Bridge.supItems.GroupByToDictionary(p => p.DescricaoInterna, StringComparer.InvariantCultureIgnoreCase);

                objColSuprimento = new List<Suprimento>();

                if (dict.ContainsKey(textoBusca))
                {
                    foreach (Suprimento item in dict[textoBusca])
                    {
                        Suprimento sup = new Suprimento();
                        sup.DescricaoInterna = item.DescricaoInterna;
                        sup.SuprimentoID = item.SuprimentoID;

                        objColSuprimento.Add(sup);
                    }
                }

                sw.Stop();
                tempoDictionary = sw.ElapsedMilliseconds;

                // --------------------------- Usando um dictionary já criado ---------------------------
                Console.WriteLine("Usando um dictionary já criado.");
                sw = new Stopwatch();
                sw.Start();
                objColSuprimento = new List<Suprimento>();

                if (dict.ContainsKey(textoBusca))
                {
                    foreach (Suprimento item in dict[textoBusca])
                    {
                        Suprimento sup = new Suprimento();
                        sup.DescricaoInterna = item.DescricaoInterna;
                        sup.SuprimentoID = item.SuprimentoID;

                        objColSuprimento.Add(sup);
                    }
                }

                sw.Stop();
                tempoDictionaryJaCriado = sw.ElapsedMilliseconds;

                Console.WriteLine("Tempos:  List={0}, Dictionary={1}, Dictionary já Criado={2}", tempoCollection, tempoDictionary, tempoDictionaryJaCriado);

            }

            static void Main(string[] args)
            {
                // número de repetições
                int count = 10000000;
                List<Suprimento> objColSuprimento;
                long tempoCollection, tempoDictionary, tempoDictionaryJaCriado;

                // gerar massa de testes
                Bridge Bridge = new Bridge();
                for (int i = 1; i <= count; i++)
                {
                    Bridge.supItems.Add(new Suprimento { DescricaoInterna = "Descrição " + (i & count / 1000), SuprimentoID = i });
                }

                string textoBusca = "Descrição " + 10;

                // --------------------------- Usando Collections ---------------------------
                Console.WriteLine("Usando Collections");
                Stopwatch sw = new Stopwatch();
                sw.Start();

                var query = from r in Bridge.supItems where r.DescricaoInterna.ToUpper().Contains(textoBusca) select r;
                objColSuprimento = new List<Suprimento>();

                foreach (Suprimento item in query)
                {
                    Suprimento sup = new Suprimento();
                    sup.DescricaoInterna = item.DescricaoInterna;
                    sup.SuprimentoID = item.SuprimentoID;

                    objColSuprimento.Add(sup);
                }

                sw.Stop();
                tempoCollection = sw.ElapsedMilliseconds;


                // --------------------------- Criando e usando um Dictionary ---------------------------
                // Cria um Dictionary indexado por String e valores de List<Suprimento>
                Console.WriteLine("Criando e usando um Dictionary");
                sw = new Stopwatch();
                sw.Start();
                var dict = Bridge.supItems.GroupByToDictionary(p => p.DescricaoInterna, StringComparer.InvariantCultureIgnoreCase);

                objColSuprimento = new List<Suprimento>();

                if (dict.ContainsKey(textoBusca))
                {
                    foreach (Suprimento item in dict[textoBusca])
                    {
                        Suprimento sup = new Suprimento();
                        sup.DescricaoInterna = item.DescricaoInterna;
                        sup.SuprimentoID = item.SuprimentoID;

                        objColSuprimento.Add(sup);
                    }
                }

                sw.Stop();
                tempoDictionary = sw.ElapsedMilliseconds;

                // --------------------------- Usando um dictionary já criado ---------------------------
                Console.WriteLine("Usando um dictionary já criado.");
                sw = new Stopwatch();
                sw.Start();
                objColSuprimento = new List<Suprimento>();

                if (dict.ContainsKey(textoBusca))
                {
                    foreach (Suprimento item in dict[textoBusca])
                    {
                        Suprimento sup = new Suprimento();
                        sup.DescricaoInterna = item.DescricaoInterna;
                        sup.SuprimentoID = item.SuprimentoID;

                        objColSuprimento.Add(sup);
                    }
                }

                sw.Stop();
                tempoDictionaryJaCriado = sw.ElapsedMilliseconds;

                Console.WriteLine("Tempos:  List={0}, Dictionary={1}, Dictionary já Criado={2}", tempoCollection, tempoDictionary, tempoDictionaryJaCriado);

            }

        }


        class Bridge
        {
            public List<Suprimento> supItems = new List<Suprimento>();
        }

        class Suprimento
        {
            public string DescricaoInterna { get; set; }
            public int SuprimentoID { get; set; }
        }

        public static class CollectionUtil
        {

            /// <summary>
            /// Método que agrupa um Dictionary, com valores duplicados gravados num List de valores
            /// </summary>
            /// <typeparam name="TKey"></typeparam>
            /// <typeparam name="TSource"></typeparam>
            /// <param name="source"></param>
            /// <param name="keySelector"></param>
            /// <returns></returns>
            public static Dictionary<TKey, List<TSource>> GroupByToDictionary<TKey, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer = null)
            {
                var dic = comparer != null ? new Dictionary<TKey, List<TSource>>(comparer) : new Dictionary<TKey, List<TSource>>();
                foreach (var pair in source.GroupBy(keySelector))
                    dic.Add(pair.Key, pair.ToList());
                return dic;
            }

        }

    }

     

    segunda-feira, 9 de setembro de 2013 05:37