Uma redução implementada usando MPI_Allreduce()
é reproduzível desde que você use o mesmo número de processadores, desde que a implementação tenha observado a seguinte observação, exibida na Seção 5.9.1 do padrão MPI-2.2.
Conselhos aos implementadores . É altamente recomendável que MPI_REDUCE
seja implementado para que o mesmo resultado seja obtido sempre que a função for aplicada nos mesmos argumentos, aparecendo na mesma ordem. Observe que isso pode impedir otimizações que tiram proveito da localização física dos processadores. ( Fim dos conselhos aos implementadores .)
Se você precisar garantir a reprodutibilidade a todo custo, siga as orientações no próximo parágrafo:
Conselhos aos usuários . Alguns aplicativos podem não ser capazes de ignorar a natureza não associativa das operações de ponto flutuante ou podem usar operações definidas pelo usuário (consulte a Seção 5.9.5) que exigem uma ordem de redução especial e não podem ser tratadas como associativas. Esses aplicativos devem impor a ordem da avaliação explicitamente. Por exemplo, no caso de operações que exigem uma ordem estrita de avaliação da esquerda para a direita (ou da direita para a esquerda), isso pode ser feito reunindo todos os operandos em um único processo (por exemplo, com
MPI_GATHER
), aplicando a operação de redução na ordem desejada (por exemplo, com
MPI_REDUCE_LOCAL
) e, se necessário, transmita ou espalhe o resultado para os outros processos (por exemplo, com MPI_BCAST
). ( Fim dos conselhos aos usuários .)
No esquema mais amplo, algoritmos eficientes para a maioria dos aplicativos aproveitam a localidade. Como o algoritmo é realmente diferente quando executado em um número diferente de processos, simplesmente não é prático reproduzir exatamente os resultados quando executado em um número diferente de processos. Uma possível exceção é o multigrid com smoothers Jacobi ou polinomiais (por exemplo, Chebyshev), onde é possível que esse método simples tenha um desempenho muito bom.
Com o mesmo número de processos, geralmente é benéfico para o desempenho processar mensagens na ordem em que são recebidas (por exemplo, usando MPI_Waitany()
), o que introduz um não determinismo. Nesses casos, você pode implementar duas variantes, a mais rápida que recebe em qualquer ordem e a "depuração" que recebe em uma ordem estática. Isso requer que todas as bibliotecas subjacentes também sejam gravadas para oferecer esse comportamento.
Para depuração em alguns casos, você pode isolar parte de um cálculo que não oferece esse comportamento reproduzível e executá-lo de forma redundante. Dependendo de como os componentes foram projetados, essa alteração pode ser uma pequena quantidade de código ou muito intrusiva.