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 :
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.
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
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
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”.
Foi um erro meu mesmo, ainda tenho dificuldade de compreender big o notation 🙈.
Valeu pelo feedback