Limpa o código legível vs código rápido de leitura difícil. Quando atravessar a linha?

66

Quando escrevo código, sempre tento tornar meu código o mais limpo e legível possível.

De vez em quando chega um momento em que você precisa cruzar a linha e passar de um código limpo para um código um pouco mais feio para torná-lo mais rápido.

Quando é aceitável cruzar essa linha?

    
por Ken Cochrane 05.07.2011 / 03:28
fonte

10 respostas

116

Você cruzou a linha quando

  • Você mediu que seu código é muito lento para o uso pretendido .
  • Você tentou melhorias alternativas que não exigem o código.

Aqui está um exemplo do mundo real: um sistema experimental que estou executando estava produzindo dados muito lentamente, levando mais de 9 horas por execução e usando apenas 40% da CPU. Em vez de atrapalhar o código, movi todos os arquivos temporários para um sistema de arquivos na memória. Adicionado 8 novas linhas de código não feio, e agora a utilização da CPU está acima de 98%. Problema resolvido; nenhuma fealdade exigida.

    
por 05.07.2011 / 04:14
fonte
57

É uma falsa dicotomia. Você pode tornar o código rápido e fácil de manter.

A maneira como você faz isso é escrevê-lo limpo, especialmente com uma estrutura de dados tão simples quanto possível.

Então você descobre onde estão os drenos de tempo (executando-os, depois que você escreveu, não antes), e conserte-os um por um. (Veja um exemplo)

Adicionado: Sempre ouvimos falar de compensações, certas, como uma troca entre tempo e memória, ou uma troca entre velocidade e facilidade de manutenção? Embora tais curvas possam existir, não se deve presumir que qualquer programa esteja na curva , ou mesmo em qualquer lugar próximo a ele.

Qualquer programa que esteja na curva pode facilmente (dando-o a um certo tipo de programador) ser feito de forma muito mais lenta, e muito menos sustentável, e então não chegará nem perto da curva. Tal programa então tem muito espaço para ser feito tanto mais rápido quanto mais sustentável.

Na minha experiência, é aí que muitos programas começam.

    
por 05.07.2011 / 04:22
fonte
31

Na minha existência de OSS eu faço muito trabalho de biblioteca voltado para o desempenho, que está profundamente ligado à estrutura de dados do chamador (isto é, externo à biblioteca), com (por design) sem mandato sobre os tipos de entrada. Aqui, a melhor maneira de fazer este desempenho é meta-programação, que (desde que eu esteja em .NET-land) significa emissão de IL. Isso é algum código feio e feio, mas muito rápido.

Desta forma, eu aceito alegremente que o código biblioteca pode ser "mais feio" que o código aplicação , simplesmente porque tem menos (ou talvez nenhum ) controle sobre as entradas , por isso precisa realizar algumas tarefas através de diferentes mecanismos. Ou como eu expressei no outro dia:

"coding over the cliff of insanity, so you don't have to "

Agora o código application é um pouco diferente, já que é onde os desenvolvedores "normais" (normalmente) estão investindo muito de seu tempo colaborativo / profissional; os objetivos e expectativas de cada um são (IMO) ligeiramente diferentes.

IMO, as respostas acima que sugerem que podem ser rápidas e fáceis de manter estão se referindo ao código application onde o desenvolvedor tem mais controle sobre as estruturas de dados, e não está usando ferramentas como meta-programação. Dito isto, existem diferentes maneiras de se fazer meta-programação, com diferentes níveis de insanidade e diferentes níveis de sobrecarga. Mesmo nessa arena, você precisa escolher o nível apropriado de abstração. Mas quando você ativamente, positivamente, genuinamente quer lidar com dados inesperados do modo mais rápido possível; pode ficar feio. Lidar com isso; p

    
por 05.07.2011 / 08:12
fonte
26

Quando você criou o código e verificou que ele está realmente causando uma lentidão significativa.

    
por 05.07.2011 / 03:30
fonte
13

O código limpo não é necessariamente exclusivo com código de execução rápida. O código normalmente difícil de ler foi escrito porque era mais rápido escrever, não porque é executado mais rapidamente.

Escrever código "sujo" em uma tentativa de torná-lo mais rápido é indiscutivelmente insensato, já que você não sabe ao certo se suas mudanças realmente melhoram alguma coisa. Knuth colocou melhor:

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified."

Em outras palavras, escreva o código limpo primeiro. Em seguida, faça o perfil do programa resultante e veja se esse segmento é, na verdade, um gargalo de desempenho. Nesse caso, otimize a seção conforme necessário e certifique-se de incluir muitos comentários de documentação (possivelmente incluindo o código original) para explicar as otimizações. Em seguida, faça o perfil do resultado para verificar se você realmente fez uma melhoria.

    
por 05.07.2011 / 09:19
fonte
10

Como a pergunta diz "rápido difícil de ler código", a resposta simples é nunca. Nunca há uma desculpa para escrever códigos difíceis de ler. Por quê? Duas razões.

  1. O que acontece se você for atropelado por um ônibus a caminho de casa hoje à noite? Ou (mais otimista e mais tipicamente) retirado deste projeto e redesignado para outra coisa? O pequeno benefício que você imagina ter feito com o seu emaranhado de código é totalmente superado pelo fato de que ninguém mais pode entendê-lo. O risco que isso representa para os projetos de software é difícil de exagerar. Trabalhei uma vez com um grande fabricante PBX (se você trabalha em um escritório, provavelmente tem um dos telefones deles no seu mesa). Um gerente de projetos me disse um dia que seu principal produto - o software proprietário que transformou uma caixa Linux padrão em uma central telefônica totalmente funcional - era conhecido dentro da empresa como "a bolha". Ninguém entendeu mais. Toda vez que eles implementaram um novo recurso. Eles bateram em compilação e depois recuaram, fecharam os olhos, contaram até vinte, depois espiaram por entre os dedos para ver se funcionava. Nenhuma empresa precisa de um produto principal que não controla mais, mas é um cenário assustadoramente comum.
  2. Mas preciso otimizar! OK, você seguiu todos os excelentes conselhos em outras respostas a essa pergunta: seu código está falhando em seus casos de teste de desempenho, você o identificou cuidadosamente e identificou os gargalos , encontre uma solução ... e isso envolverá alguns truques de bit . Tudo bem: agora vá em frente e otimize. Mas aqui está o segredo (e você pode querer se sentar para este): otimização e redução no tamanho do código-fonte não são a mesma coisa . Comentários, espaço em branco, colchetes e nomes de variáveis significativos são todos grandes auxiliares de legibilidade que não lhe custam absolutamente nada porque o compilador os jogará fora. (Ou se você estiver escrevendo uma linguagem não compilada como JavaScript - e sim, existem razões muito válidas para otimizar o JavaScript - elas podem ser tratadas por um compressor .) Longas linhas de código limitado e minimalista (como o que um muntoo publicou aqui ) não tem nada a ver com otimização: é um programador tentando mostrar como eles são inteligentes estão empacotando o máximo de código possível no menor número de caracteres possível. Isso não é inteligente, é estúpido. Um programador realmente inteligente é aquele que pode comunicar suas idéias claramente aos outros.
por 05.07.2011 / 09:18
fonte
4

Quando é um código descartável. Quero dizer que literalmente: quando você escreve um script para executar um cálculo ou tarefa única, e sabe com tanta certeza que você nunca terá que fazer essa ação novamente que você pode 'rm arquivo de origem' sem hesitação, então você pode escolher a rota feia.

Caso contrário, é uma dicotomia falsa - se você acha que precisa ser feio para fazer isso mais rápido, você está fazendo errado. (Ou seus princípios sobre o que é um bom código precisam ser revisados. Usar o goto é, na verdade, bastante elegante quando é a solução adequada para o problema. Raramente é, no entanto.)

    
por 05.07.2011 / 06:49
fonte
3

Sempre que o custo estimado de desempenho inferior no mercado for maior que o custo estimado de manutenção de código para o módulo de código em questão.

As pessoas ainda fazem SSE / NEON / etc. montagem para tentar bater o software de algum concorrente no popular chip CPU deste ano.

    
por 05.07.2011 / 04:50
fonte
3

Não se esqueça que você pode tornar o código de difícil leitura fácil de entender por documentação e comentários apropriados.

Em geral, crie um perfil depois de escrever um código de fácil leitura que faça a função desejada. Os afunilamentos podem exigir que você faça algo que pareça mais complicado, mas você resolve isso explicando a si mesmo.

    
por 05.07.2011 / 10:59
fonte
0

Para mim, é uma proporção de estabilidade (como em cimento, barro cozido no forno, em pedra, escrito com tinta permanente). Quanto mais instável for o seu código, quanto maior a probabilidade de você precisar mudá-lo no futuro, mais facilmente flexível ele precisa ser, como a argila úmida, para se manter produtivo. Também enfatizo flexibilidade e não legibilidade. Para mim, a facilidade de mudar o código é mais importante do que a facilidade de lê-lo. O código pode ser fácil de ler e um pesadelo para mudar, e de que adianta ler e compreender facilmente os detalhes da implementação se eles são um pesadelo para mudar? A menos que seja apenas um exercício acadêmico, normalmente o ponto de poder compreender facilmente o código em uma base de código de produção é com a intenção de poder alterá-lo mais facilmente conforme necessário. Se é difícil mudar, muitos dos benefícios da legibilidade saem pela janela. A legibilidade só é geralmente útil no contexto de flexibilidade, e a flexibilidade só é útil no contexto de instabilidade.

Naturalmente, até mesmo o mais difícil de manter o código imaginável, independentemente de quão fácil ou difícil seja ler, não representa um problema se nunca houver um motivo para alterá-lo, apenas use-o. E é possível obter essa qualidade, especialmente para código de sistema de baixo nível, em que o desempenho geralmente tende a contar mais. Eu tenho o código C que eu ainda uso regularmente, que não mudou desde o final dos anos 80. Não precisa mudar desde então. O código é fugaz, escrito nos dias de bit-fiddling, e eu mal o entendo. No entanto, ainda é aplicável hoje em dia e não preciso entender sua implementação para tirar o máximo proveito dela.

A escrita completa de testes é uma maneira de melhorar a estabilidade. Outro é o desacoplamento. Se o seu código não depende de mais nada, então a única razão para ele mudar é se ele próprio precisa mudar. Às vezes, uma pequena quantidade de duplicação de código pode servir como um mecanismo de desacoplamento para melhorar drasticamente a estabilidade de uma forma que torna uma troca digna se, em troca, você obtiver código que agora é completamente independente de qualquer outra coisa. Agora esse código é invulnerável a mudanças no mundo externo. Enquanto isso, o código que depende de 10 bibliotecas externas diferentes tem 10 vezes o motivo para mudar no futuro.

Outra coisa útil na prática é separar sua biblioteca das partes instáveis de sua base de código, possivelmente até mesmo construí-la separadamente, como você faria para bibliotecas de terceiros (que também devem ser usadas, não alteradas, pelo menos não pela sua equipe). Apenas esse tipo de organização pode impedir as pessoas de adulterá-lo.

Outro é o minimalismo. Quanto menos o seu código tentar fazer, maior a probabilidade de ele fazer o que bem faz. Desenhos monolíticos são quase permanentemente instáveis, uma vez que quanto mais funcionalidades são adicionadas a eles, mais incompletos parecem.

A estabilidade deve ser seu objetivo principal sempre que você quiser escrever código que inevitavelmente será difícil de mudar, como o código SIMD paralelizado que foi micro-sintonizado até a morte. Você contraria a dificuldade de manter o código, maximizando a probabilidade de não precisar alterar o código e, portanto, não precisará mantê-lo no futuro. Isso reduz os custos de manutenção para zero, não importa o quão difícil seja manter o código.

    
por 08.12.2017 / 02:24
fonte