Как сделать код производителя-потребителя с OpenMP? - PullRequest
0 голосов
/ 06 февраля 2019

Я пытаюсь заставить один поток (мастер) в коде OpenMP C непрерывно работать в одной операции (которая должна создавать задачи), в то время как другие (ведомые) ждут, пока задачи, созданные мастером,работа над.Можно ли заставить главный поток работать только для его конкретной задачи, не назначая для этого другие задачи?

Цель этого состоит в том, чтобы иметь один поток, постоянно взаимодействующий с внешним устройством исоздание задач для выполнения другими потоками.

Вот пример того, что я пытаюсь:

#pragma omp parallel
{
    #pragma omp master
    {
        printf("MASTER START\n"); fflush(stdout);
        for(int i = 0; i < 1000; ++i) {
            #pragma omp task
            {
                printf("[Thread %d] working on task %d\n", omp_get_thread_num(), i);
                fflush(stdout);
            }
        }
        printf("MASTER END\n"); fflush(stdout);
    }
}

Выполняя код выше с двумя потоками, я получаю что-то вроде этого:

MASTER START
...
[Thread 1] working on task 998
[Thread 0] working on task 999
...
MASTER END
[Thread 1] working on task 694
[Thread 0] working on task 696
...

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

Примечание: я знаю, что спецификация OpenMP гласит, что задачи могут выполняться правильно по мере их создания (создателем) вместо перехода в очередь.Поэтому, возможно, я не могу использовать директивы задач для достижения желаемого результата?Есть ли другой способ сделать это с OpenMP?

РЕДАКТИРОВАТЬ

Просто чтобы уточнить, почему я хочу, чтобы основной поток НЕ работал над задачами: Главный поток, в моемприложение, отвечает за отправку задач в GPU и получение результатовНекоторая часть работы, которая не выполняется в GPU, заключается в том, что они делегируются потокам CPU (на данный момент в форме задач omp).Если главный поток начинает работать над задачами ЦП, они не будут взаимодействовать с графическим процессором, и графический процессор будет простаивать.

Используемый мной компилятор - nvcc 10.0.130 и gcc 8.2.1

1 Ответ

0 голосов
/ 07 февраля 2019

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

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

OpenMP 4.5 дает вам один из способов сообщить компилятору, что вы хотите - приоритеты задач.т.е.

#pragma omp single
{
    #pragma omp task priority(10)
    {
        printf("MASTER START\n");
        fflush(stdout);
        for (int i = 0; i < 1000; ++i)
        {
            #pragma omp task priority(0)
            {
                    printf("[Thread %d] working on task %d\n", omp_get_thread_num(), i);

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

Свобода, предоставляемая стандартом, также позволяет ленивые имперментации .Например, libgomp (библиотека OpenMP для gcc) имеет жестко запрограммированную логику для немедленного выполнения всех порожденных задач, если в потоке имеется более 64 задач в очереди.Я не верю, что приоритет имеет значение в решении отложить задачу или нет, только для задач в очереди.

Что вы можете наблюдать, используя что-то вроде этого:

int task_count = 1;
#pragma omp parallel
{
    #pragma omp single
    {
        printf("MASTER START (%d)\n", omp_get_thread_num());
        fflush(stdout);
        for (int i = 0; i < 1000; ++i)
        {
            #pragma omp atomic
            task_count++;
            #pragma omp task
            {
                int q;
                #pragma omp atomic capture
                q = task_count--;
                printf("[Thread %d] working on task %d (%d queued)\n", omp_get_thread_num(), i, q);

Вы можете использоватьэта информация для предотвращения нерестовых задач в первую очередь, если в полете уже слишком много задач.Теперь вы говорите, не имеет значения, если задачи процессора не выполняются.Я сомневаюсь, что как-то они должны в конечном итоге завершить.Таким образом, вы все равно можете избежать слишком большого количества задач процессора.К сожалению, в результате вы получаете решение для конкретной реализации, основанное на понимании того, как работает эта реализация.С другой стороны, это, вероятно, также поможет с другой основной библиотекой OpenMP, используемой Intel / clang.Вы должны выяснить, является ли это лучшим решением, чем развертывание собственного управления очередью задач - будь то OpenMP, pthreads, tbb или что-то еще.

...