Como medir a complexidade na prática em seu grande projeto de software?


11

Na universidade, em nossos cursos de algoritmos, aprendemos a calcular com precisão a complexidade de vários algoritmos simples usados ​​na prática, como tabelas de hash ou classificação rápida.

Mas agora em um grande projeto de software, quando queremos torná-lo mais rápido, tudo o que fazemos é olhar para partes individuais - alguns loops aninhados que podem ser substituídos por uma tabela de hash mais rápida, uma pesquisa lenta aqui que pode ser acelerada por uma técnica mais sofisticada - mas nunca computamos a complexidade de todo o nosso pipeline.

Existe alguma maneira de fazer isso? Ou as pessoas na prática estão apenas confiando "localmente" usando um algoritmo rápido, para tornar o aplicativo inteiro mais rápido, em vez de considerar globalmente o aplicativo como um todo?

(Como me parece não trivial mostrar que, se você acumular um grande número de algoritmos conhecidos por serem muito rápidos por conta própria, também terá um aplicativo rápido como um todo).

Estou perguntando isso, porque estou encarregado de acelerar um grande projeto que outra pessoa escreveu, onde muitos algoritmos estão interagindo e trabalhando nos dados de entrada, portanto, não está claro para mim como o impacto de tornar o algoritmo único mais rápido toda a aplicação.


1) Isso requer uma abordagem de teste, para encontrar os pontos a serem aprimorados. Teste de benchmark, teste de durabilidade, teste dinâmico (monitorando as métricas de memória / CPU de cada componente). 2) Após encontrar os pontos a serem aprimorados, você encontrará a causa raiz desses pontos. 3) Encontre uma solução para resolver a causa raiz, mantendo a correção.
overexchange 25/05

você precisa de algumas ferramentas para esses testes mencionados no ponto 1
troca

1
A análise Big O não mostra como um algoritmo será executado. Ele mostra como o desempenho será escalado à medida que naumenta.
John Wu

Respostas:


5

Os grandes projetos de software consistem em muitos componentes diferentes e nem todos são tipicamente um gargalo. Muito pelo contrário: para quase todos os programas que vi na minha vida em que o baixo desempenho era um problema, o princípio de Pareto se aplicava: mais de 80% dos ganhos de desempenho podem ser alcançados otimizando menos de 20% do código (na realidade, eu acho que os números costumam ser mais de 95% a 5%).

Portanto, começar a olhar para peças individuais geralmente é a melhor abordagem. É por isso que a criação de perfil (conforme explicado na resposta de David Arno ) é boa, pois ajuda a identificar os 5% mencionados do código em que a otimização fornecerá o "maior retorno do investimento". A otimização de "todo o aplicativo" acarreta um certo risco de engenharia excessiva e, se você otimizar esses 95% mesmo com um fator de 10, muitas vezes isso não terá efeito mensurável. Observe também que o perfil indica muito mais do que qualquer estimativa hipotética da complexidade do algoritmo, pois um algoritmo simples que precisa de etapas O (N ^ 3) ainda pode ser mais rápido que um algoritmo complexo que requer O (N log (N)), desde que N é pequeno o suficiente.

Após o perfil revelar os pontos de acesso, é possível otimizá-los. Obviamente, um "hot spot" pode ser maior do que apenas uma ou duas linhas de código; às vezes é preciso substituir um componente inteiro para torná-lo mais rápido, mas isso normalmente ainda é uma pequena parte da base de código em um programa maior .

Técnicas de otimização típicas incluem

  • melhorando o uso de algoritmos e estruturas de dados

  • ajuste fino do antigo

  • micro-otimizações em alguns pontos de acesso reais

  • recodificando seções críticas usando código de montagem ou CUDA

Observe que essas técnicas estão trabalhando em diferentes níveis de abstração, algumas delas visualizando um componente mais "como um todo" do que outras. Portanto, depende do que você quer dizer com "tudo o que fazemos é olhar para peças individuais" - se você tivesse apenas micro-otimizações em mente, discordo que "nós" trabalhamos apenas nisso. Mas se você quer aplicar essas otimizações em larga escala em peças ou componentes isolados, então "nós" provavelmente estamos trabalhando nas peças certas e você deve questionar suas expectativas.


13

A maneira padrão, testada e comprovada, é criar um perfil do código . Você realiza análises dinâmicas do sistema em execução para medir tempos, uso de memória etc. Em seguida, analisa os resultados para encontrar gargalos de desempenho.

Esses gargalos são reescritos experimentalmente e o resultado é analisado novamente para determinar que foi alcançado um aumento de velocidade, redução no uso de memória, etc. Esse processo é repetido até que um ganho de desempenho aceitável seja alcançado.


1
Isso resolve o problema de desempenho, e é por isso que o fazemos, mas não responde à pergunta original. Penso que, no pior dos casos, a complexidade do tempo ou do espaço seria melhor compreendida usando uma ferramenta estática de análise de programa, que pode estar faltando. Os testes de desempenho são ótimos para cenários específicos, mas não informam muito sobre as piores situações possíveis.
Frank Hileman

3
@FrankHileman Acho que o ponto aqui é que o desempenho é uma preocupação prática e só pode ser medido na prática. Você não usa matemática para encontrar o gargalo do seu software, mesmo que possa resolvê-lo uma vez encontrado usando matemática (algoritmos).
Curinga

Em uma nota relacionada, na apresentação de slides antiga (slides de vidro), havia toda uma tecnologia pretendida de como calcular meticulosamente a densidade média de um slide de lanterna matematicamente para determinar o brilho da luz a ser usado. Completamente inútil: se a imagem não aparecer bem, você terá uma luz mais brilhante!
Curinga

@Wildcard Embora o desempenho possa ser medido apenas em tempo de execução, ele pode ser previsto estaticamente. Uma má escolha de uma estrutura de dados pode parecer boa, em termos de desempenho, em testes de desempenho, mas falha miseravelmente em casos extremos que podem ser previstos em uma análise estática. Essa é a mesma razão pela qual analisamos as análises de complexidade do pior caso para estruturas de dados em geral.
Frank Hileman

@Wildcard: você está correto, no entanto, Frank também está muito correto que este post não responde à pergunta.
Doc Brown

3

Embora as outras respostas estejam corretas e forneçam alguma orientação, acho que elas perdem um passo. Em um sistema complexo com o qual você está trabalhando agora, entender os diferentes componentes que compõem o sistema é essencial para entender por que algo está lento.

Meu primeiro passo seria obter um diagrama detalhado da arquitetura ou criar um eu mesmo. Descubra quais etapas são executadas, quais componentes do software e quanto tempo cada etapa leva.

Além disso, descubra como os componentes interagem entre si. Isso pode fazer toda a diferença.

Por exemplo, eu vi código em C # em que a interface entre dois componentes estava passando um IEnumerable criado pelo primeiro componente, que depois foi enumerado pelo segundo componente. Em C #, isso requer alternância de contexto, que pode ser onerosa em determinadas circunstâncias. Resolvê-lo não tem impacto no algoritmo. Um simples .ToList () garante que o resultado seja coletado antes que a próxima etapa resolva esse problema.

Outra coisa a considerar é o impacto no sistema em que você está executando o código. As interações de hardware podem obviamente ser um fator em sistemas complexos. Procure por E / S de disco, alocações de memória grande e E / S de rede. Às vezes, eles podem ser resolvidos de maneira mais eficiente, aprimorando o sistema ou até substituindo o hardware.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.