目标构造将代码区域从主机卸载到目标设备。
变量 p
、v1
、v2
使用 map 子句显式映射到目标设备。
目标数据也做同样的事情,
那么 :
- “该构造创建了将在整个过程中持续存在的变量 目标数据区域"
- “新设备数据环境创建”
关于“目标数据”构造,
我的意思是这些代码在卸载机制上有什么区别:
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);
}
我尝试执行它们,但我无法注意到它们之间的显着差异。
最佳答案
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;
现在在那种情况下,两个 target
构造也创建了两个数据环境。 data
变量自动映射为 tofrom
。这意味着会发生以下一组操作:
数据
被复制到设备- 第一个循环在设备上运行
data
从设备复制- 宿主执行
//Do something else
数据
被复制到设备- 第二个循环在设备上运行
data
从设备复制
现在假设 //Do something else
读取 data
但从不修改它。这使得在第 5 步中将 data
传输到设备变得多余 - 它可以保留在第 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;
}
在这种情况下,target
构造不会创建新的设备数据环境,而是利用由 target data
构造创建的环境(实际上它们确实创建了新的数据环境,但它们与来自 target data
的数据合并,并且它们不包含任何新引用)。所以操作顺序是:
数据
被复制到设备- 第一个循环在设备上运行
data
明确地从设备复制- 宿主执行
//Do something else
- 第二个循环在设备上运行
data
从设备复制
因为 data
在 //Do something else
中需要,但它只会在 target data
末尾自动从设备传输构造,显式目标更新
用于在第 3 步将其复制到遇到的任务的数据环境中。
现在这只是一个很小且非常人为的示例,但在现实生活中,节省不必要的数据传输可以显着提高将计算卸载到协处理器和/或加速器的 OpenMP 应用程序的性能。
关于c++ - openMP 的目标和目标数据之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22247855/