Recentemente, compartilhei uma excelente palestra, do Feredico Lois, colega no desenvolvimento do RavenDB, sobre padrões para alta performance com C#. Ele começa a palestra falando sobre três estratégias para otimização que gostaria de abordar aqui.

1. Pareto – 20% do código consome 80% dos recursos

Essa é a condição da maioria das aplicações.

Boa parte das vezes em que sou chamado para consultoria percebo que há oportunidade para trabalhar e obter excelentes resultados de performance revisando:

  • arquitetura – componentes mal planejados e desnecessariamente acoplados, que exigem interação constante com os demais componentes gerando overhead assombroso. Desperdício de recursos computacionais disponíveis.
  • implicações da rede e/ou I/O – muito comum! desenvolvedores ignoram as falácias da computação distribuída, por ter  “tudo carregado na máquina” durante o desenvolvimento.
  • algoritmos ou falhas de implementação – abordagens infelizes – mal implementadas ou selecionadas – que ignoram características do ambiente (como o Garbage Collector).

Geralmente, é possível produzir grandes melhorias com pouco esforço relativo. 

No RavenDB, por exemplo, revisitamos a forma como tratamos a alocação de memória. No Roslyn, a Microsoft adotou estratégias muito inteligentes de caching, …

Há pouco tempo, em um cliente, ajudei a reduzir a alocação de memória – o que ajudou a reduzir a pressão sobre o GC – e melhoramos a interação entre componentes. O tempo de processamento crítico caiu em 85%.

Em outro cliente, verifiquei que uma aplicação estava fazendo mais de cem requisições para compor uma tela! Reduzimos para 10 (ainda acho muito)! Tivemos ganho de 47%.

Em outro cliente, percebi que a tela principal do sistema renderizava uma quantidade absurda de gráficos e indicadores que, … ninguém olhava. Cortamos! Ganho de 85%.

Em outro caso, verificamos que boa parte do processamento que estava ocorrendo de forma sequencial poderia ser executada de forma paralela. Reduzimos o tempo de processamento para 1/4 do tempo original.

Recenetemente, ajudei uma grande empresa de simulações a levar processamento da CPU para a GPU. Baixamos o tempo de processamento de uma área crítica de 3 horas para 27 segundos.

2. Pareto2 – 4% do seu código consome 64% dos recursos

Quando a arquitetura está bem definida, a rede é “respeitada” e o código está bem escrito (respeitando o GC, sobretudo), o caminho para a otimização está na revisão dos argoritmos e das estruturas de dados utilizadas.

No RavenDB, por exemplo, substituímos a forma como representamos JSON na memória e isso reduziu a complexidade de acesso a dados de O(n log n) para O(1). Foi uma das mudanças que tornou o RavenDB4 10x mais rápido que as versões anteriores.

O esforço para revisão de algoritmos é de complexidade média. Os ganhos podem ser enormes!

3. Pareto3 – 0,8% do seu código consome 51% dos recursos

Quando a arquitetura está bem definida, a rede é “respeitada”, o código está bem escrito (respeitando o GC, sobretudo), e os algoritmos certos foram selecionados, … é hora de partir para micro otimizações.

Repare que o espaço para ganho real com micro otimizações está em uma parcela muito pequena do seu código (geralmente, menos de 1% da base total).

O uso de micro otimizações implica em profundo conhecimento de como o código será executado pelo computador e, em decorrência disso, grande intendimento sobre como o computador e o sistema operacional funcionam.

Micro otimizações geralmente levam a código de difícil leitura e, consequentemente, manutenção.

O esforço para implementar micro otimizações tem custo bem alto. Os ganhos são percebidos e justificam os custos apenas em raras situações.

Considerações finais

Se você está com problemas de performance em sua aplicação, a recomendação é seguir o seguinte caminho:

  1. Revise a arquitetura de sua aplicação garantindo que os componentes estão coesos
  2. Respeite a rede! Tudo que exigir um request representa custo elevado.
  3. Respeite o disco! Tudo que exigir operação de escrita ou leitura tem custo elevado.
  4. Respeite as características de sua linguagem/framework! Garanta que não está, por exemplo, criando mais strings que o necessário.
  5. Revise a seleção de algoritmos e, principalmente, as estruturas de dados que está adotando.
  6. Em casos extremos (apenas em casos extremos), busque oportunidades de micro otimização.

No mais, conte comigo! Posso ajudar você e sua empresa a resolver problemas de performance.

Capa: Jason Chen

Deixe uma resposta

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