Primeiro, a resposta exata depende de: (1) uso, isto é, argumentos de entrada de função, (2) qualidade e detalhes da implementação do MPI e (3) o hardware que você está usando. Freqüentemente, (2) e (3) estão relacionados, como quando o fornecedor de hardware otimiza o MPI para sua rede.
Em geral, a fusão de coletivos MPI é melhor para mensagens menores, pois os custos de inicialização podem não ser triviais e a sincronização causada pelo bloqueio de coletivos deve ser minimizada se houver variação no tempo de computação entre as chamadas. Para mensagens maiores, o objetivo deve ser minimizar a quantidade de dados enviados.
Por exemplo, em teoria, MPI_Reduce_scatter_block
deve ser melhor do que o MPI_Reduce
seguido MPI_Scatter
, embora o primeiro seja frequentemente implementado em termos do último, de modo que não exista vantagem real. Existe uma correlação entre a qualidade da implementação e a frequência de uso na maioria das implementações do MPI, e os fornecedores obviamente otimizam as funções para as quais isso é exigido pelo contrato da máquina.
Por outro lado, se alguém está em um Blue Gene, MPI_Reduce_scatter_block
usar usando MPI_Allreduce
, que faz mais comunicação do que MPI_Reduce
e MPI_Scatter
combinado, é na verdade um pouco mais rápido. Isso é algo que eu descobri recentemente e é uma violação interessante do princípio da consistência do desempenho no MPI (esse princípio é descrito em mais detalhes em "Diretrizes de desempenho do MPI autoconsistentes " ).
No caso específico de dispersão + coletar versus reunir, considere que no primeiro, todos os dados devem ir para e de um único processo, o que o torna um gargalo, enquanto no geral, os dados podem fluir para dentro e para fora de todas as classificações imediatamente , porque todas as classificações têm alguns dados para enviar a todas as outras classificações. No entanto, o envio de dados de todos os nós de uma só vez não é necessariamente uma boa ideia em algumas redes.
Por fim, a melhor maneira de responder a essa pergunta é fazer o seguinte em seu código e responder a pergunta por experiência.
#ifdef TWO_MPI_CALLS_ARE_BETTER_THAN_ONE
MPI_Scatter(..)
MPI_Gather(..)
#else
MPI_Allgather(..)
#endif
Uma opção ainda melhor é fazer com que seu código o avalie experimentalmente durante as duas primeiras iterações e use o que for mais rápido nas demais iterações:
const int use_allgather = 1;
const int use_scatter_then_gather = 2;
int algorithm = 0;
double t0 = 0.0, t1 = 0.0, dt1 = 0.0, dt2 = 0.0;
while (..)
{
if ( (iteration==0 && algorithm==0) || algorithm==use_scatter_then_gather )
{
t0 = MPI_Wtime();
MPI_Scatter(..);
MPI_Gather(..);
t1 = MPI_Wtime();
dt1 = t1-t0;
}
else if ( (iteration==1 && algorithm==0) || algorithm==use_allgather)
{
t0 = MPI_Wtime();
MPI_Allgather(..);
t1 = MPI_Wtime();
dt2 = t1-t0;
}
if (iteration==1)
{
dt2<dt1 ? algorithm=use_allgather : algorithm=use_scatter_then_gather;
}
}
MPI_Scatter
seguido porMPI_Gather
não fornece a mesma comunicação semântica queMPI_Allgather
. Talvez exista redundância quando você expressa a operação de qualquer maneira?