Código legível limpo vs código rápido e difícil de ler. Quando cruzar a linha?


67

Quando escrevo código, tento sempre torná-lo 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 bom código limpo para um pouco mais feio para torná-lo mais rápido.

Quando é bom cruzar essa linha?


69
Você respondeu sua própria pergunta, você cruza a linha quando precisa cruzar a linha
gnibbler

6
Além disso, seu "código sujo" pode funcionar tão rápido quanto o "código limpo" no hardware daqui a seis meses. Não exagere como o Windows, no entanto. :)
Mateen Ulhaq

21
Há uma diferença significativa entre um algoritmo difícil de entender e um código difícil de entender. Às vezes, o algoritmo que você precisa implementar é complicado, e o código será necessariamente confuso, simplesmente porque está expressando uma ideia complexa. Mas se o código em si é o ponto difícil, ele deve ser corrigido.
tylerl

8
Em muitos casos, um compilador / intérprete inteligente pode otimizar código limpo e legível, para ter o mesmo desempenho que o código "feio". Portanto, há pouca desculpa, a menos que o perfil diga o contrário.
Dan Diplo

11
Hoje em dia, quando se trata de compiladores, seu código feio provavelmente será o mesmo que o seu código limpo (assumindo que você não faça nada realmente estranho). Especialmente no .NET, não é como nos dias C ++ / MFC em que a forma como você define suas variáveis ​​terá impacto no desempenho. Escreva um código de manutenção. algum código acaba sendo complexo, mas isso não significa que seja feio.
precisa saber é o seguinte

Respostas:


118

Você cruza a linha quando

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

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


2
Você também deve manter o código original, mais lento e mais limpo, como uma implementação de referência e para voltar a usar quando o hardware for alterado e seu código mais rápido e mais malicioso não funcionar mais.
Paul R

4
@PaulR Como você mantém esse código? Em forma de comentários? Isso está errado, imo - os comentários ficam desatualizados, ninguém os lê e, pessoalmente, se eu vejo o código comentado, geralmente o removo - é para isso que serve o controle de origem. Um comentário sobre um método explicando o que ele faz é melhor, imo.
Evgeni

5
@ Eugene: Eu costumo manter a versão original de uma rotina chamada fooe renomeá-la foo_ref- normalmente ela fica imediatamente acima foono arquivo de origem. Na minha equipamento de teste eu chamo fooe foo_refpara validação e avaliação de desempenho relativo.
Paul R

5
@Paul, se você estiver fazendo isso, pode ser uma boa ideia reprovar no teste se a versão otimizada for mais lenta que a função ref. isso pode acontecer se as suposições feitas para torná-lo mais rápido não forem mais verdadeiras.
user1852503

58

É 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 o mais simples possível.

Então você descobre onde está o tempo gasto (executando-o, depois de escrevê-lo, não antes) e os corrige um por um. (Aqui está um exemplo.)

Adicionado: sempre ouvimos falar de compensações, certo, como uma troca entre tempo e memória ou uma troca entre velocidade e capacidade de manutenção? Embora essas curvas possam existir, não se deve presumir que um determinado programa esteja na curva , ou mesmo em qualquer lugar próximo a ela.

Qualquer programa que esteja na curva pode facilmente (dando a um certo tipo de programador) tornar-se muito mais lento e muito menos sustentável, e então não estará nem perto da curva. Esse programa, então, tem muito espaço para ser tornado mais rápido e mais sustentável.

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


Concordo. Na verdade, o código rápido que não é limpo ficará mais lento eventualmente, pois você não pode modificá-lo adequadamente.
EdA-qa mort-ora-y

22
Eu não concordo que é uma falsa dicotomia; Na IMO, existem cenários especialmente no código da biblioteca (não muito no código do aplicativo ) em que a divisão é muito real. Veja minha resposta para mais.
Marc Gravell

11
Marc, você pode vincular as respostas nos comentários com o URL "link". programmers.stackexchange.com/questions/89620/…

Todos descobrimos momentos em que precisamos tentar tornar o código mais rápido. Porém, após a experimentação com o criador de perfil para encontrar a melhor solução (caso o código fique feio), isso não significa que o código precise permanecer feio. É uma questão de encontrar a melhor solução que pode não parecer óbvia a princípio, mas uma vez encontrada, geralmente pode ser codificada de maneira limpa. Portanto, acredito que é uma dicotomia falsa e apenas uma desculpa para não arrumar o quarto depois de se divertir com seus brinquedos. Eu digo: chupa e arruma seu quarto.
Martin York

Eu discordo que é uma falsa dicotomia. Como eu trabalho com muitos gráficos, o exemplo mais óbvio para mim é o loop gráfico apertado: não sei com que frequência isso ainda é feito, mas costumava ser comum os mecanismos de jogos escritos em C usarem Assembly para renderização central. loops para espremer toda última gota de velocidade. Isso também me faz pensar em situações em que você programa em Python, mas usa módulos escritos em C ++. "Difícil de ler" é sempre relativo; sempre que você usa velocidade para um idioma de nível inferior, esse código fica mais difícil de ler do que o resto.
Jhocking 5/07

31

Na minha existência no OSS, faço muito trabalho de biblioteca voltado para o desempenho, que está profundamente vinculado à estrutura de dados do chamador (isto é, externo à biblioteca), sem (por design) nenhum mandato sobre os tipos de entrada. Aqui, a melhor maneira de obter esse desempenho é a metaprogramação, que (desde que estou no .NET-land) significa emissão de IL. Esse é um código feio, mas feio, mas muito rápido.

Dessa forma, aceito com satisfação que o código da biblioteca pode ser "mais feio" do que o código do aplicativo , simplesmente porque ele tem menos (ou talvez não) controle sobre as entradas , portanto, é necessário realizar algumas tarefas através de diferentes mecanismos. Ou como eu expressei no outro dia:

"codificando sobre o penhasco da insanidade, para que você não precise "

Agora , o código do aplicativo é um pouco diferente, pois é aí que os desenvolvedores "normais" (sãos) normalmente investem grande parte de seu tempo profissional / colaborativo; os objetivos e expectativas de cada um são (IMO) ligeiramente diferentes.

Na IMO, as respostas acima que sugerem que pode ser rápido e fácil de manter se referem ao código do aplicativo em que o desenvolvedor tem mais controle sobre as estruturas de dados e não está usando ferramentas como metaprogramação. Dito isto, existem diferentes maneiras de executar metaprogramaçã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ê deseja, de maneira ativa, positiva e genuína, ele lida com dados inesperados da maneira mais rápida possível; pode ficar feio. Lide com isso; p


4
Só porque o código é feio, não significa que ele tenha que ser mantido. Comentários e indentação são gratuitos, e códigos feios geralmente podem ser encapsulados em uma entidade gerenciável (classe, módulo, pacote, função, dependendo do idioma). O código pode ser igualmente feio, mas, pelo menos, as pessoas poderão julgar o impacto das mudanças que estão prestes a fazer.
5138 tdammers

6
@ Tdammers, de fato, e tento fazê-lo o mais longe possível; mas é como colocar batom em um porco.
Marc Gravell

11
Bem, talvez alguém deva fazer uma distinção entre sintaxe feia e algoritmos feios - algoritmos feios às vezes são necessários, mas sintaxe feia geralmente é IMO indesculpável.
5118 tdammers

4
A sintaxe feia do @IMO é bastante inevitável se o que você está fazendo é, por natureza, vários níveis de abstração abaixo do nível de linguagem usual.
Marc Gravell

11
@marc ... isso é interessante. Minha primeira reação ao fato de o meta / abstrato ser feio foi a suspeita de que a linguagem / forma de placa específica não é propícia à meta-codificação, em vez de alguma lei subjacente que os une. O que me fez acreditar que foi o exemplo de níveis progressivos de metal na matemática, terminando na teoria dos conjuntos, cuja expressão dificilmente é ugleir do que álgebra ou mesmo aritmética concreta. Mas em seguida, defina-notação é provavelmente uma língua completamente diferente e cada nível Abstration embaixo tem sua própria linguagem ....
explorest

26

Quando você cria o perfil do código e verifica que ele está realmente causando uma desaceleração significativa.


3
E o que é "significativo"?
Rook

2
@ hotpaw2: é a resposta inteligente - assume que os desenvolvedores são pelo menos um pouco concorrentes. Caso contrário, sim, usar algo mais rápido do que o tipo de bolha é (geralmente) uma boa ideia. Mas muitas vezes alguém (para acompanhar a classificação) trocará quicksort por heapsort por uma diferença de 1%, apenas para ver alguém trocá-lo novamente seis meses depois pelo mesmo motivo.

11
nunca mais um motivo para tornar o código não-limpo. Se você não pode tornar seu código eficiente limpo e fácil de manter, está fazendo algo errado.
EdA-qa mort-ora-y

2
@SF. - o cliente sempre achará muito lento, se puder ser mais rápido. Ele não se importa com o código "sem limpeza".
Rook

11
@Rook: o cliente pode achar o código da interface (trivial) muito lento. Alguns truques psicológicos bastante simples melhoram a experiência do usuário sem acelerar o código - adie os eventos dos botões para uma rotina em segundo plano em vez de executar as ações em tempo real, mostre uma barra de progresso ou algo parecido, faça algumas perguntas insignificantes enquanto a atividade é executada em segundo plano ... quando isso não for suficiente, considere a otimização real.
SF.

13

Código limpo não é necessariamente exclusivo do código de execução rápida. Normalmente, o código de difícil leitura foi escrito porque era mais rápido, não porque é executado mais rapidamente.

Escrever código "sujo" na tentativa de torná-lo mais rápido é indiscutivelmente imprudente, pois você não tem certeza de que suas alterações realmente melhoram alguma coisa. Knuth colocou da melhor maneira:

"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 não ficará satisfeito com essa complacência. raciocínio, ele será prudente em examinar atentamente o código crítico; mas somente após esse código ter sido identificado . "

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


10

Como a pergunta diz " código rápido e difícil de ler ", a resposta simples nunca é. Nunca há uma desculpa para escrever código difícil 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 (de maneira mais otimista e tipicamente) retirado deste projeto e transferido para outra coisa? O pequeno benefício que você imagina ter obtido com seu emaranhado de códigos é totalmente compensado pelo fato de que ninguém mais pode entender . É difícil exagerar o risco que isso representa para projetos de software. Eu trabalhei uma vez com um grande PBXfabricante (se você trabalha em um escritório, provavelmente tem um de seus telefones em sua mesa). Seu gerente de projeto me disse um dia que seu produto principal - o software proprietário que transformou uma caixa padrão do Linux em uma central telefônica com todos os recursos - era conhecido dentro da empresa como "o blob". Ninguém mais entendeu. Toda vez que eles implementavam um novo recurso. eles batiam na compilação, depois recuavam, fechavam os olhos, contavam até vinte e espiavam 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 eu preciso otimizar! OK, então você seguiu todos os excelentes conselhos de outras respostas a esta pergunta: seu código está falhando nos casos de teste de desempenho, você o analisou cuidadosamente, identificou os gargalos, apresentou uma solução ... e vai envolva algumas correções de bits . Tudo bem: agora vá em frente e otimize. Mas aqui está o segredo (e você pode optar por este): otimização e redução no tamanho do código-fonte não são a mesma coisa. Comentários, espaços em branco, colchetes e nomes de variáveis ​​significativos são todos um grande auxílio à legibilidade que não custa absolutamente nada, porque o compilador os descartará. (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 .) Linhas longas de código minimalista e apertado (como o que o muntoo possui postado aqui ) não tem nada a ver com otimização: é um programador tentando mostrar o quão inteligente eles são, colocando o máximo de código no menor número de caracteres possível. Isso não é inteligente, é estúpido. Um programador verdadeiramente inteligente é aquele que pode comunicar suas idéias claramente aos outros.

2
Não posso concordar que a resposta seja "nunca". Alguns algoritmos são inerentemente muito difíceis de entender e / ou implementar eficientemente. A leitura do código, independentemente do número de comentários, pode ser um processo muito difícil.
Rex Kerr #

4

Quando é um código descartável. Quero dizer que, literalmente: quando você escrever um script para executar um cálculo one-off ou tarefa, e saber com tanta certeza que você nunca terá que fazer que a ação mais uma vez que você pode 'rm fonte-file' sem hesitação, em seguida, você pode escolher a rota feia.

Caso contrário, é uma dicotomia falsa - se você acha que é feio fazê-lo mais rápido, está fazendo errado. (Ou seus princípios sobre o que é um bom código precisam ser revisados. O uso do goto é de fato bastante elegante quando é a solução adequada para o problema. Porém, raramente é o caso.)


5
Não existe código descartável. Se eu tivesse um centavo para cada vez que o "código descartável" chegasse à produção porque "está funcionando, não temos tempo para reescrevê-lo", eu seria um milionário. Cada linha de código que você escreve deve ser escrita de modo que outro programador competente possa buscá-lo amanhã depois que você for atingido por um raio esta noite. Caso contrário, não escreva.
Mark Whitaker

Não concordo que seja uma falsa dicotomia; Na IMO, existem cenários especialmente no código da biblioteca (não muito no código do aplicativo) em que a divisão é muito real. Veja minha resposta para mais.
Marc Gravell

@ Mark, se o "outro programador competente" é realmente competente, então o código descartável não deve ser um problema tampouco :)

@ Mark - Fácil. Basta escrever o código descartável para que ele falhe em qualquer teste de produção, talvez de alguma maneira não corrigível.
hotpaw2

@ Mark, se o seu "código de descarte" entrar em produção, esse não será o código de descarte. Observe que dediquei um tempo na minha resposta para esclarecer que estou falando de código que é literalmente descartável: ou seja, excluir após o primeiro uso. Caso contrário, concordo com o seu sentimento e disse o mesmo na minha resposta.
maaku 5/07

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 vencer o software de alguns concorrentes no popular chip de CPU deste ano.


Uma boa perspectiva comercial, às vezes os programadores precisam olhar além do meramente técnico.
this.josh

3

Não se esqueça de que você pode facilitar a compreensão de códigos difíceis de ler, através de documentação e comentários adequados.

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


0

Para mim, é uma proporção de estabilidade (como em cimento, concreto, argila cozida no forno, imersa em pedra, escrita com tinta permanente). Quanto mais instável é o seu código, pois quanto maior a probabilidade de você precisar alterá-lo no futuro, mais fácil é a flexibilidade, como a argila úmida, para permanecer produtivo. Também enfatizo flexibilidade e não legibilidade. Para mim, a facilidade de alterar 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 que uso é capaz de ler e entender 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 entender facilmente o código em uma base de código de produção é com a intenção de alterá-lo mais facilmente conforme necessário. Se é difícil mudar, então muitos dos benefícios da legibilidade saem pela janela. A legibilidade só é geralmente útil no contexto da flexibilidade, e a flexibilidade é útil apenas no contexto da instabilidade.

Naturalmente, mesmo o mais difícil de manter o código imaginável, independentemente de quão fácil ou difícil é a leitura, não representa um problema se nunca houver um motivo para alterá-lo, use-o apenas. E é possível obter essa qualidade, especialmente para códigos de sistema de baixo nível, nos quais o desempenho costuma contar mais. Eu tenho o código C que 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 brincadeira, e eu mal o entendo. No entanto, ainda é aplicável hoje, e não preciso entender sua implementação para tirar proveito disso.

Escrever testes exaustivamente é uma maneira de melhorar a estabilidade. Outra é a dissociação. Se o seu código não depende de mais nada, o único motivo para mudar é se ele próprio precisa mudar. Às vezes, uma pequena quantidade de duplicação de código pode servir como um mecanismo de dissociação para melhorar drasticamente a estabilidade de uma maneira que a torna uma troca valiosa se, em troca, você obtiver código que agora é completamente independente de qualquer outra coisa. Agora esse código é invulnerável a alterações no mundo externo. Enquanto isso, o código que depende de 10 bibliotecas externas diferentes tem 10 vezes a razão de mudar no futuro.

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

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

A estabilidade deve ser seu objetivo principal sempre que você tentar escrever um código que inevitavelmente será difícil de mudar, como um código SIMD paralelo que foi micro-ajustado até a morte. Você neutraliza 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 a zero, independentemente da dificuldade do código em manter.

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.