Um colega meu hoje comprometeu uma classe chamada ThreadLocalFormat
, que basicamente moveu instâncias das classes Java Format para um local de encadeamento, pois elas não são seguras e são "relativamente caras" para criar. Eu escrevi um teste rápido e calculei que eu poderia criar 200.000 instâncias por segundo, perguntei a ele se ele estava criando tantas, às quais ele respondeu "longe de tantas". Ele é um ótimo programador e todos os membros da equipe são altamente qualificados, por isso não temos problemas em entender o código resultante, mas foi claramente um caso de otimização onde não há necessidade real. Ele apoiou o código a meu pedido. O que você acha? É um caso de "otimização prematura" e quão ruim é realmente?
design
optimization
architecture
Craig Day
fonte
fonte
Respostas:
É importante ter em mente a citação completa:
O que isso significa é que, na ausência de problemas de desempenho medidos, você não deve otimizar porque acha que obterá um ganho de desempenho. Existem otimizações óbvias (como não fazer concatenação de strings dentro de um loop apertado), mas qualquer coisa que não seja uma otimização trivialmente clara deve ser evitada até que possa ser medida.
Os maiores problemas da "otimização prematura" são que ela pode apresentar bugs inesperados e pode ser uma grande perda de tempo.
fonte
HashSet
vez de umaList
era prematuro. O caso de uso em questão era uma coleção inicializada estaticamente cujo único objetivo era servir como uma tabela de consulta. Eu não acho que estou errado em dizer que há uma distinção na seleção da ferramenta certa para o trabalho versus a otimização prematura. Acho que seu post confirma esta filosofia:There are obvious optimizations...anything that isn't trivially clear optimization should be avoided until it can be measured.
A otimização de um HashSet foi completamente medida e documentada.Set
também é mais semanticamente correto e informativo do queList
, portanto, há mais do que o aspecto de otimização.As micro otimizações prematuras são a raiz de todo mal, porque as micro otimizações deixam de fora o contexto. Eles quase nunca se comportam como são esperados.
Quais são algumas boas otimizações iniciais na ordem de importância:
Algumas otimizações do ciclo de desenvolvimento intermediário:
Algumas otimizações do ciclo de desenvolvimento final
Nem todas as otimizações iniciais são ruins, as micro otimizações são ruins se forem feitas no momento errado no ciclo de vida do desenvolvimento , pois podem afetar negativamente a arquitetura, podem afetar negativamente a produtividade inicial, podem ter um desempenho irrelevante ou até mesmo um efeito prejudicial no final desenvolvimento devido a diferentes condições ambientais.
Se o desempenho é motivo de preocupação (e sempre deveria ser), pense sempre grande . O desempenho é uma imagem maior e não sobre coisas como: devo usar int ou long ? Vá para Top Down ao trabalhar com desempenho em vez de Bottom Up .
fonte
a otimização sem a primeira medição é quase sempre prematura.
Acredito que seja verdade neste caso, e também no caso geral.
fonte
A otimização é "má" se causar:
No seu caso, parece que um pouco de tempo do programador já foi gasto, o código não era muito complexo (uma suposição do seu comentário de que todos da equipe seriam capazes de entender) e o código é um pouco mais à prova de futuro (sendo thread seguro agora, se eu entendi sua descrição). Parece apenas um pouco mal. :)
fonte
Estou surpreso que essa pergunta tenha 5 anos e, no entanto, ninguém postou mais do que Knuth tinha a dizer do que algumas frases. Os parágrafos que cercam a famosa citação explicam muito bem. O artigo que está sendo citado é chamado de " Programação Estruturada com vá para Declarações " e, embora tenha quase 40 anos, trata de uma controvérsia e um movimento de software que não existe mais, além de exemplos em linguagens de programação que muitas pessoas nunca ouvido falar, uma quantidade surpreendentemente grande do que dizia ainda se aplica.
Aqui está uma citação maior (da página 8 do pdf, página 268 no original):
Outra boa parte da página anterior:
fonte
Muitas vezes vi essa citação usada para justificar código ou código obviamente ruim que, embora seu desempenho não tenha sido medido, provavelmente poderia ser feito mais rapidamente com bastante facilidade, sem aumentar o tamanho do código ou comprometer sua legibilidade.
Em geral, acho que as micro-otimizações iniciais podem ser uma má ideia. No entanto, as otimizações macro (coisas como escolher um algoritmo O (log N) em vez de O (N ^ 2)) costumam valer a pena e devem ser feitas cedo, pois pode ser um desperdício escrever um algoritmo O (N ^ 2) e depois jogue fora completamente em favor de uma abordagem O (log N).
Observe que as palavras podem ser : se o algoritmo O (N ^ 2) for simples e fácil de escrever, você poderá jogá-lo fora mais tarde sem muita culpa, se parecer muito lento. Mas se os dois algoritmos forem igualmente complexos ou se a carga de trabalho esperada for tão grande que você já sabe que precisará da mais rápida, a otimização antecipada é uma boa decisão de engenharia que reduzirá sua carga de trabalho total a longo prazo.
Portanto, em geral, acho que a abordagem correta é descobrir quais são suas opções antes de começar a escrever código e escolher conscientemente o melhor algoritmo para sua situação. Mais importante ainda, a frase "otimização prematura é a raiz de todo mal" não é desculpa para ignorância. Os desenvolvedores de carreira devem ter uma idéia geral de quanto custa operações comuns; eles deveriam saber, por exemplo,
E os desenvolvedores devem estar familiarizados com uma caixa de ferramentas de estruturas de dados e algoritmos, para que possam usar facilmente as ferramentas certas para o trabalho.
Ter muito conhecimento e uma caixa de ferramentas pessoal permite otimizar quase sem esforço. Colocar muito esforço em uma otimização que pode ser desnecessária é ruim (e admito que caí nessa armadilha mais de uma vez). Mas quando a otimização é tão fácil quanto escolher um conjunto / hashtable em vez de uma matriz ou armazenar uma lista de números em duplo [] em vez de string [], por que não? Eu posso estar discordando de Knuth aqui, não tenho certeza, mas acho que ele estava falando sobre otimização de baixo nível, enquanto eu estou falando sobre otimização de alto nível.
Lembre-se, essa citação é originalmente de 1974. Em 1974, os computadores eram lentos e o poder de computação era caro, o que deu a alguns desenvolvedores a tendência de otimizar demais, linha por linha. Eu acho que é isso que Knuth estava pressionando. Ele não estava dizendo "não se preocupe com o desempenho", porque em 1974 isso seria apenas uma conversa maluca. Knuth estava explicando como otimizar; em resumo, deve-se concentrar apenas nos gargalos e, antes disso, você deve realizar medições para encontrar os gargalos.
Observe que você não pode encontrar os gargalos até escrever um programa para medir, o que significa que algumas decisões de desempenho devem ser tomadas antes que exista algo para medir. Às vezes, essas decisões são difíceis de serem alteradas se você errar. Por esse motivo, é bom ter uma idéia geral de quanto custa as coisas, para que você possa tomar decisões razoáveis quando não houver dados disponíveis.
A rapidez com que otimizar e o quanto se preocupar com o desempenho dependem do trabalho. Ao escrever scripts que você executará apenas algumas vezes, preocupar-se com o desempenho geralmente é uma completa perda de tempo. Mas se você trabalha para a Microsoft ou Oracle e está trabalhando em uma biblioteca que milhares de outros desenvolvedores usarão de milhares de maneiras diferentes, pode valer a pena otimizar tudo, para que você possa cobrir todas as diversas casos de uso com eficiência. Mesmo assim, a necessidade de desempenho sempre deve ser equilibrada com a necessidade de legibilidade, capacidade de manutenção, elegância, extensibilidade e assim por diante.
fonte
Pessoalmente, conforme abordado em um tópico anterior , não acredito que a otimização inicial seja ruim em situações nas quais você sabe que encontrará problemas de desempenho. Por exemplo, escrevo softwares de modelagem e análise de superfícies, onde lido regularmente com dezenas de milhões de entidades. Planejar o desempenho ideal no estágio de design é muito superior à otimização tardia de um design fraco.
Outra coisa a considerar é como o aplicativo será escalado no futuro. Se você considerar que seu código terá uma vida útil longa, otimizar o desempenho no estágio de design também é uma boa idéia.
Na minha experiência, a otimização tardia fornece poucas recompensas a um preço alto. A otimização no estágio de projeto, através da seleção e ajuste de algoritmos, é muito melhor. Dependendo de um criador de perfil para entender como seu código funciona não é uma ótima maneira de obter código de alto desempenho, você deve saber isso com antecedência.
fonte
De fato, aprendi que a não otimização prematura é mais frequentemente a raiz de todo mal.
Quando as pessoas escrevem software, ele inicialmente apresenta problemas, como instabilidade, recursos limitados, usabilidade e desempenho ruins. Tudo isso geralmente é corrigido quando o software amadurece.
Tudo isso, exceto desempenho. Ninguém parece se importar com o desempenho. O motivo é simples: se um software falha, alguém corrige o erro e é isso, se um recurso está faltando, alguém o implementa e pronto; se o software tem um desempenho ruim, em muitos casos isso não ocorre por falta de microoptimização, mas devido ao mau design e ninguém vai tocar no design do software. SEMPRE.
Olhe para Bochs. É lento como o inferno. Será que vai ficar mais rápido? Talvez, mas apenas na faixa de alguns por cento. Ele nunca terá desempenho comparável ao software de virtualização como VMWare ou VBox ou mesmo QEMU. Porque é lento por design!
Se o problema de um software é lento, porque é MUITO lento e isso só pode ser corrigido melhorando o desempenho de várias pessoas. + 10% simplesmente não fazem um software lento rapidamente. E você normalmente não receberá mais de 10% pelas otimizações posteriores.
Portanto, se o desempenho for QUALQUER importante para o seu software, você deve levar isso em consideração desde o início, ao projetá-lo, em vez de pensar "ah, sim, é lento, mas podemos melhorar isso mais tarde". Porque você não pode!
Sei que isso realmente não se encaixa no seu caso específico, mas responde à pergunta geral "A otimização prematura é realmente a raiz de todo mal?" - com um NO claro.
Toda otimização, como qualquer recurso, etc. deve ser projetada com cuidado e implementada com cuidado. E isso inclui uma avaliação adequada de custo e benefício. Não otimize um algoritmo para salvar alguns ciclos aqui e ali, quando não criar um ganho de desempenho mensurável.
Apenas como exemplo: você pode melhorar o desempenho de uma função inserindo-a, possivelmente economizando alguns ciclos, mas ao mesmo tempo você provavelmente aumenta o tamanho do seu executável, aumentando as chances de falhas no TLB e no cache que custam milhares de ciclos ou até operações de paginação, que prejudicam totalmente o desempenho. Se você não entender essas coisas, sua "otimização" pode resultar ruim.
A otimização estúpida é mais maligna que a otimização "prematura", mas ambas ainda são melhores que a não otimização prematura.
fonte
Existem dois problemas com o pedido de compra: primeiro, o tempo de desenvolvimento sendo usado para trabalhos não essenciais, que podem ser usados para escrever mais recursos ou corrigir mais erros e, em segundo lugar, a falsa sensação de segurança de que o código está sendo executado com eficiência. O PO geralmente envolve a otimização do código que não será o gargalo da garrafa, sem perceber o código que será. O bit "prematuro" significa que a otimização é feita antes que um problema seja identificado usando medidas adequadas.
Então, basicamente, sim, isso soa como otimização prematura, mas eu não recuaria necessariamente, a menos que introduzisse bugs - afinal, ela foi otimizada agora (!)
fonte
Acredito que é o que Mike Cohn chama de 'banalização' do código - ou seja, gastar tempo com coisas que poderiam ser boas, mas não necessárias.
Ele aconselhou contra isso.
O PS 'gold-plating' pode ser uma espécie de funcionalidade específica. Quando você olha para o código, ele assume a forma de otimização desnecessária, classes 'à prova de futuro' etc.
fonte
Como não há problema em entender o código, esse caso pode ser considerado uma exceção.
Mas, em geral, a otimização leva a um código menos legível e menos compreensível e deve ser aplicado somente quando necessário. Um exemplo simples - se você souber que precisa classificar apenas alguns elementos - use o BubbleSort. Mas se você suspeitar que os elementos possam aumentar e não souber quanto, otimizar com o QuickSort (por exemplo) não é ruim, mas obrigatório. E isso deve ser considerado durante o design do programa.
fonte
Descobri que o problema com a otimização prematura ocorre principalmente quando reescrever o código existente para ser mais rápido. Eu posso ver como poderia ser um problema escrever alguma otimização complicada em primeiro lugar, mas principalmente vejo otimização prematura elevando sua cabeça feia ao consertar o que não é (conhecido por ser) quebrado.
E o pior exemplo disso é sempre que vejo alguém reimplementando recursos de uma biblioteca padrão. Essa é uma grande bandeira vermelha. Assim, vi uma vez alguém implementar rotinas personalizadas para manipulação de strings porque estava preocupado com o fato de os comandos internos serem muito lentos.
Isso resulta em código que é mais difícil de entender (ruim) e gasta muito tempo no trabalho que provavelmente não é útil (ruim).
fonte
De uma perspectiva diferente, é minha experiência que a maioria dos programadores / desenvolvedores não planeja ter sucesso e o "protótipo" quase sempre se torna a Versão 1.0. Eu tenho experiência em primeira mão com 4 produtos originais separados, nos quais o front-end elegante, sexy e altamente funcional (basicamente a interface do usuário) resultou em ampla adoção e entusiasmo do usuário. Em cada um desses produtos, os problemas de desempenho começaram a surgir em períodos relativamente curtos (1 a 2 anos), especialmente quando clientes maiores e mais exigentes começaram a adotar o produto. Muito em breve o desempenho dominou a lista de problemas, embora o desenvolvimento de novos recursos dominasse a lista de prioridades do gerenciamento. Os clientes ficaram cada vez mais frustrados, pois cada versão adicionava novos recursos que pareciam ótimos, mas eram quase inacessíveis devido a problemas de desempenho.
Portanto, falhas fundamentais de projeto e implementação, que eram de pouca ou nenhuma preocupação no "tipo protótipo", se tornaram grandes obstáculos para o sucesso a longo prazo dos produtos (e das empresas).
A demonstração do cliente pode ter uma ótima aparência e desempenho no seu laptop com XML DOMs, SQL Express e muitos dados em cache do lado do cliente. O sistema de produção provavelmente travará uma queimadura se você tiver êxito.
Em 1976, ainda estávamos debatendo as maneiras ideais de calcular uma raiz quadrada ou classificar uma grande variedade, e o ditado de Don Knuth foi direcionado ao erro de focar em otimizar esse tipo de rotina de baixo nível no início do processo de design, em vez de focar na solução do problema. e, em seguida, otimizando regiões localizadas de código.
Quando alguém repete o ditado como uma desculpa para não escrever código eficiente (C ++, VB, T-SQL ou outro), ou para não projetar adequadamente o repositório de dados ou por não considerar a arquitetura de trabalho líquida, então na IMO eles estão apenas demonstrando uma compreensão muito superficial da natureza real de nosso trabalho. Raio
fonte
Suponho que depende de como você define "prematuro". Tornar a funcionalidade de baixo nível rápida quando você está escrevendo não é inerentemente ruim. Eu acho que é um mal-entendido da citação. Às vezes acho que essa citação poderia ter mais qualificação. Eu ecoaria os comentários de m_pGladiator sobre legibilidade.
fonte
A resposta é: depende. Argumentarei que a eficiência é importante para certos tipos de trabalho, como consultas complexas a bancos de dados. Em muitos outros casos, o computador gasta a maior parte do tempo aguardando a entrada do usuário, portanto, otimizar a maior parte do código é, na melhor das hipóteses, um desperdício de esforço e, na pior das hipóteses, contraproducente.
Em alguns casos, é possível projetar para obter eficiência ou desempenho (percebido ou real) - selecionando um algoritmo apropriado ou projetando uma interface com o usuário para que certas operações caras ocorram em segundo plano, por exemplo. Em muitos casos, a criação de perfil ou outras operações para determinar pontos de acesso oferecem um benefício 10/90.
Um exemplo disso que posso descrever é o modelo de dados que fiz para um sistema de gerenciamento de processos judiciais que continha cerca de 560 tabelas. Tudo começou normalizado ('lindamente normalizado' como o consultor de uma determinada empresa grande-5 colocou) e tivemos apenas que colocar quatro itens de dados desnormalizados:
Uma visão materializada para suportar uma tela de pesquisa
Uma tabela mantida por acionador para oferecer suporte a outra tela de pesquisa que não pôde ser feita com uma visualização materializada.
Uma tabela de relatório desnormalizada (isso só existia porque precisávamos receber alguns relatórios de taxa de transferência quando um projeto de data warehouse era enlatado)
Uma tabela mantida por acionador para uma interface que precisava procurar o número mais recente de eventos díspares mais recentes no sistema.
Esse era (na época) o maior projeto J2EE da Australásia - bem mais de 100 anos de tempo de desenvolvedor - e tinha 4 itens desnormalizados no esquema do banco de dados, um dos quais realmente não pertencia a ele.
fonte
A otimização prematura não é a raiz de TODOS os males, com certeza. No entanto, existem desvantagens:
Em vez de otimização prematura, pode-se fazer testes de visibilidade antecipada, para ver se há uma necessidade real de uma otimização melhor.
fonte
A maioria dos que aderem ao "PMO" (ou seja, a cotação parcial) diz que as otimizações devem ser baseadas em medições e as medições não podem ser realizadas até o final.
Também é minha experiência no desenvolvimento de grandes sistemas que o teste de desempenho é realizado no final, à medida que o desenvolvimento está quase completo.
Se seguíssemos os "conselhos" dessas pessoas, todos os sistemas seriam terrivelmente lentos. Eles também seriam caros porque suas necessidades de hardware são muito maiores do que o inicialmente previsto.
Há muito tempo advogo a realização de testes de desempenho em intervalos regulares no processo de desenvolvimento: indicará a presença de um novo código (onde anteriormente não existia) e o estado do código existente.
Outra idéia ideal é instrumentar o software no nível do bloco de funções. À medida que o sistema executa, ele reúne informações sobre os tempos de execução dos blocos de funções. Quando uma atualização do sistema é executada, pode-se determinar o desempenho dos blocos de funções como na versão anterior e os que se deterioraram. Na tela de um software, os dados de desempenho podem ser acessados no menu de ajuda.
Confira esta excelente peça sobre o que o PMO pode ou não significar.
fonte