openACC передает список структур

У меня есть программа на C, чтобы определить, перекрываются ли 2 набора полигонов. Пользователь вводит 2 набора полигонов (каждый набор данных содержит несколько тысяч полигонов), и программа видит, какой полигон в наборе 1 перекрывается с каким полигоном в наборе 2.

У меня есть 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 — это массив целых чисел.
mbr двух полигонов проверяется, чтобы увидеть, не перекрываются ли они, и функция «isRectOverlap» возвращает 1, если они перекрываются, 0, если нет, и помещает значение в listOfBoolean

Проблема
Код компилируется, но не запускается. Он возвращает следующую ошибку:

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" в "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;

}

У программы нет проблем, когда она не работает в OpenACC.

Спасибо за помощь


person dondonhk    schedule 05.08.2016    source источник
comment
Это CUDA? Укажите в тегах.   -  person Eugene Sh.    schedule 05.08.2016
comment
я так не думаю? У него нет кода CUDA. Извините, я новичок в параллельном программировании.   -  person dondonhk    schedule 05.08.2016
comment
Я вижу cuEventSynchronize, который для меня выглядит как cuda.... Ну, я могу ошибаться, так как я не знаком с этим OpenACC   -  person Eugene Sh.    schedule 05.08.2016
comment
OpenACC и CUDA имеют некоторую связь, поэтому они могут использовать что-то общее, но в любом случае спасибо за ваш интерес.   -  person dondonhk    schedule 05.08.2016


Ответы (2)


Когда агрегатные типы данных используются в предложении данных OpenACC, выполняется поверхностная копия типа. Скорее всего, здесь происходит то, что когда массивы listOfPolygons копируются на устройство, «mbr» будет содержать адреса узлов. Следовательно, программа выдаст ошибку недопустимого адреса при доступе к «mbr».

Учитывая, что в комментарии говорится, что «mbr» всегда будет равно 4, проще всего сделать «mbr» массивом фиксированного размера размером 4.

Предполагая, что вы используете компиляторы PGI с устройством NVIDIA, второй метод заключается в использовании унифицированной памяти CUDA путем компиляции "-ta=tesla:managed". Вся динамическая память будет обрабатываться средой выполнения CUDA и разрешать доступ к адресам хостов на устройстве. Предостережения в том, что он доступен только для динамических данных, вся ваша программа может использовать только столько памяти, сколько доступно на устройстве, и это может замедлить вашу программу. http://www.pgroup.com/lit/articles/insider/v6n2a4.htm

Третий вариант — выполнить глубокое копирование агрегатного типа на устройство. Я могу опубликовать пример, если вы решите пойти по этому пути. Я также рассказываю об этом в рамках презентации, которую я сделал на GTC2015: https://www.youtube.com/watch?v=rWLmZt_u5u4

person Mat Colgrove    schedule 05.08.2016
comment
Меня интересует третье решение. На самом деле я реализовал глубокую копию (если я сделал это правильно) для формирования listOfPolygons. Можете ли вы показать мне пример решения 3? Спасибо - person dondonhk; 06.08.2016

Вот упрощенный пример. Ключевым моментом является использование областей неструктурированных данных в тех же местах, где вы размещаете данные хоста. Сначала выделите массив структур и создайте или скопируйте массив на устройство. Здесь я просто создаю массив, чтобы данные устройства были мусором, но если бы я сделал копирование, то произошло бы поверхностное копирование, и адреса хостов для «mbr» были бы скопированы на устройство. Чтобы это исправить, нужно создать каждый «mbr» на устройстве. Затем компилятор назначит «присоединить» указатель устройства «mbr», перезаписав таким образом значение указателя мусора/хоста. Как только "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
person Mat Colgrove    schedule 08.08.2016
comment
Мат спасибо за пример. Для чего используется предложение private(B)? Я прочитал документ, и он сказал, что для каждой банды выделяется копия каждой переменной в списке. но я понятия не имею, что это значит. - person dondonhk; 08.08.2016
comment
Это означает, что каждый вектор получит свою собственную копию переменной. Если бы это была групповая петля, то у каждой банды будет частная копия, которая используется всеми векторами. По умолчанию массивы являются общими, а скаляры — частными. Таким образом, обычно B будет закрытым по умолчанию, однако, поскольку его член mbr передается подпрограмме, компилятор должен предположить, что на него могут быть другие ссылки, поэтому, чтобы быть в безопасности, делает его общим по умолчанию. Поэтому я поместил это в приватную оговорку. - person Mat Colgrove; 08.08.2016
comment
Мат большое спасибо!! Ваш пример действительно очень полезен! Теперь моя программа может успешно компилироваться и работать. Однако мой случай немного отличается: когда я выделяю данные, я не знаю, сколько элементов у меня будет, пока не пройдусь по всему входному файлу. Поэтому мой код продолжает перераспределять массив до тех пор, пока не закончится входной файл. В вашем коде вы предполагаете, что размер известен (в функции allocData). Таким образом, вы можете заранее создать весь массив в устройстве и позже обновить значения. Есть ли прагма openACC для моего случая? - person dondonhk; 09.08.2016
comment
Я бы прошел через файл, создав структуру данных на стороне хоста. Затем снова пройдитесь по структуре, строя структуру зеркального устройства. Нет необходимости связывать размещение на стороне хоста и устройства, мне это просто нравится для удобства. Вы можете сделать что-то похожее на realloc, создав временный массив на устройстве, используя acc_memcpy для копирования старых данных в новые, а затем удалив старый массив устройств. Однако, если вы не используете данные до тех пор, пока не просмотрите файл, не должно быть необходимости выполнять перераспределение на устройстве. - person Mat Colgrove; 09.08.2016