OpenMP 4.0 - GCC 5.2.0 - Перекрытие выполнения задач устройства и хоста

Я пытаюсь протестировать очень простую программу, которая использует возможности разгрузки gcc 5 через директивы OpenMP 4.0. Моя цель — написать программу с двумя независимыми задачами, при этом одна задача выполняется на ускорителе (например, эмуляторе Intel MIC), а другая — одновременно на процессоре.

Вот код:

#include <omp.h>
#include <stdio.h>

#define limit 100000

int main(int argc, char** argv)
{
    int cpu_prime, acc_prime;

    #pragma omp task shared(acc_prime)
    {
            #pragma omp target map(tofrom: acc_prime)
            {
                    printf("mjf-dbg >> acc computation\n");
                    int i, j;
                    acc_prime=0;
                    for(i=0; i<limit; i++){
                            for(j=2; j<=i; j++){
                                    if(i%j==0)
                                            break;
                            }
                            if(j==i)
                                    acc_prime = i;
                    }
                    printf("mjf-dbg << acc computation\n");
            }
    }

    #pragma omp task shared(cpu_prime)
    {
            int i, j;
            cpu_prime=0;
            printf("mjf-dbg >> cpu computation\n");
            for(i=0; i<limit; i++){
                    for(j=2; j<=i; j++){
                            if(i%j==0)
                                    break;
                    }
                    if(j==i)
                            cpu_prime = i;
            }
            printf("mjf-dbg << cpu computation\n");
    }

    #pragma omp taskwait

    printf("cpu prime: %d \n", cpu_prime);
    printf("gpu prime: %d \n", acc_prime);

}

С этим кодом я ожидал следующего потока выполнения:

  1. Главный поток (MT) встречает первую явную область задачи, привязывается к этой задаче и начинает ее выполнение.
  2. Обнаружив целевую директиву, MT перегружает целевой блок в ускоритель и достигает точки планирования.
  3. MT возвращаются в область неявной задачи
  4. MT встречает вторую явную область задачи, привязывается к этой задаче и начинает ее выполнение.
  5. MT выполняет вычисления на хосте параллельно с вычислениями, выгруженными на ускорительное устройство.
  6. MT возвращаются в область неявной задачи и достигают точки планирования, вызванной директивой taskwait.
  7. MT возвращаются к первой области явных задач, ожидая окончания выгруженного блока.

Скомпилировать и запустить:

gcc -fopenmp -foffload="-march=knl" overlap.c -o overlap
OFFLOAD_EMUL_RUN="sde -knl --" ./overlap

Выход:

mjf-dbg >> acc computation
mjf-dbg << acc computation
mjf-dbg >> cpu computation
mjf-dbg << cpu computation
cpu prime: 99991 
gpu prime: 99991

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

mjf-dbg >> acc computation
mjf-dbg >> cpu computation
mjf-dbg << cpu computation
mjf-dbg << acc computation
cpu prime: 99991 
gpu prime: 99991

Эмулятор разгрузки работает правильно, так как во время выполнения я вижу, как процесс _offload_target переходит на 100% использование ЦП, когда программа выполняет целевой блок.

Итак, вопрос: кто-нибудь знает, почему две задачи сериализуются, а не выполняются параллельно (одна в хост-процессе, а другая в процессе эмуляции _offload_target)??


person MaJac89    schedule 19.08.2015    source источник
comment
Я предполагаю, что это проблема реализации. Возможно, вы захотите сообщить об этом как об ошибке в GCC.   -  person Jeff Hammond    schedule 19.08.2015
comment
@Jeff, спасибо за комментарий, я подумаю о том, чтобы сообщить об ошибке, если никто не сможет объяснить мне поведение, которое я наблюдаю. Сможете ли вы запустить этот простой тест на реальном оборудовании (возможно, это проблема эмуляции)? Хотел бы я это сделать, но сейчас у меня нет доступа к машине с настоящим Intel MIC. Спасибо   -  person MaJac89    schedule 19.08.2015
comment
Почему вы не можете запустить его на KNC? Многие из этих машин доступны для пользователей.   -  person Jeff Hammond    schedule 19.08.2015
comment
Да, я хотел бы сделать это, если бы у меня был настоящий KNC, пока что я должен придерживаться эмулятора, потому что у меня нет реального оборудования для тестирования моего кода :(   -  person MaJac89    schedule 20.08.2015
comment
@Jeff GCC не поддерживает генерацию кода для KNC.   -  person Ilya Verbin    schedule 20.08.2015
comment
@IlyaVerbin Вы определенно правы, мне понадобится KNL, чтобы протестировать код на реальном оборудовании.   -  person MaJac89    schedule 21.08.2015
comment
Это не совсем так. Цепочка инструментов GNU/Linux для KNC построена с помощью GCC. KNC поддерживает расширения Pentium x86 plus. Союз KNC и Xeon ISA по-прежнему x86. Конечно, GCC не поддерживает KNC VPU, но вы можете использовать стандартизацию C ABI для добавления кода, созданного с помощью ICC, в программы GCC.   -  person Jeff Hammond    schedule 21.08.2015
comment
И поскольку одной из лучших особенностей KNL является то, что он доступен как самостоятельный сокет, я не уверен, почему вы так сосредоточены на разгрузке KNL с помощью GCC. Чего вы действительно хотите достичь?   -  person Jeff Hammond    schedule 21.08.2015
comment
@Jeff Вы говорите о специальной версии GCC в MPSS. Однако GCC 5.2.0, trunk и все остальные версии не поддерживают -march=knc.   -  person Ilya Verbin    schedule 22.08.2015
comment
@IlyaVerbin GCC не поддерживает Pentium x86, который работает на KNC?   -  person Jeff Hammond    schedule 22.08.2015
comment
@Jeff Вы не можете просто скомпилировать программу с -march=pentium и запустить ее на KNC, потому что KNC требует специального значения e_machine в заголовке ELF (EM_K1OM).   -  person Ilya Verbin    schedule 23.08.2015


Ответы (1)


Здесь есть более фундаментальная (и более простая) проблема, чем разгрузка — ваши задачи не находятся в параллельном регионе.

Задачи OpenMP должны находиться в параллельном регионе, хотя обычно они затем встраиваются в omp single.

Итак, это:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv)
{

#pragma omp task 
    {
        printf("task 1 starts\n");
        sleep(3);
        printf("task 1 ends\n");
    }

#pragma omp task 
    {
        printf("task 2 starts\n");
        sleep(1);
        printf("task 2 ends\n");
    }

    return 0;
}

Запускает задачи последовательно:

$ gcc -fopenmp brokentasks.c -o brokentasks
$ export OMP_NUM_THREADS=2

$ ./brokentasks 
task 1 starts
task 1 ends
task 2 starts
task 2 ends

В то время как размещение задач в параллельной области выглядит следующим образом:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv)
{

#pragma omp parallel
#pragma omp single
    {
#pragma omp task 
        {
            printf("task 1 starts\n");
            sleep(3);
            printf("task 1 ends\n");
        }

#pragma omp task 
        {
            printf("task 2 starts\n");
            sleep(1);
            printf("task 2 ends\n");
        }
    }

}

Работает как положено

$ gcc -fopenmp tasks.c -o tasks
jdursi@odw-jdursi:~/tmp$ ./tasks
task 2 starts
task 1 starts
task 2 ends
task 1 ends
person Jonathan Dursi    schedule 19.08.2015
comment
Спасибо за ответ, но я бы сказал, что он на самом деле не отвечает на мой вопрос, который касался разгрузки. Конечно, если я поставлю свои задачи в параллельный регион как минимум с двумя потоками, тогда все пойдет более или менее так, как ожидалось. Более менее! Действительно, каждый поток выполняет задачу, поэтому я вижу ожидаемый результат, но все же поток, выполняющий задачу разгрузки, ожидает, пока устройство завершит свою работу. Это неожиданно для меня, поток должен достичь точки планирования сразу после целевой директивы. В противном случае мне всегда нужно удвоить количество потоков для перекрытия задач устройства/хоста. - person MaJac89; 20.08.2015
comment
@MaJac89 MaJac89 - Да, ваш вопрос касается разгрузки, но вы используете для этого OpenMP, и прагмы задачи будут игнорироваться за пределами параллельного региона; разгрузка не меняет этого. В частности, для разгрузки это встречающая задача, которая выполняет целевое предложение; должен быть поток, связанный с целевой конструкцией. - person Jonathan Dursi; 20.08.2015
comment
Спасибо, попробую еще раз (уже пробовал заключить все в параллельную область и результат такой: с 1 потоком поведение такое же, как и с последовательным выполнением, с 2 потоками каждый поток выполняет задачу, но поток, выполняющий часть разгрузки, ждет, пока устройство не завершит свою работу, и это то, чего я не могу понять). Однако я отредактирую код, как только смогу, чтобы лучше объяснить свою точку зрения. - person MaJac89; 20.08.2015
comment
@MaJac89 MaJac89 OpenMP 4.0 говорит, что задача, которая встречает целевую конструкцию, ожидает в конце конструкции, пока не завершится выполнение области. Похоже, вам нужен OpenMP 4.1 с пунктом omp target nowait. - person Ilya Verbin; 20.08.2015
comment
@IlyaVerbin Да, я понимаю это, но позже в спецификациях OpenMP 4.0, когда они напишут о планировании задач, вы можете прочитать: точки планирования задач подразумеваются в следующих местах: [...] точка сразу после генерации целевого региона. Поэтому я ожидаю, что поток, сталкивающийся с целевой областью, приостанавливает выполнение своей текущей задачи, переключаясь на выполнение другой (если доступна другая независимая задача). Кроме того, взгляните на эти ссылки. 1 2 3 и найдите "асинхронный". - person MaJac89; 21.08.2015