Eu costumava escrever um monte de montador naquela época. Não é apenas que os compiladores melhoraram, é que a maioria dos hardwares agora tem muita lógica dedicada à execução de código fora de ordem. O verdadeiro problema micro é o agendamento, a maioria das instruções do computador leva vários relógios da máquina para produzir um resultado - e uma carga de memória que perde o cache pode levar várias centenas! Portanto, a idéia era agendar outras instruções para fazer algo útil, em vez de esperar por um resultado. E máquinas modernas podem emitir várias instruções por período de relógio. Depois que começamos a obter o HW de execução fora de ordem, descobri que tentar obter um ótimo desempenho com a codificação manual se tornou um jogo de canecas. Primeiro, o HW fora de ordem não executaria as instruções em seu pedido cuidadosamente criado, a nova e sofisticada arquitetura HW reduziu a penalidade de agendamento de software inadequado o suficiente para que o compilador estivesse geralmente dentro de alguns por cento do seu desempenho. Também descobri que os compiladores agora estavam implementando truques conhecidos, mas geradores de complexidade, como desenrolamento, carregamento inferior, pipelining de software etc. A linha inferior, você precisa trabalhar muito, pule alguns desses truques e o compilador o vence. Use todos eles e o número de instruções de montagem que você precisa aumentar várias vezes!
Provavelmente ainda mais importante, a maioria dos problemas de desempenho, não se refere às taxas de emissão de instruções, mas à inserção dos dados na CPU. Como mencionei acima, a latência da memória agora é de centenas de ciclos e a CPU pode executar várias instruções por período de clock, portanto, a menos que o programa - e especialmente as estruturas de dados sejam projetadas para que a taxa de acertos do cache seja extremamente alta, com o microtuning na instrução nível não terá retorno. Assim como os militares dizem que os amadores falam sobre táticas, os profissionais falam sobre logística. Agora, a programação de desempenho é mais de 90% da logística (movimentação de dados). E isso é difícil de quantificar, pois o gerenciamento de memória moderno normalmente possui vários níveis de cache e as páginas de memória virtual são gerenciadas por uma unidade de hardware chamada TLB. Também o alinhamento de endereços de baixo nível se torna importante, pois as transferências de dados reais não estão em unidades de bytes, ou até 64 bits, mas eles vêm em unidades de linhas de cache. Em seguida, a maioria das máquinas modernas possui hardware que tenta prever quais linhas de cache faltam em um futuro próximo e emitir pré-buscas automáticas para colocá-las no cache. Portanto, a realidade é que, com os modernos modelos de desempenho das CPUs, são tão complexos que quase não podem ser compreendidos. Mesmo simuladores de hardware detalhados nunca podem corresponder à lógica exata dos chips, portanto, o ajuste exato é simplesmente impossível.
Ainda há espaço para algumas codificações manuais. As bibliotecas de matemática (como a função exp), assim como as operações mais importantes de álgebra linear (como a multiplicação de matrizes), ainda são geralmente codificadas manualmente por especialistas que trabalham para o fornecedor de hardware (por exemplo, Intel ou AMD ou IBM), mas provavelmente apenas precisa de alguns programadores de assembler de alto nível por mega-computador corporativo.