Onde você otimiza?


9

Existem duas áreas para otimizar a velocidade:

  • Onde é gasto mais tempo
  • O código que é chamado o mais

Qual é o melhor lugar para começar a otimizar?

Muitas vezes, o código chamado com mais frequência já tem tempos de execução baixos. Você otimiza as áreas mais lentas e menos chamadas ou gasta tempo otimizando as áreas mais rápidas e mais usadas?


Otimize a área do aplicativo que mais enfatiza seus clientes ou sua arquitetura, independentemente de seus clientes ou servidores estarem reclamando mais alto.
22411 Andy Andy

É uma equação de valor - a resposta também pode ser. Quando você não tem uma análise real, segue seu instinto, com base no provável retorno das suas melhores idéias.
22411 Nicole

Nem. Procure o código que está na pilha uma grande fração do tempo.
precisa saber é o seguinte

Respostas:


4

Você deve ignorar as pequenas eficiências em 95% das vezes. Primeiro, faça com que funcione corretamente e depois analise ...

Seu design.

Sua escolha de algoritmos de alto nível pode ter um enorme impacto no desempenho geral do seu software, a ponto de uma escolha aparentemente trivial poder significar a diferença entre esperar 20 minutos para o programa iniciar e ter uma interface de usuário rápida e responsiva.

Por exemplo, em um jogo 3D: se você começar com uma lista simples e simples de objetos para o gráfico de cenas, verá um desempenho extremamente ruim para um número relativamente pequeno de objetos; mas se você implementar uma hierarquia de volumes (como uma octree ou BVH) e selecionar partes da árvore enquanto desenha, verá um enorme aumento de desempenho.

Quando o seu design parecer correto, você poderá ...

Lógica de baixo nível.

Algoritmos de nível inferior também podem ter um impacto significativo. Ao fazer o processamento de imagens, por exemplo, se você ler a imagem na ordem errada, ocorrerá lentidão enorme ao encontrar falhas constantes no cache L2; reordenar suas operações pode significar um aumento de dez vezes no desempenho.

Nesse momento, identifique e localize o local em que a maior parte do tempo é gasto e encontre uma maneira de eliminá-lo.


O programa em que estou trabalhando está correto. Queremos torná-lo mais rápido, se pudermos, pois é um serviço da Web que pode levar de 30 a 1 minuto para ser executado.
Michael K

11
@ Michael: Nesse caso, é hora de obter uma ferramenta de perfil para analisar algumas execuções típicas de programas e identificar as seções de código que estão sendo executadas mais lentamente. Eu realmente recomendo usar uma ferramenta para isso. Você pode fazer uma certa quantia por intuição, mas às vezes você encontrará uma surpresa quando uma API que chama leva um tempo significativo, em vez de seu próprio código. Nesse caso, é hora de examinar a API e ver quais outras funções estão disponíveis, ou se você deve escrever sua própria função de substituição ... O nível de desempenho real nem sempre é o nível de desempenho suspeito ...
FrustratedWithFormsDesigner

11
Nós temos uma ferramenta de criação de perfil. Ainda estou aprendendo e o que fazer com as informações. É um assunto relativamente novo para mim e muito interessante.
Michael K

@ Michael: Bom! Você está (espero) no seu caminho para o ajuste de desempenho bem-sucedido! :)
FrustratedWithFormsDesigner

+1: Era o que eu ia dizer, mas com muito mais eloquência.
Dominique McDonnell

3

Primeiro, execute um criador de perfil para descobrir onde seu código está gastando seu tempo.

Em seguida, observe esses lugares para ver quais parecem fáceis de otimizar.

Procure as correções mais fáceis que conseguirão os maiores ganhos primeiro (escolha a fruta mais baixa). Não se preocupe muito com a importância, exatamente. Se for fácil, conserte. Isso irá somar. 25 correções fáceis podem ser mais rápidas que uma grande correção e seus efeitos cumulativos podem ser maiores. Se for difícil, anote ou envie um relatório de bug para que você possa priorizá-lo mais tarde. Não se preocupe tanto com "grande" ou "pequeno" neste momento - basta fazê-lo até chegar a funções que estão usando muito pouco tempo. Depois de fazer isso, você deve ter uma idéia melhor de quais dos outros problemas descobertos podem obter as maiores vitórias pelo menor investimento possível.

Não se esqueça de acompanhar a criação de perfil após suas correções como uma espécie de teste de regressão, para verificar se suas alterações de desempenho tiveram os efeitos que você esperava. Além disso, não se esqueça de executar seu conjunto de regressão, para garantir que nenhuma funcionalidade foi interrompida. Às vezes, o desempenho ruim indica soluções alternativas, e tentar consertar o desempenho prejudica a funcionalidade.

Pequenas funções que não podem ser otimizadas, mas estão usando muito tempo, ainda podem ser dicas sobre onde otimizar. Por que essa função está sendo chamada tanto? Existe uma função que chama essa pequena função que não precisa usá-la tanto? O trabalho está sendo duplicado ou está sendo realizado um trabalho desnecessário? Procure na pilha os horários em que ela é chamada até ter certeza de que deve ser chamada com frequência e veja se encontra uma função maior com um algoritmo ineficiente.

Editado para adicionar: como você tem uma funcionalidade específica que está demorando muito tempo, tente executar as etapas acima com apenas essa função específica sendo executada 10 ou mais vezes.


2

É difícil dizer. Isso realmente depende do que o código está fazendo. Execute um teste de desempenho, obter um perfil de desempenho, e olhar e ver quanto real tempo é gasto em diversas áreas. Suas generalizações são ... generalizações e isso varia de projeto para projeto.

Por exemplo, o código mais chamado pode simplesmente registrar-se em um arquivo ou console. Não há muito sentido em otimizar que, se já são uma ou duas linhas de código que não podem ser simplificadas, e pode ser que qualquer esforço para otimizar algo assim possa não valer o custo de realmente codificá-lo. O código menos chamado pode ser uma consulta do tamanho de um monstro usada em alguma função terrivelmente complexa. A função pode ser chamada apenas 100 vezes durante uma execução inteira (vs. 10000 para a instrução de log simples), mas se levar 20 segundos para cada tempo de chamada executado, talvez sejaque a otimização deve começar? Ou pode ser o contrário, com a grande consulta sendo a mais chamada e a instrução de log apenas uma para cada 100 consultas ...

Normalmente, não me preocupo com esse tipo de coisa (até precisar fazer o ajuste de desempenho), a menos que tenha alguma ideia antecipada do que vai acontecer.


1

Bem, "nós" geralmente não otimizamos até que exista uma necessidade óbvia de otimização quando algo é inaceitavelmente lento.

E quando essa necessidade se manifesta, geralmente traz boas sugestões sobre o que exatamente exige otimização.

Portanto, a resposta é usual: "Depende".


1

Você deve usar um criador de perfil em algumas execuções típicas e analisar o tempo total gasto em cada parte do código, não importa quantas vezes você chegou lá. A otimização dessas peças sempre deve aumentar a velocidade.

Dependendo do nível de linguagem da sua implementação, você também deve descobrir quais partes causam a maioria das falhas de cache. A consolidação do código de chamada ajudará aqui.


1

O problema é que a frase "onde mais tempo é gasto" é ambígua.

Se isso significa "onde o contador de programas é encontrado com mais frequência", eu já vi programas nos quais passava mais tempo nas funções de comparação matemática, alocação de memória e biblioteca de matemática. Em outras palavras, funções que o programador comum nunca deve tocar.

Se significa "onde no código do programador são executadas instruções que consomem uma grande fração de tempo", esse é um conceito mais útil.

O problema com o conceito de "código que é chamado mais" é que a quantidade de tempo que leva é o produto de quantas vezes ele é chamado e quanto tempo leva por chamada (incluindo chamadas e E / S). Como a quantidade de tempo que leva pode variar em várias ordens de magnitude, o número de vezes que é chamado não informa qual é o problema. A função A pode ser chamada 10 vezes e leva 0,1 segundo, enquanto a função B pode ser chamada 1000 vezes e leva um microssegundo.

Uma coisa que vai lhe dizer onde procurar é o seguinte: Sempre que uma linha de código está causando tempo a ser gasto é na pilha . Portanto, por exemplo, se uma linha de código é um hot spot, ou se é uma chamada para uma função de biblioteca, ou se é a 20ª chamada em uma árvore de chamadas de 30 níveis, se é responsável por 20% do tempo , então ele está na pilha 20% do tempo. Amostras de tempo aleatório da pilha terão 20% de chance de exibi-la. Além disso, se as amostras puderem ser coletadas durante a E / S, elas mostrarão o que é responsável pela E / S, que pode ser tão ou mais inútil quanto os ciclos de CPU desperdiçados.

E isso é totalmente independente de quantas vezes é invocado.


Por 'programador cotidiano nunca deve tocar', você quer dizer que provavelmente não tocará? Além disso, a amostragem da pilha é um método prático de criação de perfil?
Michael K

@ Michael: Sim, a amostragem da pilha é um método no qual os perfis modernos se baseiam, como o Zoom . Além disso, totalmente manual funciona surpreendentemente bem .
Mike Dunlavey 22/02

Muito interessante. Eu tenho alguns estudos para fazer agora!
Michael K

@ Michael: É como o conceito legal de responsabilidade conjunta. Em um determinado momento, a responsabilidade pelo PC estar em uma instrução é responsabilidade conjunta não apenas dessa instrução, mas de todas as chamadas acima na pilha. Eliminar qualquer um deles impediria a entrada nesse estado específico.
Mike Dunlavey 22/02

0

Otimize onde é gasto mais tempo, a menos que haja um motivo específico para não fazê-lo (ou seja, a maior parte do tempo é gasta no processamento assíncrono que os humanos realmente não se importam se termina em 5 ou 10 minutos). Naturalmente, o código chamado mais tenderá a acumular uma parte relativamente grande do tempo total decorrido, simplesmente porque até curtos tempos de execução aumentam quando você faz isso milhares de vezes.


0

Você precisa trabalhar no código que está demorando mais tempo. Melhorar o código que representa apenas alguns por cento do tempo de execução pode oferecer apenas uma pequena melhoria.

Você fez medições para saber qual código está demorando mais tempo?


0

Eu costumava fazer benchmarking e marketing para um fornecedor de supercomputadores, então vencer a concorrência rapidamente não era a coisa mais importante, era a ÚNICA coisa. Esse tipo de resultado exigia que você usasse uma combinação de bom algorthm e uma estrutura de dados que permitisse que as porções mais intensivas da CPU executassem um pico com eficiência. Isso significava que era necessário ter uma boa idéia de quais seriam as operações com maior intensidade computacional e que tipos de estruturas de dados permitiriam que eles rodassem mais rapidamente. Depois, era uma questão de criar o aplicativo em torno desses kernels / estruturas de dados otimizados.

No sentido mais geral, o típico após o ajuste do fato. Você cria um perfil, analisa os pontos de acesso e os pontos que você acha que pode acelerar são aqueles em que trabalha. Mas raramente essa abordagem fornece algo próximo da implementação mais rápida possível.

Porém, de maneira mais geral (não obstante os erros algorítmicos), para máquinas modernas, você deve estar pensando que o desempenho é determinado por três coisas: acesso a dados, acesso a dados e acesso a dados! Aprenda sobre a hierarquia de memória (registros, caches, TLBs, páginas etc.) e projete suas estruturas de dados para fazer o melhor uso possível delas. Geralmente, isso significa que você deseja que os loops sejam executados em um espaço de memória compacto. Se você simplesmente escrever (ou receber) um aplicativo e tentar otimizá-lo, geralmente ficará sobrecarregado com estruturas de dados que fazem mau uso da hierarquia de memória, e alterar as estruturas de dados geralmente envolve um grande exercício de refatoração, então você está frequentemente preso.


0

Se você deseja obter um retorno do seu esforço de otimização, verifique o código que leva mais tempo. Meu objetivo geral é algo que leva pelo menos 80% do tempo. Eu geralmente viso um ganho de desempenho 10 vezes. Às vezes, isso requer uma grande mudança na maneira como esse código é projetado. Esse tipo de mudança oferece algo que é executado aproximadamente quatro vezes mais rápido.

Meu melhor desempenho de todos os tempos é reduzir o tempo de execução de 3 dias para 9 minutos. O código que otimizei passou de 3 dias para 3 minutos. O aplicativo que substituiu o aplicativo reduziu para 9 segundos, mas isso exigiu uma alteração no idioma e uma reescrita completa.

Otimizar um aplicativo já rápido pode ser uma tarefa tola. Eu ainda segmentaria a área levando mais tempo. Se você puder usar algo usando 10% do tempo para retornar instantaneamente, ainda precisará de 90% do tempo. Você rapidamente atinge a regra dos retornos decrescentes.

Dependendo do que você está otimizando para a regra, ainda se aplica. Procure os principais usuários de recursos e otimize-os. Se o recurso que você está otimizando for o gargalo do sistema, você poderá descobrir que tudo o que faz é alterar o gargalo para outro recurso.


0

Normalmente, na maioria dos casos, serão funções mais elaboradas, e não as funções chamadas mais frequentemente um bilhão de vezes em um loop.

Quando você cria um perfil com base em amostra (com uma ferramenta ou manualmente), geralmente os maiores pontos ativos estão em pequenas chamadas frondosas que fazem coisas simples, como uma função para comparar dois números inteiros.

Essa função geralmente não se beneficia de muita, se houver, otimização. No mínimo, esses pontos de acesso granulares raramente são prioridade máxima. É a função que chama a função leaf que pode ser a criadora de problemas, ou a função que chama a função que chama a função, como um algoritmo de classificação abaixo do ideal. Com boas ferramentas, você pode detalhar do chamado ao chamador e também ver quem passa mais tempo chamando o chamador.

Muitas vezes, é um erro ficar obcecado com as chamadas e não olhar para os chamadores no gráfico de chamadas em uma sessão de criação de perfil, a menos que você esteja fazendo as coisas de maneira muito ineficiente no nível micro. Caso contrário, você pode estar suando demais as coisas pequenas e perdendo de vista o quadro geral. Ter um profiler na mão não o impede de ficar obcecado com coisas triviais. É apenas um primeiro passo na direção certa.

Além disso, você deve certificar-se de realizar operações de criação de perfil alinhadas às coisas que os usuários realmente desejam fazer, caso contrário, ser totalmente disciplinado e científico em suas medições e parâmetros de referência é inútil, pois não está alinhado com o que os clientes fazem com o produto. Certa vez, tive um colega que desviou um algoritmo de subdivisão para subdividir um cubo em um bilhão de facetas e ele se orgulhava disso ... exceto que os usuários não subdividem cubos simples de 6 polígonos em um bilhão facetas. A coisa toda diminuiu para um rastreamento quando tentou rodar em um modelo de carro de produção com mais de 100.000 polígonos para subdividir; nesse ponto, não era possível nem fazer 2 ou 3 níveis de subdivisão sem diminuir o ritmo. Simplificando, ele escreveu um código super otimizado para tamanhos de entrada irrealisticamente pequenos que não

Você precisa otimizar os casos de uso reais alinhados aos interesses dos usuários, ou isso é pior do que inútil, pois todas as otimizações que tendem a degradar a manutenção do código têm um pouco de benefício para o usuário e apenas todos os negativos para a base de código.

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.