Às vezes, você só tem algoritmos que não podem ser melhores do que o tempo linear para o qual ainda há uma strong demanda por desempenho.
Um exemplo é o processamento de vídeo, onde você não pode tornar uma imagem / quadro mais brilhante como um exemplo básico sem fazer um loop em cada pixel (bem, eu suponho que você pode com algum tipo de estrutura hierárquica indicando propriedades herdadas por crianças que acabam descendo em blocos de imagens para nós de folha, mas então você adiaria um maior custo 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 do que o micro).
Há muitos casos assim no meu campo. Costumo estar fazendo mais loops de complexidade linear que precisam tocar tudo ou ler tudo do que aqueles que se beneficiam de qualquer tipo de estrutura ou algoritmo de dados sofisticados. Não há trabalho que possa ser ignorado quando tudo tiver que ser tocado. Então, nesse ponto, se você está inevitavelmente lidando com a complexidade linear, você precisa tornar o trabalho realizado por iteração mais barato e 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, nesta ordem, a representação de dados é a mais importante, pois afeta a capacidade de fazer os dois últimos). Eu não estou correndo em tantos problemas que são resolvidos por árvores, tabelas de hash, algoritmos de classificação e coisas desse tipo. Meu código diário é mais na veia de, "para cada coisa, faça alguma coisa."
É claro que é outro caso para falar sobre quando as otimizações são necessárias (e mais importante, quando não são), micro ou algorítmico. Mas, no meu caso particular, se um caminho de execução crítico precisa de otimização, os ganhos de velocidade 10x + são geralmente atingidos por otimizações de nível micro, como multithreading, SIMD e reorganização de layouts de memória e padrões de acesso para melhor localização de referência. Não é tão frequente que eu consiga, digamos, substituir um tipo de bolha por um introsort ou um tipo de radix ou detecção de colisão de complexidade quadrática com um BVH, mas encontrar hotspots que, por exemplo, se beneficiam da divisão de campo quente / frio. p>
Agora, no meu caso, meu campo é tão crítico em desempenho (raytracing, motores de física, etc) que um raytracer lento mas perfeitamente correto, que leva 10 horas para renderizar uma imagem, é muitas vezes considerado inútil ou mais do que rápido. completamente interativo, mas gera as imagens mais feias com raios vazando em todos os lugares devido à falta de raio / tri interseção à prova d'água. A velocidade é indiscutivelmente a principal métrica de qualidade de tal software, sem dúvida até mais do que correção em algum ponto (já que "correção" é uma idéia difusa com raytracing desde que tudo está se aproximando, desde que não esteja batendo ou algo parecido). E quando esse é o caso, se eu não pensar na eficiência inicial, eu acho que tenho que realmente mudar o código no nível de design mais caro para lidar com designs mais eficientes. Então, se eu não pensar suficientemente sobre eficiência ao projetar algo como um raytracer ou motor de física, é provável que eu tenha que reescrever a coisa toda antes que possa ser considerada útil o suficiente na produção e pelos usuários reais, não por os engenheiros.
O jogo é outro campo semelhante ao meu. Não importa o quão correta é a lógica do seu jogo ou quão fácil e fácil é manter sua base de código se o seu jogo for executado a 1 quadro por segundo, como uma apresentação de slides. Em determinados campos, a falta de velocidade pode realmente tornar o aplicativo inútil para seus usuários. Ao contrário dos jogos, não há métricas "boas o suficiente" em áreas como o raytracing. Os usuários sempre querem mais velocidade, e a concorrência industrial é predominantemente na busca de soluções mais rápidas. Isso nunca será bom o suficiente até que seja em tempo real, em que os jogos estarão usando rastreadores de caminho. E então provavelmente ainda não será bom o suficiente para efeitos visuais, desde então os artistas podem querer carregar bilhões de polígonos e fazer simulações de partículas com auto-colisão entre bilhões de partículas a 30+ FPS.
Agora, se é de algum conforto, apesar disso eu ainda escrevo cerca de 90% do código em uma linguagem de script (Lua) sem nenhuma preocupação com o desempenho. Mas eu tenho uma quantidade anormalmente grande de código que realmente precisa percorrer milhões para bilhões de coisas, e quando você está passando de milhões a bilhões de coisas, você começa a notar uma diferença épica entre código ingênuo de thread único que invoca uma falha de cache a cada iteração vs., digamos, código vetorizado sendo executado em paralelo acessando blocos contíguos onde nenhum dado irrelevante é carregado em uma linha de cache.