Вложенный параллелизм OpenMP по требованию - PullRequest
2 голосов
/ 27 марта 2019

У меня есть список заданий, которые я обрабатываю параллельно с OpenMP:

void processAllJobs()
{
#pragma omp parallel for
    for(int i = 0; i < n; ++i) 
        processJob(i);
}

Все задания имеют несколько последовательных частей и частей, которые можно распараллелить, если вызывать их по отдельности:

void processJob(int i)
{
    for(int iteration = 0; iteration < iterationCount; ++iteration)
    {
        doSomePreparation(i);
        std::vector<Subtask> subtasks = getSubtasks(i);
#pragma omp parallel for
        for(int j = 0; j < substasks.size(); ++j)
            subtasks[j].Process();
        doSomePostProcessing(i)
    }
}

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

Иногда есть очень большие задания, которые требуют много времени для обработки.Достаточно долго, чтобы все остальные потоки во внешнем цикле уже заканчивали работу до последнего потока и ничего не делали.Есть ли способ переназначить неиспользуемые потоки для распараллеливания внутреннего цикла, как только они закончатся?Я представляю что-то, что проверяет количество неиспользуемых потоков каждый раз, когда вводится внутренняя параллельная область.

Я не могу предсказать, как долго выполняется задание.Это может быть не только одна длительная работа - может быть, две или три.

1 Ответ

3 голосов
/ 27 марта 2019

Ваше описание проблемы больше похоже на задачи OpenMP. Ваш код будет выглядеть так:

void processAllJobs()
{
#pragma omp parallel master
    for(int i = 0; i < n; ++i) 
#pragma omp task
        processJob(i);
}

Тогда обработка задания будет выглядеть так:

void processJob(int i)
{
    for(int iteration = 0; iteration < iterationCount; ++iteration)
    {
        doSomePreparation(i);
        std::vector<Subtask> subtasks = getSubtasks(i);
#pragma omp taskloop   // add grainsize() clause, if Process() is very short
        for(int j = 0; j < substasks.size(); ++j)
            subtasks[j].Process();
        doSomePostProcessing(i)
    }
}

Таким образом вы получаете естественную балансировку нагрузки (при условии, что у вас достаточно задач), не полагаясь на вложенный параллелизм.

...