Aprendi com o livro de Kip Irvine . Se você ignorar as críticas (justas) de suas bibliotecas (irrelevantes), posso recomendá-lo como uma boa introdução à linguagem em si - embora para as coisas realmente interessantes você tenha que caçar os obsessivos na rede.
Acho que é útil entender o que acontece nos níveis inferiores. Conforme você pesquisa o assembler, você aprenderá sobre pipelining de CPU, previsão de ramificação, alinhamento de cache, SIMD, reordenação de instruções e assim por diante. O conhecimento deles o ajudará a escrever um código de alto nível melhor.
Além disso, a sabedoria convencional é não tentar otimizar manualmente a montagem na maior parte do tempo, mas deixar o compilador se preocupar com isso. Quando você vir alguns exemplos das coisas distorcidas que os compiladores geram, entenderá melhor por que a sabedoria convencional se mantém.
Exemplo: LFSRs são executados rapidamente com a instrução rotate-with-carry, para casos específicos como este é tão fácil escrever a versão em assembler quanto descobrir se o compilador é ou não inteligente o suficiente para descobrir. Às vezes, você simplesmente sabe algo que o compilador não sabe.
Ele também aumenta sua compreensão dos problemas de segurança - gravar ou executar, saturações de pilha, etc.
Alguns problemas de simultaneidade só se tornam aparentes quando você está ciente do que está acontecendo no nível por instrução.
Pode ser útil às vezes durante a depuração, se você não tiver o código-fonte completo.
Existe o valor da curiosidade. Como as funções virtuais são implementadas? Você já tentou escrever programas DirectX ou COM em assembler? Como grandes estruturas são retornadas, a função de chamada oferece um espaço para elas ou vice-versa?
Depois, há linguagens assembly especiais para hardware gráfico, embora as linguagens de shader fossem de alto nível alguns anos atrás, qualquer coisa que permita pensar sobre um problema de uma maneira diferente é bom.