Nos "bons e velhos tempos", quando copiávamos o shareware em disquetes para amigos, também usamos bastante montagem. Havia uma prática comum de "micro-otimização", em que você encarava e olhava as linhas de montagem até descobrir uma maneira de expressá-la com menos instruções. Havia até um ditado, matematicamente impossível, de que " Você sempre pode remover mais uma instrução " . Dado que alterar o desempenho do tempo de execução por pequenos fatores constantes não é um problema importante para a maioria da programação de hoje, os programadores estão transferindo esses micro- esforços de otimização em outro lugar?
Em outras palavras, uma prática recomendada pode ser levada a um estado extremo, onde não está mais agregando nada de valor? E está perdendo tempo?
Por exemplo: os programadores perdem tempo generalizando métodos privados que são chamados apenas de um local? O tempo é desperdiçado reduzindo os dados do caso de teste? Os programadores (ainda) estão preocupados demais com a redução de linhas de código?
Existem dois grandes exemplos do que estou procurando abaixo: (1) gastar tempo encontrando os nomes de variáveis corretos e até renomeando tudo; e (2) Remoção de duplicação ainda menor e tênue de código.
Observe que isso é diferente da pergunta " Para o que você otimiza? ", Porque estou perguntando o que outros programadores parecem maximizar, com o estigma de serem "micro" otimizações e, portanto, não um uso produtivo do tempo.
fonte
Respostas:
Formatação de código
fonte
=
e o quarto para os inicializadores!Eu costumava escrever um monte de montador naquela época. Não é apenas que os compiladores melhoraram, é que a maioria dos hardwares agora tem muita lógica dedicada à execução de código fora de ordem. O verdadeiro problema micro é o agendamento, a maioria das instruções do computador leva vários relógios da máquina para produzir um resultado - e uma carga de memória que perde o cache pode levar várias centenas! Portanto, a idéia era agendar outras instruções para fazer algo útil, em vez de esperar por um resultado. E máquinas modernas podem emitir várias instruções por período de relógio. Depois que começamos a obter o HW de execução fora de ordem, descobri que tentar obter um ótimo desempenho com a codificação manual se tornou um jogo de canecas. Primeiro, o HW fora de ordem não executaria as instruções em seu pedido cuidadosamente criado, a nova e sofisticada arquitetura HW reduziu a penalidade de agendamento de software inadequado o suficiente para que o compilador estivesse geralmente dentro de alguns por cento do seu desempenho. Também descobri que os compiladores agora estavam implementando truques conhecidos, mas geradores de complexidade, como desenrolamento, carregamento inferior, pipelining de software etc. A linha inferior, você precisa trabalhar muito, pule alguns desses truques e o compilador o vence. Use todos eles e o número de instruções de montagem que você precisa aumentar várias vezes!
Provavelmente ainda mais importante, a maioria dos problemas de desempenho, não se refere às taxas de emissão de instruções, mas à inserção dos dados na CPU. Como mencionei acima, a latência da memória agora é de centenas de ciclos e a CPU pode executar várias instruções por período de clock, portanto, a menos que o programa - e especialmente as estruturas de dados sejam projetadas para que a taxa de acertos do cache seja extremamente alta, com o microtuning na instrução nível não terá retorno. Assim como os militares dizem que os amadores falam sobre táticas, os profissionais falam sobre logística. Agora, a programação de desempenho é mais de 90% da logística (movimentação de dados). E isso é difícil de quantificar, pois o gerenciamento de memória moderno normalmente possui vários níveis de cache e as páginas de memória virtual são gerenciadas por uma unidade de hardware chamada TLB. Também o alinhamento de endereços de baixo nível se torna importante, pois as transferências de dados reais não estão em unidades de bytes, ou até 64 bits, mas eles vêm em unidades de linhas de cache. Em seguida, a maioria das máquinas modernas possui hardware que tenta prever quais linhas de cache faltam em um futuro próximo e emitir pré-buscas automáticas para colocá-las no cache. Portanto, a realidade é que, com os modernos modelos de desempenho das CPUs, são tão complexos que quase não podem ser compreendidos. Mesmo simuladores de hardware detalhados nunca podem corresponder à lógica exata dos chips, portanto, o ajuste exato é simplesmente impossível.
Ainda há espaço para algumas codificações manuais. As bibliotecas de matemática (como a função exp), assim como as operações mais importantes de álgebra linear (como a multiplicação de matrizes), ainda são geralmente codificadas manualmente por especialistas que trabalham para o fornecedor de hardware (por exemplo, Intel ou AMD ou IBM), mas provavelmente apenas precisa de alguns programadores de assembler de alto nível por mega-computador corporativo.
fonte
float
. Por alguma razão que não consigo entender, muitas pessoas pensam que isso é uma coisa boa.Às vezes, gasto (desperdício?) Tempo ao escolher um bom nome para uma variável ou método, para que não seja apenas precisamente descritivo, mas também tenha um bom estilo linguístico.
Vai um pouco mais longe quando tento colocar todos os trabalhos (um programa) no mesmo estilo linguístico. Às vezes, minha opinião muda e eu reviso o livro. :)
Não, isso leva muito tempo. É bastante raro. Mas eu gosto de manter a boa gramática em meus programas.
fonte
Complexidade do tempo. Eu gasto muito tempo otimizando as coisas para obter o pior desempenho possível em uma escala que é uma ordem de magnitude maior do que qualquer coisa que eu saiba que o programa encontrará realisticamente.
Eu sou obsessivo demais para deixar de lado o osso 'mas poderia crescer muito', mesmo quando outras decisões de design impedem que isso aconteça realisticamente.
No entanto, em minha defesa, se o irrealista se tornar realidade ... a otimização realmente não é mais 'micro'. Note, eu não disse impossível , mas irrealista .
Obviamente, os requisitos têm precedência.
fonte
Acho que perdi semanas no tempo brincando com manipuladores de exceção Java.
É muito trabalho para código que falha duas ou três vezes por ano. Isso deve ser um aviso ou uma informação? Erro ou fatal? É realmente fatal se o processo for reiniciado em cinco minutos? É realmente um aviso se tudo ainda estiver no estado padrão?
Muita observação do umbigo e discussão sobre a natureza de um erro de IO. Se você não consegue ler um arquivo pela rede, é um erro de arquivo ou de rede? E assim por diante.
Em um ponto, substituí todas as strings de concat por,
String.format
para que o erro anual de arquivo não resulte em objetos extras na pilha. Isso foi um desperdício.fonte
Suponho que estou preocupado demais com os LOCs. Não tanto os próprios LOCs, mas o número de instruções e ainda mais o número de instruções duplicadas. Sou alérgico a duplicar código. Em geral, adoro refatorar, mas suponho que cerca de 50% do que faço não torne o código significativamente mais agradável.
fonte
Lendo um arquivo linha por linha, em vez de apenas ler a linha inteira em uma string e processar a string de uma só vez.
Claro, faz diferença na velocidade de execução, mas raramente vale as linhas extras de código. Isso torna o código muito menos sustentável e aumenta o tamanho do código.
Sim, isso provavelmente não faz sentido se o arquivo tiver 3 GB de tamanho, mas a maioria dos arquivos não é tão grande (pelo menos não aqueles com os quais estou trabalhando ;-)).
fonte
Quando eu estava escrevendo a linguagem assembly, fazia sentido procurar por bytes e ciclos, mas isso foi há muito tempo e os compiladores percorreram um longo caminho desde então. Eu não tentaria otimizar manualmente um agora.
Da mesma forma, quando eu mudei para escrever em C, era muito fácil fazer um trabalho melhor do que os compiladores C, mas todos os anos a Borland, a Microsoft ou alguém lançavam um novo e melhorado que chutava o anterior pela sala. Comecei a prestar atenção ao código de montagem real que o compilador estava emitindo e, se não estivesse escrevendo algum código legal e rígido, desenrolando loops, movendo variáveis para fora dos loops, etc.
Hoje em dia eu estou escrevendo em linguagens muito mais altas como Perl, Python e Ruby. Eu uso alguns dos truques do compilador na frente, como desenrolar o loop se fizer sentido, mover variáveis estáticas para fora dos loops, etc., mas não me preocupo tanto com isso porque as CPUs são um pouquinho mais rápidas agora. Se um aplicativo parece arrastar inesperadamente, usarei um criador de perfil e verei o que posso encontrar; se algo puder ser aprimorado, começarei a fazer benchmarking de várias maneiras em que posso pensar em fazer algo mais rápido e depois ver como ele aumenta de escala.
Em geral, tento ser inteligente em como escrevo código, com base em anos de experiência.
fonte
Uma micro-otimização: aprimorando o desempenho de thread único de coisas que são paralelizáveis ou que podem simplesmente ser melhoradas com o novo hardware.
Se algo está lento em um computador de 10 anos atrás, provavelmente uma solução mais barata é comprar um computador mais novo e mais rápido, em vez de desperdiçar a otimização do tempo do programador. Um grande foco das otimizações consideradas, no entanto, deve ser encontrar uma maneira de usar os 8 núcleos que você pode obter hoje ou os 64 que você poderá obter daqui a alguns anos, em vez de se agonizar com minúcias, como o custo extra do lixo coleção.
fonte
A micro-otimização em termos de desempenho em tempo de execução dificilmente é um problema fechado até hoje (embora possa ser menos comum). Ocasionalmente, tenho que explicar às pessoas que a sobrecarga de alocar um objeto extra a cada solicitação ou adicionar uma chamada de função extra é insignificante.
Além disso, também vejo programadores otimizar para simplificar (inclusive eu). Ainda tenho que trabalhar em algum lugar que não precisei explicar a diferença entre simplicidade e simplismo.
fonte
programmers.
=programmers.stackexchange.com
seria diferente de todos os lugares em que você trabalhou onde teve que explicar a diferença entre esses dois conceitos? Por favor, explique a diferença.Sou a favor do princípio de não otimizar, a menos que seja realmente necessário. E sou a favor do princípio do primeiro perfil. E, em princípio, otimizarei apenas algo que fará a diferença necessária.
Isso é em princípio. Mas o princípio é um mentiroso.
Eu tenho o hábito de cuidar de funções em bibliotecas usadas com freqüência, que eu acho que serão muito usadas em loops internos. E então, quando você descobre algo em outra função, que não é tão frequentemente usado ...
Ah - e depois há a lógica "eu estou escrevendo - é claro que é uma biblioteca importante".
Ah - e entre. Eu nunca usei um criador de perfil para orientar uma otimização. Não tenha acesso a um comercial e nunca desenvolveu a motivação para descobrir o gprof.
Basicamente, sou hipócrita. Esses princípios se aplicam a outras pessoas, mas tenho muito medo de alguém dizer "mas por que você escreveu dessa maneira lenta e ruim?" para que eu nunca possa realmente aplicá-las adequadamente a mim mesmo.
Eu não sou tão ruim quanto aqui, no entanto. E eu sou completamente imune ao auto-engano, então você sabe que eu estou certo sobre isso!
EDITAR
Devo acrescentar - um dos meus maiores problemas estúpidos são as despesas gerais. Não em todos os lugares, é claro, mas naqueles casos que eu acho que será muito usado em loops internos (onde não tenho evidências objetivas de um problema). Escrevi códigos desagradáveis baseados em offset para evitar fazer chamadas de método virtual mais de uma vez. O resultado pode até ser mais lento, pois provavelmente não é um estilo de codificação que os otimizadores sejam projetados para lidar bem - mas é mais inteligente ;-)
fonte
Nos velhos tempos, quando os computadores tinham tempos de relógio medidos em microssegundos, memória medida em kilobytes e compiladores primitivos, a micro otimização fazia algum sentido.
A velocidade e o tamanho dos computadores da geração atual e a qualidade dos compiladores da geração atual significam que a micro-otimização geralmente é uma perda total de tempo. As exceções tendem a ser quando você absolutamente precisa obter o desempenho máximo de algum código computacionalmente intensivo ... ou está escrevendo para um sistema incorporado com recursos extremamente limitados.
O Twitter e o Facebook vêm à mente :-)
fonte