Sobre otimização em aplicações .Net – Parte 2

Bom, continuando o post anterior: Sobre Otimização em aplicações .Net – Parte 1, se você não leu, vai lá,
porque eu estou tentando ser o mais detalhista possível.

Nós paramos no seguinte cenário:

eu dei uma lida no código inteiro e não particularmente não vi nada gritante muito explícito, sendo assim, já posso começar fazer o que acho mais conveniente.

Quebrando o problema em partes.

Bom, o código é grande o suficiente para que minha eficiência seja baixa, então vamos separar em 5 pontos.

  • Carregar os mods.
  • Identificar os conflitos
  • Atualizar a lista de conflitos
  • Atualização da GUI
  • Feedback via progress Bar

Destes pontos eu preciso identificar aonde está o maior tempo

O que se resume em :

https://gist.github.com/millerscout/6f2db01d0e304f997ec3c61efdc9b5c8
Sim… um código muito grande não é mesmo? eu também acho que é necessário dividir pra conquistar.

Coletando Métricas

Primeiro passo foi separar todo o código que eu tinha em um ambiente mais controlado, afinal neste mesmo projeto outros itens utilizam o mesmo recurso de Feedback pro usuário e atualizam a GUI sem impacto, logo nosso foco é mais específico.

Não sei se costumam usar o profiler do VS, se não tem o costume, comecem, ele é mais barato que quebrar a cabeça, chutar o problema ou esperar que mágicamente o problema suma.

Eu tenho uma lista de Profilers que já usei, o do VS é okay, meu favorito? nProfiler, como a ferramenta é cerca de R$ 3000, vou demorar um pouco pra adquirir e apresentar a diferença em um post futuro.

Voltando ao assunto…

Coloquei o breakpoint em 5 pontos:

  • Na primeira linha do main.
  • Antes de carregar os mods pra memória.
  • Antes de carregar as alterações do jogo base.
  • Carragando as mudanças do Mod.
  • Última linha.
Bom perceberam? quando clica nos eventos na parte superior dá pra identificar a quantidade de GC, Memória e CPU está sendo consumido, as setas amarelas são execuções de limpeza do GC 😉

essa é uma forma, fácil de saber qual dos itens você quer começar a otimização, quer uma mais fácil ainda sem breakpoints?

Alt+F2, selecione CPU Usage e rode o projeto.

só faltou o VS programar pra você agora né?

Vamos nos focar nesse caso aqui

faz sentido demorar eim..

Várias iterações que podem ser simplificadas.

Dados coletados, bora pro código

Vamos dar um tapa nessa parte, e mensurar os ganhos.

O Commit da alteração e o resultado:

de certa forma melhorou, reduzimos um pouco o GC, aumentou o tempo para processamento mas já esperávamos, já que não processamos mais paralelamente as alterações, Estamos dependendo do IO, dá pra fazer mais nada… será?

O Inimigo agora é outro:

Mais uma iteração no commit mudanças:

  • Mudei para acessar o dictionary diretamente e atualizar o objeto
  • Remoção do concurrentBag.
  • Alteração de classes para structs de ModListChanges e DetailChanges

Gen 0:499
Gen 1:103
Gen 3:10

estamos no caminho certo, começando respeitar o GC, executamos mesmo processo e em alguns segundos mais rápido, está bom, mas vamos buscar a excelência, ou até onde minha capacidade der, mas vocês podem me mandar msg pra melhorarmos ainda mais 🙂

Notas:

  • Percebeu como eu ignorei o fato que estou usando 3 objetos estáticos em uma classe nada haver “Helpers”?
    pois é, o foco está em resolver o problema depois pensamos em uma forma elegante de organizar o código.
  • Tenho quase certeza que estava usando o updateFactory e addFactory errado naquele dictionary :x, nem vou olhar porque deu vergonha
  • paramos de atualizar um objeto em memória sem necessidade no DetailChanges.
  • Simplifiquei a forma que era gerado o Hash.

Vou encerrar mais esse post, acredito que temos grandes lições aprendidas.
E lembrem-se para tomar uma ação precisamos testar, mensurar, mudar e repetir.

Próximo post.. quando eu tiver tempo :p

4 comentários em “Sobre otimização em aplicações .Net – Parte 2

  1. Artigo interessante, mas de onde você tirou essa notação O(n2 log n)? ListOfGameData e mod.items.Values são inputs distintos, então não pode ser N quadrático, mas sim N * M. Quanto a parte logarítmica também está incorreto, o paralelismo não não traduz esse tipo de comportamento, mas sim “M / NumeroThreads”.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *