MPI динамически распределяет задачи - PullRequest
0 голосов
/ 09 июля 2019

У меня есть программа C ++ MPI, которая работает в кластере Windows HPC (12 узлов, 24 ядра на узел).

  • Логика программы действительно проста:
    1. есть пул задач
    2. В начале программа делит задачи поровну на каждый процесс MPI
    3. Каждый процесс MPI выполняет свои задачи
    4. После того, как все будет завершено, используйте MPI Reduce для сбора результатов в корневой процесс.

Есть одна проблема. Каждое задание может иметь разное время выполнения, и я никак не могу сказать об этом заранее. Равное распределение задачи приведет к множеству процессов, ожидающих простоя. Это тратит много компьютерных ресурсов и увеличивает общее время выполнения.

Я думаю об одном решении, которое могло бы работать.

  • Процесс такой.
    1. Пул задач делится на небольшие участки (например, 10 заданий)
    2. Каждый процесс MPI принимает посылку в момент простоя (не получил посылку или завершил предыдущую посылку)
    3. Шаг 2 продолжается до исчерпания пула задач
    4. Использование MPI Reduce для сбора всех результатов в корневой процесс

Насколько я понимаю, для этой схемы нужен универсальный счетчик между узлами / процессом (чтобы избежать, чтобы разные процессы MPI выполняли одну и ту же посылку), а для его изменения необходим некоторый механизм блокировки / синхронизации. Это, безусловно, имеет свои накладные расходы, но при правильной настройке, я думаю, это может помочь улучшить производительность.

Я не совсем знаком с MPI и имею некоторые проблемы с реализацией. Я могу придумать два способа реализации этого универсального счетчика

  1. Используя технику ввода / вывода MPI, запишите этот счетчик в файл, когда посылка будет взята, увеличьте этот счетчик (безусловно, потребуется механизм блокировки файла)
  2. Использование MPI односторонней связи / общей памяти. Поместите этот счетчик в общую память и увеличьте его при получении посылки. (непременно понадобится механизм синхронизации)

К сожалению, я не знаком ни с какой техникой, и хочу изучить возможность, реализацию или возможные недостатки двух вышеупомянутых методов. Пример кода будет принята с благодарностью.

Если у вас есть другие способы решения проблемы или предложения, это тоже будет здорово. Спасибо.

прослеживание:

Спасибо за все полезные предложения. Я реализовал тестовую программу по схеме использования процесса 0 в качестве распределителя задач.

#include <iostream>
#include <mpi.h>

using namespace std;

void doTask(int rank, int i){
    cout<<rank<<" got task "<<i<<endl;
}

int main ()
{
    int numTasks = 5000;
    int parcelSize = 100;

    int numParcels = (numTasks/parcelSize) + (numTasks%parcelSize==0?0:1);

    //cout<<numParcels<<endl;

    MPI_Init(NULL, NULL);

    int rank, nproc;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);

    MPI_Status status;
    MPI_Request request;

    int ready = 0;
    int i = 0;
    int maxParcelNow = 0;

    if(rank == 0){
        for(i = 0; i <numParcels; i++){
            MPI_Recv(&ready, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
            //cout<<i<<"Yes"<<endl;
            MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, 0, MPI_COMM_WORLD);
            //cout<<i<<"No"<<endl;
        }
        maxParcelNow = i;
        cout<<maxParcelNow<<" "<<numParcels<<endl;
    }else{
        int counter = 0;
        while(true){
            if(maxParcelNow == numParcels) {
                cout<<"Yes exiting"<<endl;
                break;
            }
            //if(maxParcelNow == numParcels - 1) break;
            ready = 1;
            MPI_Send(&ready, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
            //cout<<rank<<"send"<<endl;
            MPI_Recv(&i, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
            //cout<<rank<<"recv"<<endl;
            doTask(rank, i);
        }
    }

    MPI_Bcast(&maxParcelNow, 1, MPI_INT, 0, MPI_COMM_WORLD);    

    MPI_Finalize();
    return 0;
}

Это не работает и никогда не останавливается. Любые предложения о том, как заставить это работать? Этот код отражает идею правильно или я что-то упустил? Спасибо

1 Ответ

1 голос
/ 11 июля 2019

[Преобразование моих комментариев в ответ ...]

С учетом n процессов вы можете иметь свой первый процесс p0 для отправки задач другим n - 1 процессам. Во-первых, он будет осуществлять двухточечную связь с другими n - 1 процессами, чтобы у каждого была работа, а затем он будет блокировать Recv. Когда какой-либо процесс завершится, скажем, p3, он вернет свой результат обратно к p0. На этом этапе p0 отправит другое сообщение на p3 с одной из двух вещей:

1) Еще одно задание

или

2) Какой-то сигнал завершения, если не осталось задач. (Использование тега сообщения - один из простых способов.)

Очевидно, p0 будет перебирать эту логику до тех пор, пока не останется ни одной задачи, и в этом случае она также вызовет MPI_Finalize.

В отличие от того, что вы подумали в своих комментариях, это не круговая схема. Сначала он дает работу каждому процессу или работнику, а затем возвращает другую работу, когда она завершается ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...