Nos meus cursos de análise numérica, aprendi a analisar a eficiência dos algoritmos contando o número de operações de ponto flutuante (flops) necessárias, em relação ao tamanho do problema. Por exemplo, no texto de Trefethen & Bau sobre Álgebra Linear Numérica, existem até imagens em 3D das contagens de flop.
Agora está na moda dizer que "os fracassos são gratuitos" porque a latência da memória para buscar qualquer coisa que não esteja no cache é muito maior do que o custo de um fracasso. Mas ainda estamos ensinando os alunos a contar falhanços, pelo menos em cursos de análise numérica. Em vez disso, deveríamos ensiná-los a contar acessos à memória? Precisamos escrever novos livros didáticos? Ou o acesso à memória é muito específico da máquina para gastar tempo? Qual será a tendência de longo prazo em termos de fracasso ou acesso à memória?
Nota: algumas das respostas abaixo parecem estar respondendo a uma pergunta diferente como "Devo reescrever obsessivamente minha implementação para salvar alguns fracassos ou melhorar o desempenho do cache?" Mas o que estou perguntando é mais parecido com " É mais útil estimar a complexidade algorítmica em termos de operações aritméticas ou de acesso à memória ?"
fonte
Respostas:
Eu acho que contar acessos à memória é obrigatório, mas também devemos pensar em:
Quanta memória local é necessária
Quanta simultaneidade possível temos
Então você pode começar a analisar algoritmos para o hardware moderno.
fonte
De uma perspectiva mais ampla, acho que a análise do desempenho algorítmico deve ser "abrangente". Se estamos ensinando as pessoas a serem desenvolvedores e usuários reais de HPC, elas precisam entender quais são os custos de programação no mundo real. Os modelos de análise abstrata que temos não levam em consideração o tempo do programador. Deveríamos pensar em termos de "tempo total para solução", em vez de apenas contagens no flop e eficiência algorítmica. Não faz sentido gastar três ou quatro dias de programador para reescrever uma rotina que economiza um segundo de tempo do computador por trabalho, a menos que você esteja planejando executar alguns milhões de cálculos. Da mesma forma, o investimento de alguns dias para economizar uma ou duas horas de computação rapidamente compensa. Esse novo algoritmo pode ser incrível,
fonte
Como outros já apontaram, a resposta depende, é claro, se o gargalo é a CPU ou a largura de banda da memória. Para muitos algoritmos que funcionam em alguns conjuntos de dados de tamanho arbitrário, o gargalo geralmente é a largura de banda da memória, pois o conjunto de dados não se encaixa no cache da CPU.
Além disso, Knuth menciona que a análise de acesso à memória tem maior probabilidade de resistir ao teste do tempo, presumivelmente porque é relativamente simples (mesmo considerando a compatibilidade com o cache) em comparação com as complexidades dos pipelines de CPU modernos e da previsão de ramificações.
Knuth usa o termo gigamems no Volume 4A do TAOCP, ao analisar BDDs. Não tenho certeza se ele o usa em volumes anteriores. Ele fez o comentário acima mencionado sobre resistir ao teste do tempo em sua Palestra anual da Árvore de Natal em 2010.
Curiosamente, você está fazendo errado, mostra que nem mesmo a análise do desempenho com base nas operações de memória é sempre direta, pois há elementos como a pressão da VM que entram em cena se os dados não se ajustam à RAM física de uma só vez.
fonte
Como você determina os custos de um algoritmo depende de qual "nível" de computação científica você trabalha e de qual classe de problemas (restrita ou ampla) você considera.
Se você pensa em otimização de cache, isso é claramente mais relevante para, por exemplo, a implementação de pacotes de álgebra linear numérica como BLAS e bibliotecas semelhantes. Portanto, isso pertence à otimização de baixo nível, e é bom se você tiver um algoritmo fixo para um problema específico e com restrições suficientes na entrada. Por exemplo, a otimização de cache pode ser relevante para ter uma implementação rápida da iteração de gradiente conjugado, se prometer que a matriz é suficientemente esparsa.
Por outro lado, quanto mais ampla a classe de problemas, menos você pode prever na computação real (como, por exemplo, você não sabe o quão escassas serão as matrizes de entrada da sua implementação de CG). Quanto mais ampla a classe de máquinas em que seu programa for executado, menos você poderá prever na arquitetura de cache.
Além disso, em um nível mais alto de computação científica, pode ser mais relevante alterar a estrutura do problema. Por exemplo, se você gasta tempo na busca de um bom pré-condicionador para um sistema linear de equações, esse tipo de otimização geralmente supera qualquer otimização de baixo nível, porque o número de iterações é drasticamente reduzido.
Em conclusão, a otimização de cache é útil apenas se não houver mais nada a ser otimizado pelo paralelismo e redução do número assintótico de FLOPs.
Eu acho que é aconselhável adaptar a postura da ciência da computação teórica: no final, melhorar a complexidade assintótica de um algoritmo tem mais retorno do que a micro-otimização de algumas linhas de código existentes. Portanto, a contagem de FLOPs ainda é preferida.
fonte
Sempre me recusei a pensar em contar flops, acessos à memória ou o que você tiver. Esse é um conceito da década de 1960, quando o que você fez foi praticamente dado e somente como o fez foi até a otimização algorítmica. Pense em resolver um problema de elemento finito em uma malha xyz uniforme usando a eliminação gaussiana da iteração Jacobi.
Agora, você pode otimizar isso para o inferno e salvar alguns fracassos, ganhando 10% do tempo de execução. Ou você pode pensar em implementar um método multigrid e um pré-condicionador de bloco ideal, ganhando um fator de 10 em tempo de execução. É isso que devemos treinar nossos alunos a fazer - pense em quais algoritmos externos complexos você pode ganhar ao tentar encontrar um algoritmo interno melhor. Seu chefe (Keyes) tem esses slides sobre o progresso nos cálculos de MHD que tornam esse ponto bastante óbvio.
fonte
Sim obsoleto. A análise algorítmica por flops, ou qualquer outro método, é tão útil quanto o modelo abstrato da máquina quando se considera o tamanho do problema em questão. O desempenho real depende da implementação e do hardware, e a aplicabilidade de qualquer modelo abstrato para este na realidade está diminuindo ao longo do tempo. Por exemplo, à medida que você paralela ainda mais a implementação de um algoritmo complexo, como a dinâmica molecular, aspectos diferentes tornam-se limitadores de taxa em diferentes hardwares e a análise algorítmica não tem nada a ver com as observações. Em um sentido, a única coisa importante é medir o desempenho da (s) implementação (ões) do (s) algoritmo (s) no (s) tipo (s) de hardware em questão.
Essas abstrações são úteis como uma ferramenta de aprendizado? Sim, como muitos modelos usados para o ensino, eles são úteis desde que sejam colocados ao lado da compreensão das limitações do modelo. A mecânica clássica é boa desde que você aprecie que não funcionará em escalas de pequena distância ou grande velocidade ...
fonte
Não estou realmente respondendo à sua pergunta, mas mais adicionando outra variável a considerar: algo a ter em conta são os recursos da linguagem de programação. Por exemplo, o Python
sort
usa o algoritmo Timsort , projetado (entre outras propriedades legais) para minimizar o número de comparações, que podem ser potencialmente lentas para objetos Python. Por outro lado, comparar dois carros alegóricos em C ++ é rápido, mas trocá-los é mais caro, portanto eles usam outros algoritmos.Outros exemplos são a alocação dinâmica de memória (trivial em uma lista Python, rápida tanto em tempo de execução quanto em tempo de desenvolvedor
.append()
), vs FORTRAN ou C, onde, embora possível e mais rápida quando implementada adequadamente, leva muito mais tempo de programação e cérebro. Veja Python é mais rápido que FORTRAN.fonte