openACC 传递结构列表
openACC passing a list of struct
我有一个 C 程序来查找 2 组多边形是否重叠。用户输入2组多边形(每组数据有数千个多边形),程序查看set1中的哪个多边形与set2中的哪个多边形重叠
我有 2 个这样的结构:
struct gpc_vertex /* Polygon vertex */
{
double x;
double y;
};
struct gpc_vertex_list /* Polygon contour */
{
int pid; // polygon id
int num_vertices;
double *mbr; // minimum bounding rectangle of the polygon, so always 4 elements
};
我有以下代码段:
#pragma acc kernels copy(listOfPolygons1[0:polygonCount1], listOfPolygons2[0:polygonCount2], listOfBoolean[0:dump])
for (i=0; i<polygonCount1; i++){
polygon1 = listOfPolygons1[i];
for (j=0; j<polygonCount2; j++){
polygon2 = listOfPolygons2[j];
idx = polygonCount2 * i + j;
listOfBoolean[idx] = isRectOverlap(polygon1.mbr, polygon2.mbr); // line 115
}
}
listOfPolygons1 和 listOfPolygons2(顾名思义)是 gpc_vertex_list 的数组。
listOfBoolean 是一个 int 数组。
检查 2 个多边形的 mbr 以查看它们是否重叠,函数 "isRectOverlap" return 如果重叠则为 1,如果不重叠则为 0 并将值放入 listOfBoolean
问题
代码可以编译但不能运行。它 return 出现以下错误:
call to cuEventSynchronize returned error 700: Illegal address during kernel execution
我的观察
该程序可以通过将第 115 行更改为以下内容来编译和 运行:
isRectOverlap(polygon1.mbr, polygon2.mbr); // without assigning value to listOfBoolean
或者这个:
listOfBoolean[idx] = 5; // assigning an arbitrary value
(虽然结果不对,但至少,可以运行)
问题
如果值没有从 "isRectOverlap" 传递到 "listOfBoolean"
,"isRectOverlap" 和 "listOfBoolean" 似乎都不会产生问题
有谁知道如果我将 "isRectOverlap" 中的 return 值分配给 "listOfBoolean" 为什么它不能 运行?
isRectOverlap函数是这样的:
int isRectOverlap(double *shape1, double *shape2){
if (shape1[0] > shape2[2] || shape2[0] > shape1[2]){
return 0;
}
if (shape1[1] < shape2[3] || shape2[1] < shape1[3]){
return 0;
}
return 1;
}
不运行ning在OpenACC
下程序没有问题
感谢您的帮助
当在 OpenACC 数据子句中使用聚合数据类型时,将执行该类型的浅表复制。此处最有可能发生的情况是,当将 listOfPolygons 数组复制到设备时,"mbr" 将包含主机地址。因此,程序在访问"mbr"时会报非法地址错误。
鉴于评论说 "mbr" 将始终为 4,最简单的做法是使 "mbr" 成为大小为 4 的固定大小数组。
假设您在 NVIDIA 设备上使用 PGI 编译器,第二种方法是通过编译“-ta=tesla:managed”来使用 CUDA 统一内存。所有动态内存都将由 CUDA 运行时处理,并允许在设备上访问主机地址。需要注意的是它只适用于动态数据,你的整个程序只能使用设备上可用的内存,这可能会减慢你的程序。 http://www.pgroup.com/lit/articles/insider/v6n2a4.htm
第三种选择是对设备执行聚合类型的深层复制。如果你决定走这条路,我可以 post 举个例子。我还在 GTC2015 做的演讲中谈到了这个主题:https://www.youtube.com/watch?v=rWLmZt_u5u4
这是一个简化的例子。关键是在分配主机数据的相同位置使用非结构化数据区域。首先分配结构数组,然后创建或复制数组到设备。在这里,我只是创建了一个数组,所以设备数据是垃圾,但如果我做了一个复制,那么就会发生一个浅拷贝,"mbr" 的主机地址将被复制到设备。要解决此问题,您需要在设备上创建每个 "mbr"。然后,编译器将分配 "attach" 设备 "mbr" 指针,从而覆盖 garbage/host 指针值。一旦 "mbr" 具有有效的设备指针,就可以在设备上引用它们。
% cat example_struct.c
#include <stdlib.h>
#include <stdio.h>
#ifndef N
#define N 1024
#endif
typedef struct gpc_vertex_list
{
int pid; // polygon id
int num_vertices;
double *mbr; // minimum bounding rectangle of the polygon, so always 4 elements
} gpc_vertex_list;
gpc_vertex_list * allocData(size_t size);
int deleteData(gpc_vertex_list * A, size_t size);
int initData(gpc_vertex_list *Ai, size_t size);
#pragma acc routine seq
int isRectOverlap(double * mbr) {
int result;
result = mbr[0];
result += mbr[1];
result += mbr[2];
result += mbr[3];
return result;
}
int main() {
gpc_vertex_list *A;
gpc_vertex_list B;
size_t size, i;
int * listOfBoolean;
size = N;
A=allocData(size);
initData(A,size);
listOfBoolean = (int*) malloc(sizeof(int)*size);
#pragma acc parallel loop present(A) copyout(listOfBoolean[0:size]) private(B)
for (i=0; i<size; i++){
B = A[i];
listOfBoolean[i] = isRectOverlap(B.mbr);
}
printf("result: %d %d %d\n",listOfBoolean[0], listOfBoolean[size/2], listOfBoolean[size-1]);
free(listOfBoolean);
deleteData(A, size);
exit(0);
}
gpc_vertex_list * allocData(size_t size) {
gpc_vertex_list * tmp;
tmp = (gpc_vertex_list *) malloc(size*sizeof(gpc_vertex_list));
/* Create the array on device. */
#pragma acc enter data create(tmp[0:size])
for (int i=0; i< size; ++i) {
tmp[i].mbr = (double*) malloc(sizeof(double)*4);
/* create the member array on the device */
#pragma acc enter data create(tmp[i].mbr[0:4])
}
return tmp;
}
int deleteData(gpc_vertex_list * A, size_t size) {
/* Delete the host copy. */
for (int i=0; i< size; ++i) {
#pragma acc exit data delete(A[i].mbr)
free(A[i].mbr);
}
#pragma acc exit data delete(A)
free(A);
}
int initData(gpc_vertex_list *A ,size_t size) {
size_t i;
for (int i=0; i< size; ++i) {
A[i].pid = i;
A[i].num_vertices = 4;
for (int j=0; j<4;++j) {
A[i].mbr[j]=(i*4)+j;
}
#pragma acc update device(A[i].pid,A[i].num_vertices,A[i].mbr[0:4])
}
}
% pgcc example_struct.c -acc -Minfo=accel
isRectOverlap:
20, Generating acc routine seq
main:
39, Generating copyout(listOfBoolean[:size])
Generating present(A[:])
Accelerator kernel generated
Generating Tesla code
40, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */
39, Local memory used for B
allocData:
55, Generating enter data create(tmp[:size])
59, Generating enter data create(tmp->mbr[:4])
deleteData:
67, Generating exit data delete(A->mbr[:1])
70, Generating exit data delete(A[:1])
initData:
83, Generating update device(A->mbr[:4],A->pid,A->num_vertices)
% a.out
result: 6 8198 16374
我有一个 C 程序来查找 2 组多边形是否重叠。用户输入2组多边形(每组数据有数千个多边形),程序查看set1中的哪个多边形与set2中的哪个多边形重叠
我有 2 个这样的结构:
struct gpc_vertex /* Polygon vertex */
{
double x;
double y;
};
struct gpc_vertex_list /* Polygon contour */
{
int pid; // polygon id
int num_vertices;
double *mbr; // minimum bounding rectangle of the polygon, so always 4 elements
};
我有以下代码段:
#pragma acc kernels copy(listOfPolygons1[0:polygonCount1], listOfPolygons2[0:polygonCount2], listOfBoolean[0:dump])
for (i=0; i<polygonCount1; i++){
polygon1 = listOfPolygons1[i];
for (j=0; j<polygonCount2; j++){
polygon2 = listOfPolygons2[j];
idx = polygonCount2 * i + j;
listOfBoolean[idx] = isRectOverlap(polygon1.mbr, polygon2.mbr); // line 115
}
}
listOfPolygons1 和 listOfPolygons2(顾名思义)是 gpc_vertex_list 的数组。
listOfBoolean 是一个 int 数组。
检查 2 个多边形的 mbr 以查看它们是否重叠,函数 "isRectOverlap" return 如果重叠则为 1,如果不重叠则为 0 并将值放入 listOfBoolean
问题
代码可以编译但不能运行。它 return 出现以下错误:
call to cuEventSynchronize returned error 700: Illegal address during kernel execution
我的观察
该程序可以通过将第 115 行更改为以下内容来编译和 运行:
isRectOverlap(polygon1.mbr, polygon2.mbr); // without assigning value to listOfBoolean
或者这个:
listOfBoolean[idx] = 5; // assigning an arbitrary value
(虽然结果不对,但至少,可以运行)
问题
如果值没有从 "isRectOverlap" 传递到 "listOfBoolean"
,"isRectOverlap" 和 "listOfBoolean" 似乎都不会产生问题
有谁知道如果我将 "isRectOverlap" 中的 return 值分配给 "listOfBoolean" 为什么它不能 运行?
isRectOverlap函数是这样的:
int isRectOverlap(double *shape1, double *shape2){
if (shape1[0] > shape2[2] || shape2[0] > shape1[2]){
return 0;
}
if (shape1[1] < shape2[3] || shape2[1] < shape1[3]){
return 0;
}
return 1;
}
不运行ning在OpenACC
下程序没有问题感谢您的帮助
当在 OpenACC 数据子句中使用聚合数据类型时,将执行该类型的浅表复制。此处最有可能发生的情况是,当将 listOfPolygons 数组复制到设备时,"mbr" 将包含主机地址。因此,程序在访问"mbr"时会报非法地址错误。
鉴于评论说 "mbr" 将始终为 4,最简单的做法是使 "mbr" 成为大小为 4 的固定大小数组。
假设您在 NVIDIA 设备上使用 PGI 编译器,第二种方法是通过编译“-ta=tesla:managed”来使用 CUDA 统一内存。所有动态内存都将由 CUDA 运行时处理,并允许在设备上访问主机地址。需要注意的是它只适用于动态数据,你的整个程序只能使用设备上可用的内存,这可能会减慢你的程序。 http://www.pgroup.com/lit/articles/insider/v6n2a4.htm
第三种选择是对设备执行聚合类型的深层复制。如果你决定走这条路,我可以 post 举个例子。我还在 GTC2015 做的演讲中谈到了这个主题:https://www.youtube.com/watch?v=rWLmZt_u5u4
这是一个简化的例子。关键是在分配主机数据的相同位置使用非结构化数据区域。首先分配结构数组,然后创建或复制数组到设备。在这里,我只是创建了一个数组,所以设备数据是垃圾,但如果我做了一个复制,那么就会发生一个浅拷贝,"mbr" 的主机地址将被复制到设备。要解决此问题,您需要在设备上创建每个 "mbr"。然后,编译器将分配 "attach" 设备 "mbr" 指针,从而覆盖 garbage/host 指针值。一旦 "mbr" 具有有效的设备指针,就可以在设备上引用它们。
% cat example_struct.c
#include <stdlib.h>
#include <stdio.h>
#ifndef N
#define N 1024
#endif
typedef struct gpc_vertex_list
{
int pid; // polygon id
int num_vertices;
double *mbr; // minimum bounding rectangle of the polygon, so always 4 elements
} gpc_vertex_list;
gpc_vertex_list * allocData(size_t size);
int deleteData(gpc_vertex_list * A, size_t size);
int initData(gpc_vertex_list *Ai, size_t size);
#pragma acc routine seq
int isRectOverlap(double * mbr) {
int result;
result = mbr[0];
result += mbr[1];
result += mbr[2];
result += mbr[3];
return result;
}
int main() {
gpc_vertex_list *A;
gpc_vertex_list B;
size_t size, i;
int * listOfBoolean;
size = N;
A=allocData(size);
initData(A,size);
listOfBoolean = (int*) malloc(sizeof(int)*size);
#pragma acc parallel loop present(A) copyout(listOfBoolean[0:size]) private(B)
for (i=0; i<size; i++){
B = A[i];
listOfBoolean[i] = isRectOverlap(B.mbr);
}
printf("result: %d %d %d\n",listOfBoolean[0], listOfBoolean[size/2], listOfBoolean[size-1]);
free(listOfBoolean);
deleteData(A, size);
exit(0);
}
gpc_vertex_list * allocData(size_t size) {
gpc_vertex_list * tmp;
tmp = (gpc_vertex_list *) malloc(size*sizeof(gpc_vertex_list));
/* Create the array on device. */
#pragma acc enter data create(tmp[0:size])
for (int i=0; i< size; ++i) {
tmp[i].mbr = (double*) malloc(sizeof(double)*4);
/* create the member array on the device */
#pragma acc enter data create(tmp[i].mbr[0:4])
}
return tmp;
}
int deleteData(gpc_vertex_list * A, size_t size) {
/* Delete the host copy. */
for (int i=0; i< size; ++i) {
#pragma acc exit data delete(A[i].mbr)
free(A[i].mbr);
}
#pragma acc exit data delete(A)
free(A);
}
int initData(gpc_vertex_list *A ,size_t size) {
size_t i;
for (int i=0; i< size; ++i) {
A[i].pid = i;
A[i].num_vertices = 4;
for (int j=0; j<4;++j) {
A[i].mbr[j]=(i*4)+j;
}
#pragma acc update device(A[i].pid,A[i].num_vertices,A[i].mbr[0:4])
}
}
% pgcc example_struct.c -acc -Minfo=accel
isRectOverlap:
20, Generating acc routine seq
main:
39, Generating copyout(listOfBoolean[:size])
Generating present(A[:])
Accelerator kernel generated
Generating Tesla code
40, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */
39, Local memory used for B
allocData:
55, Generating enter data create(tmp[:size])
59, Generating enter data create(tmp->mbr[:4])
deleteData:
67, Generating exit data delete(A->mbr[:1])
70, Generating exit data delete(A[:1])
initData:
83, Generating update device(A->mbr[:4],A->pid,A->num_vertices)
% a.out
result: 6 8198 16374