openMP的目标数据和目标数据之间的区别?

Pun*_*han 4 c c++ parallel-processing multithreading openmp

目标构造将代码区域从主机卸载到目标设备。变量pv1v2,明确地映射到使用在地图子句目标设备。目标数据也一样,

那么,这意味着:

  • “该构造创建的变量将在整个目标数据区域中持续存在”
  • “创建新设备数据环境”

关于“目标数据”结构,

我的意思是这些代码之间的卸载机制有什么区别:

void vec_mult1(float *p, float *v1, float *v2, int N)
{
    int i;
    init(v1, v2, N);
#pragma omp target map(to: v1[0:N], v2[:N]) map(from: p[0:N])
#pragma omp parallel for
    for (i=0; i<N; i++)
        p[i] = v1[i] * v2[i];
    output(p, N);
}


void vec_mult2(float *p, float *v1, float *v2, int N)
{
    int i;
    init(v1, v2, N);
#pragma omp target device(mic0) data map(to: v1[0:N], v2[:N]) map(from: p[0:N])
    {
    //this code runs on accelerator card
#pragma omp target //if we omit it what difference will it make ? 
#pragma omp parallel for
        for (i=0; i<N; i++)
            p[i] = v1[i] * v2[i];
    }
    output(p, N);
}

void vec_mult3(float *p, float *v1, float *v2, int N)
{
    int i;
    init(v1, v2, N);
#pragma omp target data map(to: v1[0:N], v2[:N]) map(from: p[0:N])
    {

        //target construct omitted
#pragma omp parallel for
        for (i=0; i<N; i++)
            p[i] = v1[i] * v2[i];
    }
    output(p, N);
}
Run Code Online (Sandbox Code Playgroud)

我尝试执行它们,但是我无法注意到它们之间的重大差异。

Hri*_*iev 5

target data构造仅创建一个持续该区域范围的设备数据环境。它仅设置设备数据环境中的变量与遇到任务的数据环境之间的映射。具有单独结构的背后原理是,在许多情况下,希望某些数据保留在设备上,而不是不断地在设备上传输和从设备传输。

想象以下非常人为的示例:

int data[N];

#pragma omp target
#pragma omp for
for (int i = 0; i < N; i++)
   data[i] *= 2;

// Do something else

#pragma omp target
#pragma omp for
for (int i = 0; i < N; i++)
   data[i] += 5;
Run Code Online (Sandbox Code Playgroud)

现在,在这种情况下,这两个target构造还创建了两个数据环境。该data变量被自动映射为tofrom。这意味着将发生以下一系列操作:

  1. data 复制到设备
  2. 第一个循环在设备上运行
  3. data 从设备复制
  4. 主机执行 // Do something else
  5. data 复制到设备
  6. 第二个循环在设备上运行
  7. data 从设备复制

现在想象一下,// Do something elsedata,但从来没有修改它。这使得data在步骤5 中向设备的传输变得多余-它可以仅保留在步骤2之后的状态。这就是target data构造起作用的地方。它允许您创建一个数据环境,该环境所涵盖的范围远远超过target构造范围。然后可以将上面的示例重写为:

int data[N];

#pragma omp target data map(tofrom: data)
{
   #pragma omp target
   #pragma omp for
   for (int i = 0; i < N; i++)
      data[i] *= 2;

   #pragma omp target update from(data)

   // Do something else

   #pragma omp target
   #pragma omp for
   for (int i = 0; i < N; i++)
      data[i] += 5;
}
Run Code Online (Sandbox Code Playgroud)

target在这种情况下,这些构造并不会创建新的设备数据环境,而是利用该target data构造所创建的环境(实际上,它们确实会创建新的数据环境,但是这些数据环境将与合并,target data并且它们不包含任何新引用)。因此,操作顺序为:

  1. data 复制到设备
  2. 第一个循环在设备上运行
  3. data 从设备显式复制
  4. 主机执行 // Do something else
  5. 第二个循环在设备上运行
  6. data 从设备复制

由于data需要,// Do something else但仅在target data构造结束时从设备自动传输,因此target update在第3步使用显式将其复制到遇到任务的数据环境中。

现在,这只是一个很小的,非常人为的示例,但是在现实生活中,节省不必要的数据传输可以显着提高OpenMP应用程序的性能,这些应用程序将计算工作卸载到协处理器和/或加速器。