По-перше, точна відповідь залежить від: (1) використання, тобто аргументів введення функції, (2) якості та деталей реалізації MPI та (3) обладнання, яке ви використовуєте. Часто (2) і (3) пов'язані між собою, наприклад, коли постачальник обладнання оптимізує MPI для своєї мережі.
Загалом, злиття колективів MPI краще для менших повідомлень, оскільки витрати на запуск можуть бути нетривіальними, а синхронізація, пов’язана з блокуванням колективів, повинна бути зведена до мінімуму, якщо є різниця в часі обчислення між викликами. Для більшої кількості повідомлень метою має бути мінімізація кількості даних, що надсилаються.
Наприклад, теоретично MPI_Reduce_scatter_block
має бути краще, ніж MPI_Reduce
слідувати MPI_Scatter
, хоча перший часто реалізується з точки зору останнього, таким, що не має реальної переваги. Існує кореляція між якістю впровадження та частотою використання у більшості впроваджень MPI, і виробники, очевидно, оптимізують ті функції, для яких це вимагається машинним контрактом.
З іншого боку, якщо один знаходиться на Blue Gene, роблячи при MPI_Reduce_scatter_block
допомоги MPI_Allreduce
, яка робить більше спілкування , ніж MPI_Reduce
та в MPI_Scatter
поєднанні, насправді зовсім небагато швидше. Це те, що я нещодавно виявив, і є цікавим порушенням принципу самозгодженості продуктивності в MPI (цей принцип більш докладно описаний в "Постійних принципах щодо ефективності MPI" ).
У конкретному випадку розкидання + зібрати проти всіх, врахуйте, що в першому всі дані повинні надходити в один процес і з нього, що робить його вузьким місцем, тоді як в цілому збір даних може надходити в та з усіх рангів відразу , тому що всі ранги мають деякі дані для надсилання до всіх інших рангів. Однак надсилання даних з усіх вузлів одночасно не обов'язково є доброю ідеєю в деяких мережах.
Нарешті, найкращий спосіб відповісти на це питання - це зробити наступне у своєму коді та відповісти на питання експериментом.
#ifdef TWO_MPI_CALLS_ARE_BETTER_THAN_ONE
MPI_Scatter(..)
MPI_Gather(..)
#else
MPI_Allgather(..)
#endif
Ще кращим варіантом є те, щоб ваш код вимірювався експериментально під час перших двох ітерацій, а потім використовувати те, що швидше для інших ітерацій:
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
ПотімMPI_Gather
не забезпечує такий же зв'язку семантичний , якMPI_Allgather
. Можливо, є надмірність, коли ви виражаєте операцію в будь-який спосіб?