Solução simples versus complexa (mas com desempenho eficiente) - qual escolher e quando?

28

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?

MoveFast
fonte
10
consulte: Como evito a "Intuição de otimização ruim do desenvolvedor"? "Infelizmente, os desenvolvedores geralmente têm intuição horrível sobre onde os problemas de desempenho em um aplicativo vai ser realmente ...."
mosquito
11
"Não é o melhor" ainda será "bom o suficiente"? Então fique com isso.
8
Sim. " Le mieux est l'ennemi du bien ". Voltaire. ("O perfeito é o inimigo do bem.") O suficiente é bom o suficiente - até que o teste de desempenho diga o contrário.
David Hammen 14/09/12
Acho que (em geral) simples implica eficiente. Portanto, muitas vezes não há necessidade de compromisso.
Dan
2
“Parece que a perfeição é atingida não quando não há mais nada a acrescentar, mas quando não há mais nada para remover.” - Antoine de Saint-Exupery
keuleJ

Respostas:

58

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?

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?

David Hammen
fonte
sua resposta é muito útil
MoveFast
11
Tão simples quanto possível, mas não mais simples; O mais rápido possível, mas não mais rápido; etc
user606723
2
"Em caso de dúvida, o uso de força bruta";)
tdammers
Os comentários no código podem ser catárticos e úteis aqui. Um pequeno comentário apontando a solução complexa + rápida, e por que você não a usou, pode deixar você sentir menos como se tivesse ignorado o algoritmo ideal. E isso pode ajudar os mantenedores a entender sua escolha e apontá-los na direção certa se a otimização for realmente necessária posteriormente.
TheAtomicOption
12

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 Knuth

EL Yusubov
fonte
11
@ ManojGumber, não há problema e é realmente a essência do que nós, como programadores, devemos cuidar em primeiro lugar.
EL Yusubov 14/09/12
8

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.

simon
fonte
4
Observe que isso não significa que você não deve escrever uma boa implementação em primeiro lugar.
@ ThorbjørnRavnAndersen: é claro, é disso que se trata os dois primeiros pontos.
simon
11
@simon a citação é freqüentemente usado como uma desculpa para escolher de forma descuidada,
Sobre o seu segundo ponto: tive um colega que muitas vezes afirmava que preferia um código incorreto, bem estruturado e limpo, antes do espaguete correto.
Buhb
@ ThorbjørnRavnAndersen pessoas incompetentes usarão qualquer coisa como desculpa. Não tem nenhum impacto no valor do pensamento original.
18712 simon
7

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.

dasblinkenlight
fonte
Você esqueceu a outra lei de Moore, que afirma: "Opa, sobre a minha primeira lei .." Desculpe chefe, a lei de Moore não existe mais (trocadilhos). Eu não discordo do resto do seu ponto, gostaria apenas de pelo menos ressaltar a última parte lá.
Jimmy Hoffa
2
Desculpe, mas em toda a minha experiência neste setor. "conjuntos de trabalho" estão aumentando MUITO mais rápido que a velocidade do nosso hardware, que é constantemente atualizado. Realmente, eu apenas removeria o argumento da lei de Moore.
user606723
@ user606723 O crescimento do ponto de "conjunto de trabalho" é ortogonal à pergunta "otimizada ou simples": a carga de trabalho os alcançará, independentemente da solução implementada. O objetivo de incorporar a lei de Moore à mistura era apontar que, mesmo que a solução simples esteja sob alguma pressão de desempenho no momento da redação, a pressão diminuirá à medida que o hardware mais rápido se tornar disponível.
precisa saber é o seguinte
@dasblinkenlight, o crescimento do workset não é mais ortogonal à questão do que a lei de moore. O objetivo de trazer o workset para o problema é que, se uma solução simples estiver sob alguma pressão de desempenho no momento do lançamento, o desempenho será insuficiente em um futuro próximo, pois o crescente workset destrói qualquer melhoria de desempenho obtida por hardware aprimorado. Embora eu seja a favor de um software simples, confiável e de manutenção, liberar software que já está sob pressão de desempenho no lançamento e esperar que a lei de moore o iguale é uma filosofia terrível.
user606723
3

É uma boa prática apresentar a solução complexa ideal se o seu SLA de desempenho puder ser atendido por uma solução simples?

Ó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.

Andrew
fonte
2

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.

Tsvetomir Dimitrov
fonte
2

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.

Caleb
fonte
2

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.

zxcdw
fonte
1

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á.

Jimmy Hoffa
fonte
0

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.

Dragon Energy
fonte