Venho programando há alguns anos e frequentemente me encontro em um dilema.
Existem duas soluções -
- um é simples, ou seja, abordagem simples, mais fácil de entender e manter. Envolve alguma redundância, algum trabalho extra (E / S extra, processamento extra) e, portanto, não é a solução mais ideal.
- outro usa uma abordagem complexa, difícil de implementar, geralmente envolvendo a interação entre muitos módulos e é uma solução eficiente em termos de desempenho.
Qual solução devo procurar quando não tenho um SLA de alto desempenho para atender e até a solução simples pode atender ao SLA de desempenho? Senti desdém entre meus colegas desenvolvedores por soluções simples.
É uma boa prática apresentar a solução complexa ideal se o seu SLA de desempenho puder ser atendido por uma solução simples?
Respostas:
O simples. Ele atende às especificações, é mais fácil de entender, é mais fácil de manter e provavelmente é bem menos complicado.
O que você está fazendo ao defender a solução com desempenho eficiente é introduzir generalidade especulativa e otimização prematura em seu código. Não faça isso! O desempenho vai de encontro a quase todas as outras 'capacidades' de engenharia de software que existem (confiabilidade, manutenção, legibilidade, testabilidade, compreensibilidade, ...). Perseguir o desempenho durante o teste indica que realmente há uma necessidade de perseguir o desempenho.
Não persiga o desempenho quando o desempenho não importa. Mesmo que isso importe, você só deve perseguir o desempenho nas áreas em que o teste indica que existe um gargalo de desempenho. Não permita que problemas de desempenho sejam uma desculpa para substituir
simple_but_slow_method_to_do_X()
por uma versão mais rápida se essa versão simples não aparecer como gargalo.O desempenho aprimorado é quase inevitavelmente sobrecarregado com uma série de problemas de cheiro de código. Você mencionou vários na pergunta: Uma abordagem complexa, difícil de implementar, maior acoplamento. Vale mesmo a pena arrastar aqueles?
fonte
Resposta curta: prefira as soluções simples às complexas e lembre -se dos princípios KISS e YAGNI
Como os requisitos e o software inicial do projeto nunca são perfeitos, ele requer alterações à medida que o aplicativo é desenvolvido / usado. A abordagem iterativa nas fases de desenvolvimento é uma combinação muito boa para iniciar as coisas simples e estendê-las conforme necessário. As soluções mais simples têm espaço para flexibilidade e são mais fáceis de manter.
Além disso, tentar ser inteligente e colocar alguma otimização add-hoc ao mesmo tempo em que constrói seu aplicativo não é uma boa prática e pode complicar demais sua solução. Como se sabe,
"premature optimization is the root of all evil"
- do livro de Knuthfonte
Tome uma lição de Knuth aqui: "Devemos esquecer pequenas eficiências, digamos, 97% das vezes: a otimização prematura é a raiz de todo mal".
Pense em suas soluções nesta ordem: Primeiro, sempre, correção. Segundo, melhore a clareza e a simplicidade. Terceiro, e somente quando você puder demonstrar a necessidade, eficiência.
Adicionar eficiência quase sempre lhe custa algo importante e, portanto, só deve ser buscado quando você sabe que precisa.
fonte
A simplicidade é um pré-requisito da confiabilidade . Se você tem uma solução simples que funciona, por todos os meios, vá em frente! É muito mais fácil otimizar um programa de trabalho do que fazer um programa otimizado funcionar. Também não se esqueça da lei de Moore : se sua solução simples atender aos objetivos de desempenho hoje, provavelmente os esmagará 1 em um ano ou dois.
1 Não há garantia lá, porque, como Jimmy Hoffa observou em seu comentário abaixo, a lei de Moore tem seus limites.
fonte
Ótimo é uma palavra ambígua!
Por fim, se houver muito risco em manter o complexo, e se o simples for "bom o suficiente", eu sempre errei do lado do simples.
Acrescente qualquer risco de que o complexo não seja bom o suficiente, então o KISS é provavelmente a resposta certa.
fonte
Eu preferiria o mais simples. Na minha opinião, as otimizações prematuras causam tantos problemas quanto resolvem. Em muitos casos, o bom design permite alterar determinadas implementações no futuro, se elas se tornarem gargalos.
Então, no final das contas - vou projetá-lo o mais flexível possível, mas não sacrificarei muito a simplicidade pela flexibilidade.
fonte
Qual custa menos?
Na maioria das vezes, uma solução simples um pouco mais lenta será perfeitamente aceitável em termos de desempenho, e a simplicidade torna mais barato o desenvolvimento, a manutenção e a substituição.
Por outro lado, às vezes a velocidade é realmente importante e o ganho financeiro resultante de pequenas melhorias na velocidade pode ser muito maior do que o aumento do custo de uma solução mais complicada. Por exemplo, reduzir 0,01s do tempo para concluir uma transação pode tornar um sistema de negociação de valores mobiliários muito mais lucrativo. Uma melhoria de 10% na eficiência de um sistema que suporta vários milhões de usuários pode significar uma redução significativa nos custos do servidor.
Portanto, a pergunta que você deve se perguntar é: O uso da solução complexa tem impacto suficiente nos resultados para pagar pelo custo adicional? Na verdade, você provavelmente deve pedir ao seu cliente que decida, pois está pagando as contas e colhendo os benefícios potenciais. Uma boa opção é ir primeiro com a solução simples e oferecer a solução mais complexa como uma possível melhoria. Isso permite que você instale o sistema e dê ao cliente algo para começar a testar, e essa experiência pode informar a decisão de implementar (ou não implementar) a solução mais complicada.
fonte
Ao avaliar duas abordagens, uma sendo mais simples, mas menos eficiente, enquanto outra sendo mais complexa e mais eficiente, é preciso considerar o problema e o domínio do projeto.
Considere um projeto de software multibilionário para o setor de saúde que planejou a vida útil de mais de 15 anos de manutenção e +20 anos de uso. Em um projeto como esse, definitivamente não será uma preocupação, mas a complexidade e a estrutura do projeto podem causar grandes problemas para a manutenção do projeto, que dura pelo menos 15 anos. Manutenção e simplicidade vêm antes de qualquer coisa.
Então, considere outro exemplo. Um mecanismo de jogo de console que deve alimentar os próximos jogos da empresa pelos próximos 5 anos ou mais. Como os jogos são programas extremamente limitados por recursos, a eficiência é anterior à capacidade de manutenção em muitos casos. Escrever suas próprias estruturas de dados e algoritmos muito específicos para alguma tarefa pode ser muito importante, mesmo se for contra qualquer tipo de "melhores práticas" de desenvolvimento de software. Um bom exemplo disso pode ser o Design Orientado a Dados, no qual você armazena seus dados em matrizes de dados semelhantes, e não em objetos reais. Isso é para aumentar a referência da localidade e, como tal, aumentar a eficiência do cache da CPU. Não é prático, mas muito crucial no domínio especificado.
fonte
Essa é sempre uma pergunta difícil e vejo respostas oscilando de uma maneira, então jogarei o jogo do outro lado, embora não afirme que nenhuma das respostas esteja correta, é um tópico muito suave e caso a caso.
Uma coisa sobre uma solução complexa, mas de alto desempenho, é que você sempre pode documentar o que está sempre vivo. Geralmente sou fã de código de auto-documentação, mas também sou fã de software que responde em um período de tempo que me faz sentir que não está me atrasando. Se você optar pela solução complexa, mas de alto desempenho, considere o que você pode fazer para torná-la não tão ruim:
Envolva-o em uma interface, coloque-o em uma montagem por si só, possivelmente até em um processo próprio. Torne-o o mais fracamente possível possível, com uma parede de abstração tão grossa quanto possível para evitar vazamentos . Escreva muitos testes de unidade para salvar regressões no futuro.
Documente no código, considere escrever alguma documentação real. Pense em estruturas de dados complexas e como elas são documentadas, imagine tentar entender a implementação de uma delas a partir do código sem um artigo do livro / wikipedia sobre estruturas de dados para explicá-la. E, no entanto, todos aceitamos que essas estruturas de dados complexas são realmente boas e é benéfico que alguém as tenha implementado em nossos idiomas.
Lembre-se de que todos nós estamos enviando mensagens em uma pilha TCP / IP que provavelmente é tão problemática quanto o código pode receber se algum de nós olhar para ela, expressamente, para que ele execute da maneira que todos exigimos também. Talvez o seu problema não exija esse nível de otimização, talvez exista, mas tenha cuidado ao abordar essa questão, como todos nós precisamos de vez em quando: Existem dragões lá.
fonte
Estou trabalhando nisso em áreas onde não há SLA de desempenho. Quando se trata de renderizadores offline em computação gráfica, não há "desempenho satisfatório" para os usuários, porque eles já estão gastando enormes somas de dinheiro para distribuir a computação nas nuvens e renderizar farms, mesmo com os renderizadores de última geração para produzir imagens e quadros com qualidade de produção para filmes, por exemplo
Mas devo dizer, como alguém que trabalha neste domínio há muitos anos que qualquer solução que degrada significativamente a capacidade de manutenção em favor da eficiência está realmente trabalhando contra os requisitos de desempenho em constante mudança. Como se você não conseguir manter sua solução efetivamente nos próximos anos, conforme as coisas estão mudando sob seus pés (tanto em termos de código envolvente quanto no que os usuários esperam que os concorrentes continuem se superando), sua solução já está trabalhando para a obsolescência e necessidade de substituição por atacado.
Não vejo o objetivo final de criadores de perfil como o VTune como uma maneira de tornar meu código mais rápido. O valor final deles é garantir que não diminua minha produtividade para atender às crescentes demandas de desempenho. Se eu absolutamente precisar aplicar alguma micro-otimização de aparência grosseira, o criador de perfil, combinado com a execução em casos de usuários do mundo real (e não em alguns casos de teste que eu imagino que possam ser importantes), garantirá a aplicação inevitável de aparência grosseira otimizações muito, muito criteriosamente, apenas para os principais pontos de acesso que aparecem, além de documentá-los com muito cuidado, porque inevitavelmente terei que revisitar, manter, ajustar e alterá-los nos próximos anos, se essa solução permanecer viável.
E, especialmente, se sua solução otimizada envolver mais acoplamento, eu realmente relutarei em usá-la. Entre as métricas mais valiosas que eu apreciei nas áreas mais críticas de desempenho da base de código, está o desacoplamento (como minimizar a quantidade de informações que algo precisa trabalhar, o que também minimiza a probabilidade de exigir mudanças, a menos que precise diretamente de alterações). ), porque essas áreas críticas multiplicam significativamente os motivos pelos quais as coisas mudam. O que significa que, quanto menos informações forem necessárias para o trabalho, menos razões haverá para a mudança e minimizar essas razões é uma parte enorme da melhoria da produtividade em minhas áreas específicas de foco, porque as coisas terão que mudar constantemente de qualquer maneira (nós se tornará obsoleto em um ano),
Para mim, as melhores e mais eficazes soluções que encontrei são aquelas em que eficiência, manutenção e produtividade não são diametralmente opostas uma à outra. A busca para mim é tentar tornar esses conceitos o mais harmoniosos possível.
fonte