Por que devo me preocupar com micro desempenho e eficácia?

71

Muitas perguntas e respostas nas páginas C / C ++ discutem específica ou indiretamente questões de micro desempenho (como a sobrecarga de uma função indireta versus direta versus inline) ou o uso de um algoritmo O (N 2 ) vs O (N log N) em uma lista de 100 itens.

Eu sempre codigo sem nenhuma preocupação com o desempenho micro e pouca preocupação com o desempenho macro, focando em código fácil de manter e confiável, a menos ou até que eu saiba que tenho um problema.

Minha pergunta é por que um grande número de programadores se importa tanto? É realmente um problema para a maioria dos desenvolvedores, tive a sorte de não ter que me preocupar muito com isso ou sou um programador ruim?

Tim Post
fonte
5
+1, boa pergunta geral.
Iammilind
+1 boa pergunta .. eu adicionei 2 tags .. espero que você não se importe com isso.
2
Eu encabeço duas grandes citações 1) "Otimização prematura é a raiz de todo mal." 2) 80% do seu tempo será gasto com 20% do seu código (regra 80/20).
James Khoury
2
Percebo que algumas respostas falam sobre o meu exemplo O (n * n). Especifiquei explicitamente uma lista de 100 itens, mas eles ainda insistem que o O (nlogn) é melhor, declarando explicitamente melhorias de desempenho se a lista, no futuro, for de mil ou milhões. Essa obsessão por micro otimização ocorre porque os programadores estão programando para possíveis requisitos futuros, em vez de requisitos atuais atuais? (Onde eu ouvi isso antes ...)
mattnz
5
@ James, a citação completa de Donald Knuth é "Devemos esquecer pequenas eficiências, digamos cerca de 97% das vezes: a otimização prematura é a raiz de todo mal". Haverá boas respostas sobre os 3% restantes neste tópico.
StuperUser

Respostas:

14

Na prática, o desempenho raramente é um problema que precisa ser gerenciado nesse nível de detalhe. Vale a pena ficar de olho na situação, se você sabe que armazenará e manipular grandes quantidades de dados, mas, caso contrário, você está certo e, melhor ainda, mantendo as coisas simples.

Uma das armadilhas mais fáceis de se cair - especialmente em C e C ++, onde você tem esse controle refinado - é otimizar muito cedo e com um nível muito fino. Em geral, a regra é: A) não otimize até descobrir que tem um problema e B) não otimize nada que você não tenha provado ser uma área problemática usando um criador de perfil.

Um corolário de B) é: os programadores são notoriamente ruins em prever onde estão seus gargalos de desempenho, embora, para um, eles achem que são bons nisso. Use um criador de perfil e otimize as partes lentas, ou altere os algoritmos se uma seção do código for chamada muitas vezes, para causar um problema.

jwismar
fonte
6
Outro: o código de inicialização que é executado uma vez geralmente não precisa de otimização, então procure em outro lugar.
Mike DeSimone
3
Depende de quantas vezes "uma vez" é. Ao executar ./configure, atrevo- me a dizer que até 75% do tempo de execução pode ser gasto no código de "inicialização" nos programas executados pelo script. 25-50% podem até ser gastos em links dinâmicos.
R ..
12
A regra A é uma regra terrível. A arquitetura de um sistema desempenha um papel no desempenho e, se você descobrir mais tarde, sua arquitetura simplesmente não pode suportar seus requisitos de desempenho, você está basicamente ferrado. Portanto, embora você possa repassar detalhes finos, ignorá-lo completamente no começo está completamente errado.
EdA-qa mort-ora-y
3
@ edA-qa: Eu costumava pensar assim, mas ao longo dos anos experimentamos muitos outros projetos stuggle or fail antes de qualquer consideração de desempenho se tornar uma preocupação. Toda vez que tenho problemas de desempenho, a correção tem um custo comparativamente baixo, dias ou algumas semanas. Não é mais uma preocupação do que qualquer outro "bug" detectado e uma correção no desenvolvimento. No entanto, como qualquer outro item de risco, os resultados de desempenho precisam ser identificados e mitigados no início do projeto.
mattnz
5
O OP perguntou por que tantos se importam e não vejo como essa resposta realmente respondeu à pergunta, a menos que o OP estivesse mais interessado em ouvir alguém dizer "não se preocupe!".
red-dirt
54

Acho que tudo na sua lista é de micro-otimização, que geralmente não deve ser visto, exceto por

usando um algoritmo O (n * n) vs O (NlogN) em uma lista de 100 itens

que eu acho que deveria ser encarado. Claro, essa lista tem 100 itens no momento e tudo é rápido para pequenos n , mas eu estaria disposto a apostar em breve que o mesmo código será reutilizado para uma lista de vários milhões de linhas, e o código ainda terá trabalhar razoavelmente.

Escolher o algoritmo certo nunca é uma micro-otimização. Você nunca sabe que tipos de dados esse mesmo código será usado por dois meses ou dois anos depois. Diferentemente das "micro-otimizações", fáceis de aplicar com a orientação de um criador de perfil, as alterações de algoritmos geralmente exigem um redesenho significativo para fazer uso efetivo dos novos algoritmos. (Por exemplo, alguns algoritmos exigem que os dados de entrada já estejam classificados, o que pode forçar você a modificar partes significativas de seus aplicativos para garantir que os dados permaneçam classificados)

Billy ONeal
fonte
36
+1 para "Escolher o algoritmo certo nunca é uma micro-otimização".
9
Eu marquei com +1 também, mas observe que escolher o algoritmo grande-O-ideal quando seus tamanhos de dados são pequenos pode ser prejudicial ao tempo de desenvolvimento, tamanho do programa e talvez até uso de memória. Se você está classificando as mãos de poker, quer mesmo escrever uma classificação rápida, suave ou mesclada? Eu começaria com uma classificação de inserção simples ou usaria uma rede de classificação.
R ..
8
Isso é engraçado. Em um tópico sobre micro-otimização, muitos comentaristas otimizam as respostas. ;)
Secure
5
"Em breve, eu apostaria que o mesmo código será reutilizado para uma lista de vários milhões de linhas": isso depende completamente do domínio do problema. Exemplos: se você estiver escrevendo um algoritmo de xadrez, pode ter certeza razoável de que o tamanho do tabuleiro não será alterado. Se você programar um veículo autônomo, o número de rodas também não crescerá tão rápido.
Nikie
3
Não gosto de "escolher o algoritmo certo nunca é uma micro-otimização", porque é obvio que é verdade, dada a natureza da palavra "certo". No entanto, sinto que sua implicação é realmente o algoritmo "mais rápido ou mais eficiente", do qual discordo. Escolher o algoritmo mais eficiente é a escolha errada, se levar muito tempo para implementar e a velocidade ou espaço desse segmento dificilmente importa.
Casey Patton
18

Há muito tempo, no meu primeiro emprego, escrevi código para sistemas embarcados. Esses sistemas usavam microprocessadores 8086 e tinham memória limitada. Usamos o compilador Intel C. Um sistema que eu construí precisava acessar uma matriz 3-d de estruturas. Eu o construí como o livro me disse: chame o malloc para as três dimensões, aloque linhas para a próxima dimensão e, em seguida, calloc para os nós finais.

Era bem complicado (para mim na época), eu tinha que fazer ajuste de curva, controle de processo ANOVA e análise qui-quadrado. Não havia bibliotecas que fizeram isso por nós; tivemos que escrever tudo e encaixar tudo no 8086.

O sistema funcionava como um cachorro. Após um rápido perfil, descobri que um dos maiores problemas era o alocador. Para resolver o problema, abandonei todas as chamadas para malloc e fiz meu próprio gerenciamento de memória de um grande bloco de memória.


Em outro caso no mesmo trabalho, o cliente estava reclamando do tempo de resposta em seu sistema estatístico de controle de processos. A equipe antes de mim havia projetado o sistema "software PLC", onde os operadores podiam usar uma lógica booleana para combinar sinais e disparar interruptores. Eles escreveram em um idioma simplificado, o que hoje chamaríamos de "idioma específico do domínio". pelo que me lembro, parecia ((A1 + B1) > 4) AND (C1 > C2)e assim por diante.

O design original analisou e interpretou essa sequência toda vez que foi avaliada. Em nosso modesto processador, isso consumia muito tempo e significava que o controlador do processo não podia ser atualizado tão rápido quanto o processo em execução.

Dei uma nova olhada e decidi que poderia traduzir essa lógica em código de montagem, em tempo de execução. Analisei-o uma vez e, a cada vez que ele rodava, o aplicativo chamava uma função gerada dinamicamente. Mais ou menos como alguns vírus hoje, eu acho (mas eu realmente não sei). O resultado foi um aumento de 100 vezes no desempenho, o que deixou o cliente e meu chefe muito felizes.

O novo código não era tão sustentável, pois eu havia construído um compilador personalizado . Mas a vantagem de desempenho superou bem a desvantagem de manutenção.


Mais recentemente, eu estava trabalhando em um sistema que precisava analisar dinamicamente uma mosca XML. Arquivos maiores levariam muito mais tempo. Isso foi muito sensível ao desempenho; uma análise muito lenta faria com que a interface do usuário se tornasse completamente inutilizável.

Esse tipo de coisa surge o tempo todo.


Então .... às vezes você deseja um código de manutenção e fácil de escrever. Às vezes, você deseja um código que seja executado rapidamente. A compensação é a decisão de engenharia que você precisa tomar em cada projeto.

Cheeso
fonte
9
Em todos os seus exemplos, o custo de otimização depois não foi muito maior do que escrever o código rápido desde o início. Portanto, escrever primeiro um código mais lento e mais simples e, em seguida, otimizar sempre que necessário, funcionou bem em todos eles.
CodesInChaos 11/11
6
@CodeInChaos: A resposta não afirma o contrário. Ele fala da pergunta do OP "Por que eu deveria me preocupar com micro desempenho e eficiência?" Os problemas de pré-otimização foram apenas inferidos pelos outros respondentes.
Webbiedave
12

Se você estiver processando imagens grandes e iterando sobre cada pixel, os ajustes de desempenho podem ser críticos.

Steve Wellens
fonte
2
+1 - também, finanças de alta frequência, qualquer tipo de áudio / vídeo codificador / decodificador, simulações e modelagem (por exemplo, jogos), os bits de todo o sistema, como programadores de CPU e gerenciadores de memória, etc.
Billy Oneal
3
PODE ser crítico, mas só é crítico depois que você provar que é assim e criar um perfil para estar onde você acha que está o problema. (Dica: provavelmente não está lá.)
APENAS MINHA OPINIÃO correta
2
@ APENAS MINHA OPINIÃO correta: na verdade, para o processamento de imagens, o processamento de dados geralmente é o segundo maior consumidor de tempo (a E / S ainda é a maior). No entanto, otimizar a E / S requer muitos designs malucos / incomuns e sua aceitação por outros programadores, e às vezes é impossível melhorar. A parte de processamento, no entanto, é geralmente paralelamente embaraçosa, portanto, são benefícios facilmente acessíveis. (Ajustes de One pode ser visto por outros como uma implementação livro reta ... a menos que você atingir o nível de VirtualDub)
rwong
12

Deixe-me contar um pouco sobre o por trás da cultura.

Se você tem mais de 40 anos do que 20 anos e programa a vida como adulto, então atingiu a maioridade quando o C ++ era realmente o único jogo na cidade, os aplicativos de desktop eram a norma e o hardware ainda era software muito atrasado em termos de largura de banda / capacidade de desempenho.

  • Costumávamos fazer truques estúpidos de programação para poder ler arquivos grandes (> 2G) ...
  • Costumávamos nos preocupar com o tamanho do executável ...
  • Costumávamos nos preocupar com quanta memória nossos programas consumiam ...
  • Tomamos regularmente decisões algorítmicas sobre troca de tempo versus espaço ...
  • Mesmo no back-end, tivemos que escrever programas CGI em C ou C ++ para qualquer coisa para lidar com um não decente. de RPS ... Foi várias ordens de magnitude mais rápidas.
  • Costumávamos executar testes sobre os méritos do desempenho entre delphi / c ++ / vb!

Pouquíssimas pessoas precisam se preocupar com essas coisas hoje.

No entanto, há 10 anos, você ainda precisava se preocupar com o download de seu software em um modem de 56kb e a execução em um PC de 5 anos ... Você se lembra de como eram ruins os PCs em 1996? Pense em termos de 4 GB de disco rígido, um processador de 200 MHz e 128 MB de RAM ...

E os servidores de 10 anos atrás? O servidor de "próxima geração" da Dell custou US $ 2.000 e veio com 2 (!) Processadores pentium de 1 Ghz, 2 Gb ou Ram e um disco rígido de 20 Gb.

Era simplesmente um jogo diferente , e todos os engenheiros "seniores" que têm 10 anos de experiência (os sujeitos que provavelmente responderão suas perguntas) cortaram os dentes naquele ambiente.

sujeira vermelha
fonte
11
Os 20 anos de experiência adicionais também significam que temos as marcas de queimadura por termos passado pelo processo de otimização muitas e muitas vezes e evitamos fazer as coisas que possam precisar mais tarde. Mesmo motivo, não bato muito no polegar enquanto uso um martelo.
Blrfl
11
loop desenrolando <shudder>
red-dirt
5
e hoje todas as crianças que pensam que largura de banda, CPU e memória são ilimitadas estão descobrindo que seus aplicativos móveis não funcionam muito bem.
gbjbaanb
9

já existem 10 respostas aqui e algumas são realmente boas, mas porque essa é uma irritação pessoal para mim ...

otimização prematura, que a) leva muito mais tempo do que uma solução simples b) introduz mais código em que a solução simples teria metade do tamanho e metade da complexidade ec) torna as coisas menos legíveis, ABSOLUTAMENTE devem ser evitadas. No entanto, se um desenvolvedor tiver a opção de usar um std :: map ou std :: vector e ele escolher a coleção errada por pura ignorância, o desempenho é tão ruim, se não pior, quanto a otimização prematura. E se você pudesse mudar um pouco seu código hoje, manter a legibilidade, manter a mesma complexidade, mas torná-lo mais eficiente, você faria isso? Ou você chamaria isso de "otimização prematura"? Acho que muita gente nem pensaria nisso de uma maneira ou de outra.

Uma vez eu fui o cara que aconselhou a "micro-otimização" que exigia muito pouca alteração e recebi a mesma resposta que você acabou de dizer: "você não deve otimizar muito cedo. Vamos fazê-lo funcionar e vamos alterá-lo mais tarde, se houver um problema de desempenho ". Foram necessários vários lançamentos antes de corrigi-lo. E sim, foi um problema de desempenho.

Embora a otimização antecipada possa não ser boa, acho que é muito benéfico se as pessoas escrevem código entendendo o que esse código fará e simplesmente não desconsideram qualquer pergunta que resulte na notação O (x) como sendo "otimização". Agora você pode escrever bastante código e, com um pouco de reflexão sobre o desempenho, evite 80% dos problemas futuros.

Considere também que muitos problemas de desempenho não ocorrerão no seu ambiente e nem imediatamente. Às vezes, você tem um cliente que ultrapassa o limite ou outro desenvolvedor decide construir sobre sua estrutura e aumentar em 10 vezes o número de objetos. Com algumas considerações sobre o desempenho agora, você poderá evitar um reprojeto muito caro mais tarde. E se o problema for encontrado após o lançamento oficial do software, mesmo uma correção simples se tornará 20 vezes mais cara de aplicar.

Concluindo, manter sempre o desempenho em mente ajuda a desenvolver bons hábitos. O que é tão importante ter como escrever de forma limpa, o mais simples possível e o código organizado.

DXM
fonte
+1: Esse é um dos fatores de empregabilidade dos contratados para desenvolver o software Shrinkwrap e os sites comerciais . A prevenção é menos dispendiosa do que a maldição do cliente.
rwong 12/05
6

Suspeito que muito do que você está vendo seja um simples erro de amostragem. Quando as pessoas estão lidando com situações simples, elas escrevem código e esse é o fim das coisas. Eles fazem perguntas quando lidam com algo relativamente complicado, como a necessidade de otimizar, especialmente em uma situação em que não é necessariamente óbvio que a otimização seria necessária.

Dito isto, há, sem dúvida, alguma otimização prematura envolvida também. Correta ou não, C e C ++ têm uma reputação de desempenho, o que tende a atrair pessoas que se preocupam com o desempenho - incluindo aquelas que podem fazer otimização tanto por diversão quanto por serem realmente necessárias.

Jerry Coffin
fonte
11
+1 - Nota: A maioria das perguntas sobre SO com a tag "performance" provavelmente faz parte desse erro de amostragem: P
Billy ONeal
3
Eu com certeza vejo muitas perguntas de otimização prematura aqui ... Eu acho que vem do fato de muitos programadores amadores começarem com a idéia de escrever jogos, e há um enorme conjunto de livros absurdos de "otimização" e sites relacionados ao desenvolvimento de jogos que colocam más idéias na cabeça dos iniciantes. :-)
R ..
4
Quando você está lidando com algo complicado, muitas vezes parece mais fácil de fazer uma pausa a partir do problema complicado e desperdiçar seu tempo se preocupar se você deve usar i++ou++i
Carson63000
@ Carson63000: sim, isso poderia distorcer totalmente as amostras. Ou eles passam um tempo respondendo perguntas sobre por que minha operator ++compilação não foi realizada.
rwong
4

Algumas das outras respostas mencionam sistemas embarcados , e eu gostaria de expandir isso.

Existem muitos dispositivos que contêm processadores de baixo custo, por exemplo: o controlador da caldeira em sua casa, uma calculadora de bolso simples ou dezenas de chips dentro de um carro moderno.

Para economizar dinheiro, eles podem ter quantidades de flash (para armazenar código) e RAM que parecem pequenas para aqueles que escreveram apenas código para PCs ou smartphones. Para economizar energia, eles podem funcionar com taxas de clock relativamente baixas.

Por exemplo, a família de microcontroladores STM32 vai de 24 MHz, 16 KB de flash e 4 KB de RAM , até 120 MHz, 1 MB de flash e 128 KB de RAM .

Ao escrever código para chips como esses, economiza muito tempo se você deseja tornar seu código o mais eficiente possível, como é óbvio. Obviamente, a otimização prematura continua sendo uma má idéia; mas com a prática, você aprende como problemas comuns podem ser resolvidos rapidamente e / ou com recursos mínimos, e codifica adequadamente.

Steve Melnikoff
fonte
11
bons pontos a considerar para sistemas embarcados, um campo em que trabalho em mim mesmo. Mesmo com isso em mente, minha experiência ao longo dos anos é que a otimização equivocada é sempre uma perda de tempo. Sem ferramentas para nos guiar raramente encontramos as áreas problemáticas
Jeff
2

Sendo essas linguagens essencialmente de baixo nível, quando se depara com um caso de desempenho patológico em que um detalhe que não importaria 99% do tempo está causando o gargalo, na verdade, você tem a oportunidade de solucionar diretamente o problema (ao contrário da maioria dos outros línguas); mas é claro, muitas vezes, como fazê-lo com mais eficácia não é imediatamente aparente. Portanto, metade das perguntas de micro-otimização estranhas / interessantes feitas aqui.

A outra metade vem daqueles curiosos sobre o quão perto eles podem chegar do metal. Sendo essas linguagens essencialmente de baixo nível, afinal ...

ildjarn
fonte
+1: vale ressaltar que o "desempenho patológico" pode acontecer a qualquer pessoa no mundo, independentemente da linguagem ou plataforma. A capacidade de reimplementar em uma linguagem de nível inferior para testar e desmontar a leitura pode fornecer mais informações , mas nem sempre fornece uma solução viável. Exemplo: "Eu sei que posso fazê-lo em montagem - mas ele precisa ser executado em ambiente de confiança parcial!"
rwong 11/05
2

O desempenho é sempre um tópico importante quando você está lidando com C e C ++. Em relação à distância que você deve ir, você sempre pode enlouquecer ao ponto de alinhar o ASM ou usar a aritmética dos ponteiros para uma iteração mais rápida. No entanto, chega um momento em que se gasta tanto tempo otimizando que o trabalho no desenvolvimento do programa geral é interrompido.

Ao lidar com esses problemas, há desempenho do programador e desempenho do código. Em qual delas focar sempre trará perguntas interessantes. No final, a questão mais importante é como é perceptível para o usuário. O usuário estará trabalhando com dados que criam matrizes com centenas ou milhares de elementos? Nesse caso, a codificação para fazer as coisas rapidamente pode fazer o usuário reclamar que as operações padrão do programa são lentas.

Depois, há o usuário que estará trabalhando com pequenas quantidades de dados. Alguns arquivos aqui e ali, nos quais executar tarefas como classificação e operações de arquivo não serão tão perceptíveis para o usuário se você estiver usando funções de nível superior que facilitam a manutenção das coisas à custa de algum desempenho.

Este é apenas um pequeno exemplo dos problemas que você encontrará. Outros assuntos incluem o hardware do usuário alvo. Você terá que se preocupar muito mais com o desempenho se lidar com sistemas embarcados, se seus usuários tiverem, por exemplo, máquinas de núcleo duplo com gigs de ram.

onteria_
fonte
Hmm .. Eu não uso aritmética de ponteiro para iteração mais rápida - é uma instrução de multiplicação e adição por loop, esteja você usando iteração baseada em índice ou em ponteiro. No entanto, eu o uso, porque geralmente é mais claro que a iteração baseada em índice.
Billy ONeal
A aritmética do ponteiro não é mais rápida que w / e.
2

Por que os programadores se importam tanto? Existem idéias tolas ocupando suas cabeças, como resolver problemas de desempenho antes que eles saibam que os têm e não entender quando estão tentando adivinhar .

É complicado porque, na minha experiência, há alguns problemas de desempenho que devemos pensar antes do tempo. É preciso experiência para saber o que são.

Dito isto, o método que eu uso é semelhante ao seu, mas não é o mesmo:

  1. Comece com o design mais simples possível. Em particular, a estrutura de dados deve ser o mais normalizada e mínima possível. Na medida em que tem redundância inevitável, deve-se evitar as notificações como forma de mantê-la consistente. É melhor tolerar inconsistência temporária e repará-la com um processo periódico.

  2. Quando o programa estiver em desenvolvimento, faça o ajuste do desempenho periodicamente, porque os problemas de desempenho podem surgir silenciosamente. O método que uso é uma pausa aleatória , porque acho que é o melhor.

Aqui está um exemplo golpe a golpe do que quero dizer.

Mike Dunlavey
fonte
1

Para ser honesto, isso depende do seu objetivo e se você está programando profissionalmente ou como um hobby.

Atualmente, os computadores modernos são máquinas realmente poderosas. Independentemente de quais operações básicas você decide fazer, se você está tentando otimizar ou não, elas podem tornar o trabalho notavelmente rápido. Mas é claro que, se você estiver fazendo outra coisa (por exemplo, supercomputação para campos como física ou química), convém otimizar o quanto quiser.

Os primeiros programadores do MIT não nasceram para fazer coisas incríveis; Eles começaram a simplificar e alimentar os algoritmos existentes. O orgulho deles era fazer com que 2 + 2 desse quatro em dois segundos a menos que o algoritmo existente (isso é apenas um exemplo, você entendeu). Eles constantemente tentavam usar menos cartões perfurados em suas máquinas TI-83 para obter desempenho.

Além disso, se você está programando para sistemas embarcados, certamente precisa ficar de olho no micro desempenho. Você não quer ter um relógio digital lento que marca um segundo 5 nanossegundos antes que outro relógio digital.

Finalmente, se você é um programador amador, certamente não há mal algum em otimizar os mínimos detalhes, mesmo que seu programa seja rápido. Não é necessário, mas certamente algo em que você pode trabalhar e aproveitar a oportunidade para aprender mais. Se você trabalha profissionalmente em um software, não pode aproveitar esse luxo, a menos que seja extremamente necessário.

Andy Ibanez
fonte
11
Não acho que ser um programador amador tenha algo a ver com isso. Só porque você não está fazendo algo profissionalmente, não significa necessariamente que você tem todo o tempo do mundo para gastar com isso. Além disso, a maioria dos amadores vai cometer erros maiores, como escolher os algoritmos errados, do que a maioria dos verdadeiros profissionais. Além disso, o profissional provavelmente está trabalhando em um produto que processa significativamente mais dados que o amador (que, portanto, deve ser mais rápido) e precisa manter os clientes satisfeitos com o desempenho do aplicativo. Os entusiastas do hobby não têm tais restrições.
Billy ONeal
Eles não têm, mas certamente têm mais tempo para trabalhar neles apenas se quiserem.
3
Eu argumentaria o contrário. Eu tenho 8 horas ou mais por dia para trabalhar em algo como profissional. Recebo 1, talvez 2 horas por dia, para meus projetos de hobby.
Billy ONeal
1

usando um algoritmo O (N2) vs O (NlogN) em uma lista de 100 itens.

Eu estava em uma situação semelhante recentemente. Eu tinha uma variedade de itens. No caso esperado, havia dois (!) Itens na lista e, mesmo no pior caso, não espero mais que quatro ou talvez oito.

Eu precisava classificar essa lista. Acontece que a substituição std::sortpor uma rede de classificação (essencialmente muitos ifs aninhados ) reduziu uma grande porcentagem do tempo de execução (não me lembro do número, mas era algo entre 10 e 20%). Esse é um grande benefício de uma micro otimização, e o código é absolutamente crítico para o desempenho.

Claro, eu só fiz isso depois de criar um perfil. Mas o ponto é que, se eu usar uma linguagem que é tão inconveniente e complicada quanto o C ++ (para não mencionar suas regras irritantemente complexas para a resolução de sobrecargas), quero aproveitar todos os benefícios.

Konrad Rudolph
fonte
1

Uso cumulativo de energia

Há uma resposta que eu sempre acho que falta nessa discussão e que me incomoda um pouco - o uso acumulado de energia .

Claro, talvez não importe muito se você escrever seu programa em uma linguagem interpretada de alto nível e deixá-lo rodar em um navegador com algumas camadas de indireção, ou se seu loop demorar 0,01 segundos em vez de 0,001 segundos. Ninguém notará, ou seja, nenhum usuário individual notará.

Mas quando dezenas de milhares, ou até milhões de usuários, em alguns casos, usam seu código, toda essa ineficiência extra aumenta. Se sua ferramenta impede que uma CPU entre no estado de suspensão por apenas dez segundos por dia e um milhão de usuários a utiliza, seu algoritmo ineficiente acaba de gastar 140 kWh extras [1] por dia.

Raramente vejo isso discutido e acho triste. Suspeito fortemente que os números sejam muito piores para estruturas populares, como Firefox e aplicativos da web interativos sofisticados, e seria interessante pesquisar.


[1] Acabei de inventar, 10 milhões de segundos vezes 50 Watts. A figura exata depende de muitas coisas.

tubo
fonte
11
Você deve começar mencionando a palavra mágica "celular". Quando executado em uma plataforma de desktop, um aplicativo que leva 1/100 segundo de tempo da CPU para desenhar um quadro 60 vezes por segundo será "rápido o suficiente"; melhorar o desempenho em dez vezes faria diferença zero para o usuário. Em uma plataforma móvel, no entanto, um aplicativo que é executado com 90% de utilização da CPU pode consumir baterias muito mais rapidamente do que com 10%.
22716
1

Às vezes, você apenas possui algoritmos que não podem ser melhores que o tempo linear, para os quais ainda há uma forte demanda de desempenho.

Um exemplo é o processamento de vídeo em que você não pode tornar uma imagem / quadro mais brilhante como um exemplo básico sem percorrer todos os pixels (bem, suponho que você possa com algum tipo de estrutura hierárquica indicando propriedades herdadas por crianças que acabam descendo para blocos de imagem para nós de folha, mas você adiaria um custo mais alto de loop através de cada pixel para o renderizador e o código provavelmente seria mais difícil de manter do que o filtro de imagem mais otimizado para micro).

Existem muitos casos assim no meu campo. Costumo fazer loops de complexidade linear mais que precisam tocar em tudo ou ler tudo do que aqueles que se beneficiam de qualquer tipo de estrutura ou algoritmo sofisticado de dados. Não há trabalho que possa ser pulado quando tudo tiver que ser tocado. Portanto, nesse ponto, se você inevitavelmente estiver lidando com complexidade linear, precisará tornar o trabalho realizado por iteração cada vez mais barato.

Portanto, no meu caso, as otimizações mais importantes e comuns são frequentemente representações de dados e layouts de memória, multithreading e SIMD (normalmente nessa ordem, com a representação de dados sendo a mais importante, pois afeta a capacidade de executar as duas últimas). Não estou enfrentando tantos problemas que são resolvidos por árvores, tabelas de hash, algoritmos de classificação e coisas desse tipo. Meu código diário está mais na linha de "para cada coisa, faça alguma coisa".

É claro que é outro caso para discutir quando as otimizações são necessárias (e mais importante, quando não são), micro ou algorítmicas. Mas no meu caso particular, se um caminho de execução crítico precisar de otimização, os ganhos de velocidade 10x + são frequentemente alcançados por otimizações de nível micro, como multithreading, SIMD e reorganizando layouts de memória e padrões de acesso para melhorar a localidade de referência. Não é tão frequente que, digamos, substitua um tipo de bolha por um tipo introsort ou tipo radix ou detecção de colisão de complexidade quadrática por um BVH tanto quanto encontro pontos de acesso que, por exemplo, se beneficiam da divisão de campo quente / frio.

Agora, no meu caso, meu campo é tão crítico para o desempenho (rastreamento de raios, mecanismos físicos, etc.) que um rastreador de raios lento, mas perfeitamente correto, que leva 10 horas para renderizar uma imagem, é frequentemente considerado inútil ou mais do que rápido, completamente interativo, mas produz as imagens mais feias, com raios vazando em todos os lugares devido à falta de interseção estanque de raios / tri. A velocidade é sem dúvida a principal métrica de qualidade desse software, sem dúvida até mais do que correção até certo ponto (uma vez que "correção" é uma ideia difusa do raytracing, pois tudo está se aproximando, desde que não esteja travando ou algo assim). E quando for esse o caso, se eu não pensar em eficiência antecipadamente, acho que preciso alterar o código no nível de design mais caro para lidar com designs mais eficientes. Então, se eu não

O jogo é outro campo semelhante ao meu. Não importa o quão correta é a lógica do jogo ou a manutenção e a engenharia da sua base de código, se o jogo for executado a 1 quadro por segundo, como uma apresentação de slides. Em certos campos, a falta de velocidade pode realmente tornar o aplicativo inútil para seus usuários. Ao contrário dos jogos, não existe uma métrica "boa o suficiente" em áreas como raytracing. Os usuários sempre querem mais velocidade, e a concorrência industrial é predominantemente na busca de soluções mais rápidas. Nunca será bom o suficiente até que seja em tempo real, quando os jogos estarão usando rastreadores de caminho. E então provavelmente ainda não será bom o suficiente para o VFX, pois os artistas podem querer carregar bilhões de polígonos e ter simulações de partículas com auto-colisão entre bilhões de partículas a mais de 30 FPS.

Agora, se for de algum conforto, apesar disso ainda escrevo cerca de 90% do código em uma linguagem de script (Lua) sem preocupações com o desempenho. Mas eu tenho uma quantidade invulgarmente grande de código que realmente precisa percorrer milhões a bilhões de coisas e, quando você percorre milhões a bilhões de coisas, começa a notar uma diferença épica entre o código ingênuo de thread único que invoca uma falta de cache a cada iteração versus, digamos, código vetorizado executando em paralelo acessando blocos contíguos onde nenhum dado irrelevante é carregado em uma linha de cache.

user204677
fonte
0

Como você mencionou, o cuidado com problemas de micro desempenho é inútil antes de você considerar alguns problemas realmente causados ​​por esses problemas

huubby
fonte
0

É realmente impossível responder a essa pergunta em geral. A maioria dos softwares que estão sendo criados hoje em dia são sites internos e aplicativos LOB, e para esse tipo de programação, seu raciocínio está correto. Por outro lado, se você estiver escrevendo algo como um driver de dispositivo ou um mecanismo de jogo, nenhuma otimização é "prematura"; é provável que seu software seja executado em sistemas muito diferentes, com diferentes restrições de hardware. Nesse caso, você deve projetar o desempenho e garantir que não escolhe um algoritmo abaixo do ideal.

Nemanja Trifunovic
fonte
Exatamente o que eu queria dizer. Cada parte do software tem seu domínio de aplicativo e não deve se comportar de maneira ideal fora dele. Nesse sentido, a otimização prematura é um exemplo de perfeccionismo equivocado.
K.Steff
0

Acho que o problema do programador, que se preocupa tanto com o desempenho, é que, às vezes, em sua vida, ele precisava escrever código de micro-desempenho, talvez com muita urgência, e aprendeu, aprendeu, aprendeu e, no final, conheceu um muitas coisas e truques.

E agora é difícil esquecer, e sem medição prévia, o que mostra que ele não precisa se preocupar, ele está do lado seguro, usando código rápido.

É sempre bom mostrar seu profundo conhecimento, suas habilidades e alguns truques, e reutilizar algo que você aprendeu. Faz você se sentir valioso e gasta tempo aprendendo, valendo a pena.

Às vezes, na minha vida, aprendi que o incremento do prefixo é mais rápido ...

for (int i = 0; i < MAX; ++i)

... do incremento do postfix:

for (int i = 0; i < MAX; i++)

Agora, se o MAX estiver baixo, isso não importará e, se houver um trabalho real no loop, também não importará. Mas não há uma razão para usar a versão do postfix, mesmo que o compilador de hoje otimize o código por conta própria.

Talvez os candidatos a desempenho precisem de um objetivo adicional, além de escrever 'código funcional', como 'código funcional e legível' para ter uma orientação no grande mar de opções.

Usuário desconhecido
fonte
0

Eu apenas tive a sorte de não ter que me preocupar muito com isso ou sou um programador ruim?

Você se importa com suas necessidades? Se o desempenho não é um requisito, não se preocupe. Passar algum tempo significativo é um desserviço ao seu empregador.

Até certo ponto, o desempenho é sempre um requisito. Se você pode acertá-lo sem pensar nisso, você está justificado em não pensar nisso.

Pessoalmente, sou mais frequentemente motivado pelo desempenho quando meus testes levam muito tempo para passar. Estou impaciente demais para esperar 5 minutos enquanto um conjunto de testes passa. Mas isso geralmente é resolvido brincando com os testes.

Minha pergunta é por que um grande número de programadores se importa tanto? É realmente um problema para a maioria dos desenvolvedores,

Há um grande número de programadores justificados em quanto eles se importam. Existem grandes números que não são. Vamos falar sobre aqueles que não são.

Uma das primeiras coisas que os programadores aprendem na escola, depois de como fazer as coisas realmente funcionarem, é uma grande notação O. Muitos deles aprendem a lição adequadamente e, portanto, concentram-se adequadamente nas coisas dramaticamente impactadas por n. Outros não aprendem matemática e apenas tiram a lição de que, uma vez que ela funciona, precisa ser rápida. Pior, alguns desses alunos nunca aprendem mais nada sobre o que é importante fazer com seu código, além de fazê-lo funcionar e rapidamente. As lições perdidas: torná-lo legível, projetá-lo bem, não brinque com ele sem motivo.

Knuth estava certo: a otimização prematura é a raiz de todo mal. Mas uma vez que funciona, qual é o próximo passo? Rápido, certo? NÃO! O próximo passo é legível. Legível é o primeiro, o próximo, o meio e o último passo. Muitas das pessoas que considero fazer otimizações de desempenho desnecessárias estão jogando legibilidade sob o barramento.

Alguns até sentem uma emoção perversa com a ilegibilidade do código. Eles tiveram que sofrer com a dificuldade de entender o código criado por outros, então agora é a vez deles de retorno.

Eu sei disso porque costumava fazer isso. Uma vez refatorei uma linha de 5 linhas perfeitamente legível, se transformando em uma expressão booleana indecifrável de uma linha, e orgulhosamente a enviei ao meu professor esperando impressionar, pois eu poderia criar algo tão compacto e intimidador. Não recebi os elogios que esperava.

Se o código permanecer legível, será mais rápido. É por isso que Knuth enfatiza "prematuro", não "desnecessário". Porque com certeza, mais rápido é melhor. Mas melhor é apenas melhor, dependendo do que você sacrifica por isso. Portanto, espere até saber qual desempenho realmente precisa antes de fazer sacrifícios por isso. Sacrifique a legibilidade com relutância, porque uma vez que se foi, é difícil voltar.

Além da legibilidade, existe todo o mundo do design de software. Sobre o que é este site. Alguns não têm idéia do que fazer no que diz respeito ao design. Então, como eles não conseguem impressionar com o design, fazem uma bagunça indecifrável, para que as pessoas não possam dizer que não têm idéia. Como ninguém nunca corrige seu código, deve ser um bom código, certo?

Para alguns, o desempenho é a desculpa ideal para fazer o que quiserem. Os programadores têm muito poder e autonomia. Confiança foi colocada neles. Não abuse da confiança.

CandiedOrange
fonte