2012-11-17 3 views
7
Node *head = &node1; 
while (head) 
{ 
    #pragma omp task 
     cout<<head->value<<endl; 
    head = head->next; 
} 

#pragma omp parallel 
{ 
    #pragma omp single 
    { 
     Node *head = &node1; 
     while (head) 
     { 
      #pragma omp task 
       cout<<head->value<<endl; 
      head = head->next; 
     } 
    } 
} 

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

Код на мой комментарий:

void traverse(node *root) 
{ 
    if (root->left) 
    { 
     #pragma omp task 
     traverse(root->left); 
    } 
    if (root->right) 
    { 
     #pragma omp task 
     traverse(root->right); 
    } 
    process(root); 
} 

ответ

12

Разница заключается в том, что в первом блоке вы не действительно создает каких-либо задач, так как сам блок не вложен (ни синтаксически, ни лексически) внутри активной параллельная область. Во втором блоке конструкция task синтаксически вложена внутри области parallel и будет ставить в очередь явные задачи, если регион будет активен во время выполнения (активная параллельная область - это область, которая выполняется с командой из более чем одного потока). Лексическое гнездование менее очевидно. Обратите внимание на следующий пример:

void foo(void) 
{ 
    int i; 

    for (i = 0; i < 10; i++) 
     #pragma omp task 
     bar(); 
} 

int main(void) 
{ 
    foo(); 

    #pragma omp parallel num_threads(4) 
    { 
     #pragma omp single 
     foo(); 
    } 

    return 0; 
} 

Первый вызов foo() происходит вне каких-либо параллельных областей. Следовательно, директива task делает (почти) ничего, и все звонки в bar() происходят серийно. Второй вызов foo() происходит изнутри параллельной области, и, следовательно, новые задачи будут генерироваться внутри foo(). Область parallel активна, так как число потоков было зафиксировано на 4 по статье num_threads(4).

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

Тем не менее наличие конструкции task в foo() выполняет некоторые преобразования кода, например. foo() превращаются в нечто вроде:

void foo_omp_fn_1(void *omp_data) 
{ 
    bar(); 
} 

void foo(void) 
{ 
    int i; 

    for (i = 0; i < 10; i++) 
     OMP_make_task(foo_omp_fn_1, NULL); 
} 

Здесь OMP_make_task() гипотетическая (не является общедоступной) функция из библиотеки поддержки OpenMP, что в очереди вызова функции, поставляемый в качестве первого аргумента. Если OMP_make_task() обнаруживает, что он работает вне активной параллельной области, вместо этого он просто вызывает foo_omp_fn_1(). Это добавляет некоторые накладные расходы на вызов bar() в серийном корпусе. Вместо main -> foo -> bar звонок идет как main -> foo -> OMP_make_task -> foo_omp_fn_1 -> bar. Следствием этого является более медленное выполнение серийного кода.

Это еще более очевидно, показано с директивой распараллеливание:

void foo(void) 
{ 
    int i; 

    #pragma omp for 
    for (i = 0; i < 12; i++) 
     bar(); 
} 

int main(void) 
{ 
    foo(); 

    #pragma omp parallel num_threads(4) 
    { 
     foo(); 
    } 

    return 0; 
} 

Первый вызов foo() будет запускать цикл в последовательный. Второй вызов распределил бы 12 итераций среди 4 потоков, т. Е. Каждый поток выполнил бы только 3 итератора. Еще раз для этого используется магия преобразования кода, и последовательный цикл будет работать медленнее, чем если бы #pragma omp for присутствовал в foo().

Урок здесь заключается в том, чтобы никогда не добавлять конструкции OpenMP, где они действительно не нужны.

+0

+1 Хороший ответ. – dreamcrash

+0

Кажется, я допустил ошибку при использовании задачи.Эта проблема возникла из-за того, что я видел код рекурсивного пересечения дерева только с «заданием», как я добавил в вопросе. Я предполагаю, что должны быть «параллельные» и «одиночные», охватывающие функцию перемещения, где она вызывается. Огромное спасибо вашему искреннему ответу. –

+0

@AnnieKim, да, функция 'traverse()', как показано в вашем вопросе, будет пересекать дерево параллельно, если вызывается из активной области 'parallel' и в противном случае в противном случае. Это красота OpenMP :) (несмотря на дополнительные накладные расходы) –

Смежные вопросы