Otimização de sistemas

Adriano Croco
8 min readJul 11, 2023

--

Olá!

No texto de hoje, abordarei o tema da otimização, explorando diferentes problemas dentro da área de tecnologia. Espero fornecer informações úteis para evitar o desperdício de energia em soluções inutéis.

Vamos começar com alguns conceitos que não estão relacionados à computação.

Lei de Potência

Eu gostaria de iniciar a abordagem deste assunto sob a perspectiva de leis de potência (power laws), que tem a seguinte definição:

Uma lei de potência é uma relação matemática que descreve como alguns eventos raros ou extremos ocorrem com frequência menor, mas têm um impacto significativo. É como se houvesse uma distribuição onde a maioria das coisas é pequena ou comuns, mas algumas coisas são muito grandes ou raras.

Basicamente, uma distribuição comum em estatística tem o formato representado à esquerda na imagem abaixo. Já uma lei de potência tem uma distribuição semelhante ao gráfico da direita, resultando em efeitos observáveis, como um comportamento de cauda longa:

Distribuição normal vs distribuição via lei de potência

A lei de potência mais famosa (que é a que iremos nos basear neste texto), é o príncipio de Pareto, que possui a seguinte definição:

O princípio de Pareto, também conhecido como regra 80/20, diz que cerca de 80% dos resultados vêm de 20% das causas. Isso significa que a maioria dos efeitos é resultado de um número reduzido de causas principais. Em outras palavras, uma minoria de esforços ou recursos geralmente gera a maior parte dos resultados.

A partir dessa perspectiva de poucas causas gerando muitos efeitos, podemos começar a analisar os problemas e discutir formas mais eficientes de resolvê-los.

Sobre heróis

A aplicação do princípio de Pareto na carga de trabalho se resume a 20% das pessoas realizando 80% do trabalho. Isso é popularmente conhecido como a cultura do herói. A carga de trabalho é tão desequilibrada que apenas alguns são chamados para resolver os problemas e fazer as coisas acontecerem, enquanto muitos outros se escondem atrás dos processos e performam mal e não são responsabilizados pelo baixo desempenho.

Esse cenário é mais comum em culturas excessivamente burocráticas (como bancos e empresas tradicionais) e em empresas que não possuem uma gestão de pessoas adequada. Geralmente, o foco está na entrega a todo custo, sem considerar outros fatores.

A solução para esse tipo de empresa? Bem, não tem. Não existem soluções rápidas e individuais para problemas culturais. Vou explicar o motivo no próximo tópico.

Produtividade é sobre sistemas, não sobre pessoas

Após a Segunda Guerra Mundial, o engenheiro americano William Edwards Deming foi ao Japão para auxiliar na reconstrução do país. Em alguns anos, o Japão se tornou uma potência industrial, devido, em parte, aos processos produtivos notoriamente mais eficientes que o restante do mundo até então. Deming ficou conhecido como o estrangeiro que mais impactou os processos produtivos industriais do país, sendo inclusive criado o Prêmio Deming de Qualidade Total, que ainda é concedido até hoje no Japão. No Ocidente, ele é conhecido como o pai da melhoria contínua. Ou seja, praticamente todas as práticas de melhoria de processos anteriores à agilidade, que ainda são utilizadas atualmente, foram sistematizadas e aplicadas por ele nas empresas.

Em 1982, ele publicou o livro “Out of Crisis”, que faz uma análise detalhada (utilizando métodos estatísticos) de diversos processos produtivos. Para não me prolongar, irei citar uma de suas conclusões como meu argumento principal (tradução minha):

Eu estimo que na minha experiência, a maioria dos problemas e a maior quantidade de possibilidade de melhoria se dão em proporções similares a essa: 94% pertencem ao sistema (responsibilidade da gestão) e 6% se devem a fatores especiais.

Ou seja, mesmo os heróis são responsáveis apenas por 6% dos resultados potenciais de um sistema. Entenda o sistema como um conjunto de elementos que interagem entre si, como equipes em uma empresa. Se você ainda não se convenceu, vou citar outra frase do livro (também traduzida por mim):

Nenhuma habilidade ou cuidado no trabalho pode superar falhas fundamentais no sistema.

Não quero ser fatalista e dizer que o que você faz como contribuinte individual não importa. No entanto, o que você faz em nível individual — pensando em um sistema —bem… não importa tanto mesmo. Em outras palavras, essas frases resumem, com dados, o sentimento de que nossas ações não importam tanto no final.

Pense no sistema educacional. Por mais dedicados que sejam os professores ao ensinar alunos em comunidades desfavorecidas, o problema está no sistema que perpetua a desigualdade de oportunidades desde o início. O acesso limitado a recursos, infraestrutura precária e falta de suporte adequado afetam negativamente o aprendizado desses estudantes. Essa é uma questão sistêmica que requer esforços para mudar políticas educacionais, distribuição de recursos e investimentos em igualdade de oportunidades. Portanto, se você deseja promover uma mudança real, é fundamental enfrentar o inimigo correto, ou seja, o sistema educacional desigual.

E como podemos identificar as falhas de um sistema para combatê-las com eficácia?

A regra do gargalo

Essa regra a tem origem na teoria das restrições, criada pelo pesquisador israelense Dr. Eliyahu Goldratt e publicada em 1984 no livro A Meta (The Goal, em inglês). Resumidamente:

A regra do gargalo diz que o desempenho de um sistema é limitado pelo seu ponto mais fraco ou restrito, chamado de gargalo. Isso significa que, para melhorar o sistema como um todo, é necessário focar em melhorar ou resolver esse gargalo específico. Ao resolver o gargalo, é possível aumentar a eficiência e o rendimento do sistema.

Em outras palavras, se você deseja de fato melhorar a produtividade, throughput de uma equipe ágil ou qualquer outra métrica que avalie os resultados de um sistema, concentre-se na eliminação dos gargalos. E não se surpreenda se, ao resolver um gargalo, outro surgir logo em seguida. Esse é o comportamento esperado; caso contrário, a regra do gargalo não teria sentido, concorda? (:

Achando os gargalos de forma efetiva

O que acontece se tentarmos aplicar o princípio de Pareto a si mesmo? Ou seja, encontrar os 20% dos 20% e os 80% dos 80%, e assim por diante? Ao aplicar essa ideia recursiva aos números, é possível obter resultados interessantes, conforme demonstrado a seguir. Considere o número 100 como o sistema como um todo:

100*(0,2)¹ gera 100*(0,8)¹ = 20% das causas gera 80% dos efeitos (nível 1)
100*(0,2)² gera 100*(0,8)² = 4% das causas gera 64% dos efeitos (nível 2)
100*(0,2)³ gera 100*(0,8)³ = 0,8% das causas gera 51,2% dos efeitos (nível 3)
100*(0,2)⁴ gera 100*(0,8)⁴ = 0,16% das causas gera 40,96% dos efeitos (nível 4)
100*(0,2)⁵ gera 100*(0,8)⁵ = 0,032% das causas gera 32,76% dos efeitos (nível 5)

Substitua o número 100 pela quantidade de linhas de código de uma aplicação e você saberá por onde começar a procurar os 20% que são realmente importantes. Substitua os 20% pelas pessoas em uma avaliação de desempenho e você saberá onde estão as pessoas com melhor desempenho.

Ao projetar a arquitetura de um sistema, identifique se a leitura ou a escrita é responsável por 80% das requisições e você terá a resposta se o sistema deve ser otimizado para leitura ou escrita. 20% do conhecimento técnico em programação permite escrever 80% dos programas que vemos por aí. Aprender o restante é o desafio e é o que o distinguirá como profissional (e o que impactará sua remuneração no longo prazo).

Se forem necessárias análises mais detalhadas, você pode usar os outros níveis. No entanto, geralmente, no nível 3 (0,8% das causas geram 51,2% dos efeitos), já estamos falando de micro-otimizações, que são necessárias apenas em casos muito específicos. Em sistemas que são basicamente CRUD, provavelmente não é necessário.

Vamos trazer um exemplo técnico hipotético. Ao pensar no resultado possível e no esforço envolvido em otimizar um sistema em termos de desempenho, o que é mais efetivo?

  • Atuar na camada de arquitetura e como os componentes interagem entre si, alternando um processo sync para async, por exemplo (nível 1);
  • Entrar na camada de engenharia e alterar os algoritmos usados e algumas estruturas de dados, mexendo em tabelas e estruturas similares (nível 2);
  • Ignorar todas as camadas de segurança da linguagem e manipular memória diretamente em caminhos críticos (nível 3);
  • Usar assembly (nível 4);
  • Escrever uma nova arquitetura de processador exclusivamente para o problema (nível 5);
Acho que deixei o meu ponto claro

Na maioria dos sistemas, o primeiro gargalo que surge é no banco de dados, seguido pela rede ou disco e em seguida, pelos algoritmos e pela linguagem. Embora eu considere Python e PHP mais lentas em comparação com outras linguagens populares, é possível obter resultados satisfatórios com essas tecnologias por meio de uma arquitetura sólida, um banco de dados dimensionado corretamente e o uso adequado de filas.

Para fornecer mais referências, segue a imagem abaixo, que representa bem as diferentes ordens de magnitude de desempenho:

Comparativo entre latência entre os diversos componentes

Portanto, memorize a seguinte ordem: CPU é mais rápido que Memória que é mais rápido que Disco que é mais rápido que a Rede. O desempenho de um sistema depende de como cada recurso é utilizado. Ou seja, ao pensar em otimização, imagine que o fluxo deve seguir corretamente a camada em que ocorre o processamento. Tente sempre otimizar pensando em utilizar o recurso mais rápido ao invés do atual.

Por exemplo, se você está buscando dados em um parceiro externo via rede, tente trazer os dados para o disco local. Se estiver acessando frequentemente o disco para buscar dados, utilize cache. Se estiver alocando muitos dados na memória, utilize estruturas de dados mais simples. Geralmente, essas simples mudanças no uso dos recursos já proporcionam melhorias significativas (estou falando de melhorias de desempenho de 100 a 1000 vezes com alterações simples).

Essencialmente, é isso que você precisa se preocupar no final das contas.

Para concluir, a frase que melhor resume os pontos discutidos é a do lendário Donald Knuth:

“O problema real é que os programadores têm se preocupado demais com eficiência nos lugares e momentos errados; a otimização prematura é a raiz de todo mal (ou pelo menos da maior parte) na programação.”

o xkcd sabe das coisas

Eu gostaria de acrescentar meu ponto de vista: a proporção 80/20 do princípio de Pareto é uma aproximação, também é possível ter proporções como 70/30 ou até mesmo 90/10. Curiosamente, existe outra versão dessa mesma frase que reforça essa ideia:

“A gente deveria esquecer das pequenas eficiências, cerca de 97% do tempo a otimização prematura é a raiz de todo mal.”

Um bom sinal de conhecimento sólido é quando várias fontes consolidadas de diversas áreas diferentes conseguem ter uma conversa coerente entre si. Essa é a beleza da multidisciplinaridade, afinal.

Até a próxima!

Você gostou do conteúdo e gostaria de fazer mentoria comigo? Clique aqui e descubra como.

--

--