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?
performance
optimization
quality
readability
maintainability
Ken Cochrane
fonte
fonte
Respostas:
Você cruza a linha quando
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.
fonte
foo
e renomeá-lafoo_ref
- normalmente ela fica imediatamente acimafoo
no arquivo de origem. Na minha equipamento de teste eu chamofoo
efoo_ref
para validação e avaliação de desempenho relativo.É 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.
fonte
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:
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
fonte
Quando você cria o perfil do código e verifica que ele está realmente causando uma desaceleração significativa.
fonte
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:
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.
fonte
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.
fonte
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.)
fonte
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.
fonte
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.
fonte
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.
fonte