工作共享循环中创建的所有任务是否都在 OpenMP 中的并行区域同级任务中构建?

Are all tasks that are created in worksharing loop constructs inside a parallel region sibling tasks in OpenMP?

我有一个非常基本的模板应用程序的简单独立示例,用于处理 OpenMP 任务和依赖项。在 2 个步骤中,一个数组的一个位置添加了另一个数组的 3 个值,一个来自相应位置及其左右邻居。为了避免数据竞争,我设置了依赖关系,以便对于第二次更新的每个部分,只有在执行了第一个更新步骤的部分的相关任务时,才能安排其任务。我得到了预期的结果,但我不确定我的假设是否正确,因为这些任务可能会立即由遇到的线程执行而不是产生。所以我的问题是,在工作共享中创建的任务是否循环了所有同级任务,因此是否保留了依赖关系,就像在 single 构造中生成任务时一样。


#include <iostream>
#include <omp.h>
#include <math.h>

typedef double value_type;
int main(int argc, char * argv[]){

    std::size_t size = 100000;
    std::size_t part_size = 25;

    std::size_t parts = ceil(float(size)/part_size);
    std::size_t num_threads = 4;

    value_type * A = (value_type *) malloc(sizeof(value_type)*size);
    value_type * B = (value_type *) malloc(sizeof(value_type)*size);
    value_type * C = (value_type *) malloc(sizeof(value_type)*size);


    for (int i = 0; i < size; ++i) {
        A[i] = 1;
        B[i] = 1;
        C[i] = 0;
    }


#pragma omp parallel num_threads(num_threads)
{

    #pragma omp for schedule(static)
        for(int part=0; part<parts; part++){
            std::size_t current_part = part * part_size;
            std::size_t left_part = part != 0 ? (part-1)*part_size : current_part;
            std::size_t right_part = part != parts-1 ? (part+1)*part_size : current_part;
            std::size_t start = current_part;
            std::size_t end = part == parts-1 ? size-1 : start+part_size;
            if(part==0) start = 1;

            #pragma omp task depend(in: A[current_part], A[left_part], A[right_part]) depend(out: B[current_part])
            {
                for(int i=start; i<end; i++){
                    B[i] += A[i] + A[i-1] + A[i+1];
                }
            }
        }
    #pragma omp for schedule(static)
        for(int part=0; part<parts; part++){
            int current_part = part * part_size;
            std::size_t left_part = part != 0 ? (part-1)*part_size : current_part;
            std::size_t right_part = part != parts-1 ? (part+1)*part_size : current_part;
            std::size_t start = current_part;
            std::size_t end = part == parts-1 ? size-1 : start+part_size;
            if(part==0) start = 1;
            #pragma omp task depend(in: B[current_part], B[left_part], B[right_part]) depend(out: C[current_part])
            {
                for(int i=start; i<end; i++){
                    C[i] += B[i] + B[i-1] + B[i+1];
                }
            }
        }
}



    value_type sum = 0;
    value_type max = -1000000000000;
    value_type min =  1000000000000;
    for(int i = 0; i < size; i++){
        sum+=C[i];
        if(C[i]<min) min = C[i];
        if(C[i]>max) max = C[i];
    }
    std::cout << "sum: " << sum << std::endl;
    std::cout << "min: " << min << std::endl;
    std::cout << "max: " << max << std::endl;
    std::cout << "avg: " << sum/(size) << std::endl;
    return 0;
}



OpenMP specification中可以找到对应的定义:

sibling tasks - Tasks that are child tasks of the same task region.

child task - A task is a child task of its generating task region. A child task region is not part of its generating task region.

task region - A region consisting of all code encountered during the execution of a task. COMMENT: A parallel region consists of one or more implicit task regions

并行构造的描述中,您可以读到:

A set of implicit tasks, equal in number to the number of threads in the team, is generated by the encountering thread. The structured block of the parallel construct determines the code that will be executed in each implicit task.

这实际上意味着在平行区域中生成了许多任务区域并使用#pragma omp for不同的任务区域 将生成明确的任务(即#pragma omp task...)。但是,只有由同一个任务区域生成的任务同级任务(不是全部!) .如果您想确保所有生成的 任务 都是 同级任务 ,您必须使用单个 任务区域 (例如使用single构造)生成所有明确的任务

请注意,您的代码给出了正确的结果,因为在 worksharing-loop 构造 (#pragma omp for) 的末尾有一个隐式屏障。要移除此障碍,您必须使用 nowait 子句,您会发现在这种情况下结果将不正确。

另一条评论是,在您的情况下,工作负载小于并行开销,因此我猜测您的并行代码会比串行代码慢。