Quando a otimização não é prematura e, portanto, não é má?

75

"Otimização prematura é a raiz de todo mal" é algo que quase todos nós ouvimos / lemos. O que estou curioso é que tipo de otimização não é prematuro, ou seja, em todas as etapas do desenvolvimento de software (design de alto nível, design detalhado, implementação de alto nível, implementação detalhada etc.) qual é o grau de otimização que podemos considerar sem que ela passe para o lado escuro.

Gaurav
fonte

Respostas:

116

Quando você está baseando-se na experiência? Não é mau. "Toda vez que fizemos o X, sofremos um impacto brutal no desempenho. Vamos planejar otimizar ou evitar o X totalmente dessa vez."

Quando é relativamente indolor? Não é mau. "Implementar isso como Foo ou Bar exigirá muito trabalho, mas, em teoria, o Bar deve ser muito mais eficiente. Vamos barrar".

Quando você está evitando algoritmos de baixa qualidade, que escalam terrivelmente? Não é mau. "Nosso líder técnico diz que nosso algoritmo de seleção de caminho proposto é executado em tempo fatorial; não tenho certeza do que isso significa, mas ela sugere que cometamos seppuku por considerá-lo. Vamos considerar outra coisa."

O mal vem de gastar muito tempo e energia resolvendo problemas que você não sabe que realmente existem. Quando os problemas existem definitivamente, ou quando os psudo-problemas fantasmas podem ser resolvidos de maneira barata, o mal desaparece.


Steve314 e Matthieu M. levantam pontos nos comentários que devem ser considerados. Basicamente, algumas variedades de otimizações "indolores" simplesmente não valem a pena porque a atualização trivial de desempenho que eles oferecem não vale a ofuscação do código, estão duplicando aprimoramentos que o compilador já está executando, ou ambos. Veja os comentários para alguns bons exemplos de não-melhorias muito inteligentes pela metade.

BlairHippo
fonte
22
Ocasionalmente, resolver um problema fantasma com facilidade ainda é levemente ruim, pois pode resultar em leitura mais difícil e mais difícil de manter o código. Não é muito mais difícil (ou não seria uma solução fácil), mas talvez ocasionalmente ainda seja relevante. Um exemplo pode estar usando um truque inteligente que algumas pessoas não reconhecem e que o compilador provavelmente aplicará de qualquer maneira, se for útil.
Steve314
26
Eu concordo com Steve aqui, às vezes a "otimização" simplesmente não vale a pena, especialmente porque os compiladores são muito bons. Exemplo? se inão tiver sinal, i / 2pode ser substituído por i >> 1. É mais rápido. Mas também é mais enigmático (nem todos verão o efeito, mesmo aqueles que o fazem podem perder tempo). Mas o pior de tudo é que o compilador fará isso de qualquer maneira, então por que ofuscar o código-fonte;)?
Matthieu M.
19
@ Larry: Eu não fiz, então acho que é um bom exemplo.
Joris Meys 01/01
18
Na minha opinião, otimizações, mesmo as simples, também devem ser consideradas más se impactarem a legibilidade / manutenção do código e não forem baseadas em medições de desempenho reais.
Bart van Ingen Schenau
14
@ Matthew: Ensine a eles o que? Truques sujos e desnecessários? Por quê? Se a criação de perfil mostra que a i/2é realmente um ponto quente e que (inacreditável, mas vamos supor) o i>>1torna mais rápido, faça-o e faça um comentário de que esse perfil mostrou que é mais rápido. Se isso é realmente necessário em qualquer lugar (o que eu duvido, já que, como Matthieu disse, os compiladores devem ser inteligentes o suficiente para fazer isso), os iniciantes aprenderão algo, se não for (o que é provável), por que você deseja conectar suas cabeças com folclore desnecessário?
SBI
38

O código do aplicativo deve ser tão bom quanto necessário, mas o código da biblioteca deve ser o melhor possível, pois você nunca sabe como sua biblioteca será usada. Portanto, quando você estiver escrevendo código de biblioteca, ele precisa ser bom em todos os aspectos, seja desempenho, robustez ou qualquer outra categoria.

Além disso, você precisa pensar em desempenho ao projetar seu aplicativo e ao escolher algoritmos . Se não foi projetado para ter desempenho, nenhum grau de hackeamento pode ter desempenho posterior e nenhuma micro otimização superará um algoritmo superior.

sbi
fonte
5
O código da biblioteca deve documentar se está tentando ser "o melhor possível" ou qual é seu objetivo. O código não precisa ser absolutamente ideal para ser útil, desde que os consumidores o usem somente quando apropriado.
Supercat
1
Desculpe, mas "seja bom em todos os aspectos" parece suspeito como excesso de engenharia. Além disso, provavelmente não é realista - a vida é sempre sobre trocas.
sleske
1
+1 por enfatizar a fase de projeto; se você estiver avaliando deliberadamente seus benefícios, não é prematuro.
Nathan Tuggy
Por outro lado, se você nunca sabe como sua biblioteca será usada, não sabe se gastar algum tempo em aprimorá-la tem algum valor comercial. Então isso dificilmente é um argumento.
RemcoGerlich
25

que tipo de otimização não é prematuro

O tipo resultante de problemas conhecidos.

George Marian
fonte
17

Quando a otimização não é prematura e, portanto, não é má?

É difícil dizer o que é bom e mau. Quem tem esse direito? Se olharmos para a natureza, parece que estamos programados para a sobrevivência com uma ampla definição de "sobrevivência", que inclui a transmissão de nossos genes para a prole.

Então eu diria, pelo menos de acordo com nossas funções e programação básicas, que a otimização não é má quando está alinhada com o objetivo da reprodução. Para os caras, existem as loiras, morenas, ruivas, muitas adoráveis. Para as meninas, existem homens, e alguns deles parecem estar bem.

Talvez devêssemos otimizar esse objetivo, e aí ajuda o uso de um criador de perfil. O criador de perfil permitirá que você priorize suas otimizações e tempo com mais eficiência, além de fornecer informações detalhadas sobre pontos de acesso e por que eles ocorrem. Isso lhe dará mais tempo livre gasto para a reprodução e sua busca.


fonte
3
É refrescante ver alguém trazer uma nova visão para essa velha castanha. Basta ler a citação completa de Knuth, e não apenas uma frase, não é?
Robert Harvey
1
@RobertHarvey Eu tenho um pouco de irritação lá - já que muitos parecem citar apenas uma frase e tantas informações contextuais importantes acabam sendo perdidas no processo. Não tenho certeza de que seja uma ótima resposta, já que tenho um pouco de garantia. :-D
14

A cotação completa define quando a otimização não é prematura:

Um bom programador não será levado à complacência por esse raciocínio; será prudente olhar atentamente para o código crítico; mas somente após esse código ter sido identificado . [ênfase minha]

Você pode identificar código crítico de várias maneiras: estruturas ou algoritmos de dados críticos (por exemplo, usados ​​intensamente ou o "núcleo" do projeto) podem oferecer grandes otimizações, muitas otimizações menores são identificadas por meio de criadores de perfil e assim por diante.

Fred Nurk
fonte
6
É ... é bom economizar 90% do tempo que uma chamada de função aleatória leva, mas talvez você tenha um impacto maior observando o código em que seu aplicativo gasta 80% do tempo e economizando um tempo. alguns por cento lá.
11

Você sempre deve escolher uma solução "suficientemente boa" em todos os casos, com base em suas experiências.

O ditado de otimização refere-se à escrita "código mais complexo do que 'bom o suficiente' para torná-lo mais rápido" antes de realmente saber que é necessário, tornando o código mais complexo do que o necessário. Complexidade é o que dificulta as coisas, de modo que não é uma coisa boa.

Isso significa que você não deve escolher uma rotina de classificação super complexa "pode ​​classificar arquivos de 100 Gb trocando de forma transparente para o disco" quando uma classificação simples ocorrer, mas você também deve fazer uma boa escolha para a classificação simples. Escolhendo cegamente a Classificação por bolha ou "escolha todas as entradas aleatoriamente e veja se estão em ordem. Repita". raramente é bom.


fonte
3

Minha regra geral: se você não tiver certeza de que precisará da otimização, presuma que não. Mas lembre-se de quando você precisa otimizar. Existem alguns problemas que você pode conhecer antecipadamente. Isso geralmente envolve a escolha de bons algoritmos e estruturas de dados. Por exemplo, se você precisar verificar a associação em uma coleção, pode ter certeza de que precisará de algum tipo de estrutura de dados definida.

Jason Baker
fonte
3

Na minha experiência, na fase de implementação detalhada, a resposta está na criação de perfil do código. É importante saber o que precisa ser mais rápido e o que é aceitavelmente rápido.

Também é importante saber onde está exatamente o gargalo de desempenho - otimizar uma parte do código que leva apenas 5% do tempo total para executar não é bom.

As etapas 2 e 3 descrevem a otimização não prematura:

  1. Faça funcionar
  2. Teste. Não é rápido o suficiente? Crie um perfil .
  3. Usando os dados da etapa 2, otimize as seções mais lentas do código.
Gorgi Kosev
fonte
Você esqueceu a etapa 0, que é: arquitetar o aplicativo corretamente para que você possa esperar um desempenho razoável desde o início.
Robert Harvey
Eu estava falando apenas sobre a fase de implementação detalhada.
Gorgi Kosev
1
Eu questiono a etapa 3 - muitas vezes a melhor resposta é descobrir uma abordagem diferente, para que você não faça o código lento em primeiro lugar.
Loren Pechtel
1
Escolha as estruturas de dados corretas.
jasonk
3

Não é otimização ao escolher coisas difíceis de mudar, por exemplo: plataforma de hardware.

A escolha de estruturas de dados é um bom exemplo - essencial para atender aos requisitos funcionais e não funcionais (desempenho). Não é facilmente alterado e, no entanto, direcionará todo o resto do seu aplicativo. Suas estruturas de dados alteram quais algoritmos estão disponíveis etc.

jasonk
fonte
3

Eu só conheço uma maneira de responder a essa pergunta, e é obter experiência no ajuste de desempenho. Isso significa - escreva programas e, depois de escritos, encontre acelerações neles e faça-o iterativamente. Aqui está um exemplo.

Aqui está o erro que a maioria das pessoas comete: Eles tentam otimizar o programa antes de executá-lo. Se eles fizeram um curso de programação (de um professor que na verdade não tem muita experiência prática), eles terão grandes óculos de cor O e pensarão que é disso que se trata . É tudo o mesmo problema, otimização prévia. **

Alguém disse: Primeiro, faça certo, depois rápido. Eles estavam certos.

Mas agora, para o kicker: se você já fez isso algumas vezes, reconhece as coisas tolas que fez anteriormente que causam problemas de velocidade, e evita-as instintivamente. (Coisas como tornar sua estrutura de classe muito pesada, sobrecarregar-se de notificações, tamanho confuso de chamadas de função com seu custo de tempo, a lista continua indefinidamente ...) Você as evita instintivamente, mas adivinhe o que parece ser para os menos- experiente: otimização prematura!

Então esses debates bobos continuam :)

** Outra coisa que eles dizem é que você não precisa mais se preocupar com isso, porque os compiladores são muito bons e as máquinas são tão rápidas hoje em dia. (KIWI - Kill It With Iron.) Não há acelerações exponenciais de hardware ou sistema (feitas por engenheiros muito inteligentes) que podem compensar lentidão exponencial de software (feita por programadores que pensam assim).

Mike Dunlavey
fonte
2

Quando os requisitos ou o mercado solicitam especificamente.

Por exemplo, o desempenho é um requisito na maioria dos aplicativos financeiros, porque a baixa latência é crucial. Dependendo da natureza do instrumento comercializado, a otimização pode passar do uso de algoritmos sem travamento em uma linguagem de alto nível até o uso de uma linguagem de baixo nível e até o extremo - implementar os algoritmos de correspondência de ordem no próprio hardware (usando FPGA por exemplo )

Outro exemplo seria alguns tipos de dispositivos incorporados. Tomemos, por exemplo, o freio ABS; Em primeiro lugar, há a segurança; quando você bate no freio, o carro deve desacelerar. Mas também há desempenho, você não gostaria de atrasos quando acertar o intervalo.

m3th0dman
fonte
0

A maioria das pessoas chamaria a otimização prematura, se você estiver otimizando algo que não está resultando em uma "falha leve" (funciona, mas ainda é inútil) do sistema devido ao desempenho.

Exemplos do mundo real.

  • Se minha classificação de bolha demorar 20ms para ser executada, otimizá-la para 1ms quicksort não melhorará o utilitário geral de maneira significativa, apesar de haver um aumento de desempenho de 2000%.

  • Se uma página da web leva 20s para carregar e a diminuímos para 1s, isso pode aumentar o utilitário do site de 0 para o infinito próximo. Basicamente, algo que foi quebrado por ser muito lento agora é útil.

Eric
fonte
É importante observar que, se sua classificação for chamada 1000 vezes ao longo do programa, otimizar de 20ms a 1ms é um grande problema.
Beefster
0

Que tipo de otimização não é prematura?

Uma otimização que corrige um problema de desempenho conhecido com seu aplicativo ou uma otimização que permite que seu aplicativo atenda a critérios de aceitação bem definidos.

Tendo sido identificado, deve-se levar algum tempo para estabelecer a correção e o benefício de desempenho deve ser medido.

(ou seja, não é - "Eu acho que esse pedaço do código pode parecer lento, mudarei o X para usar Y e será mais rápido").

Eu encontrei muitas "otimizações" prematuras que acabaram tornando o código mais lento - neste caso, estou considerando prematuro significar 'não pensado'. O desempenho deve ser comparado antes e depois da otimização e apenas o código que realmente melhora o desempenho mantido.

Paddy
fonte
0

"Otimização prematura é a raiz de todo mal" é algo que quase todos nós ouvimos / lemos

Verdadeiro. Infelizmente, é também uma das citações de programação mais mal utilizadas (maliciosamente) de todos os tempos. Desde que Donald Knuth cunhou o meme, vale a pena adicionar algum contexto original da citação:

Devemos esquecer pequenas eficiências, digamos, 97% das vezes: a otimização prematura é a raiz de todo mal. No entanto, não devemos desperdiçar nossas oportunidades nesses 3% críticos. ... Um bom programador ... será prudente olhar atentamente para o código crítico; mas somente após a identificação desse código. ... a experiência universal de programadores que usam ferramentas de medição é que suas suposições intuitivas falham

Observe que Knuth falou especificamente sobre velocidade de execução em tempo de execução .

..Os programadores perdem muito tempo pensando ou se preocupando com a velocidade das partes não críticas de seus programas.

Além disso, ele escreveu o artigo em 1974, quando qualquer recurso de máquina que apresentasse correlação premium e negativa entre velocidade de execução e capacidade de manutenção do programa (maior velocidade - menos manutenção) provavelmente fosse mais forte do que agora.

OK, para responder sua pergunta, de acordo com Donald Knuth, a otimização NÃO é prematura se corrigir um sério gargalo de desempenho que foi identificado (idealmente medido e identificado durante a criação de perfil).

Como eu disse antes, "otimização prematura" é um dos memes mais mal-intencionados, então a resposta não será completa sem alguns exemplos de coisas que não são otimizações prematuras, mas que às vezes são descartadas da seguinte forma:

  • gargalos visíveis a olho nu e que podem ser evitados antes de serem introduzidos, como o número O (N ^ 2) de ida e volta ao banco de dados com N grande onde existe alternativa O (1)

Além disso, nem sequer estão relacionados à velocidade de execução do tempo de execução:

Cola
fonte