Правильный способ использования функций на основе CUDA в контексте ROS - PullRequest
0 голосов
/ 02 мая 2018

Я работаю над конвейером на основе ROS, основной функцией которого является подписка на темы изображений и непрерывное выполнение таких функций, как обнаружение функций, сопоставление и т. Д. Чтобы ускорить этот конвейер, я пытаюсь использовать модули обнаружения и сопоставления на основе CUDA как часть моего пакета. В контексте этого вопроса я предполагаю простой конвейер, где я подписываюсь на тему изображения, и в обратном вызове подписчика, который вызывается каждый раз, когда изображение доступно, вызываются две функции-члена разных классов: одна для обнаружения, другая чтобы соответствовать, каждое из которых содержит свое собственное ядро ​​CUDA. Несколько похоже на выполнение этих двух функций в цикле.

Первая функция берет данные изображения и вычисляет ключевые точки и дескрипторы функций, возвращая их хосту. Затем я копирую эти дескрипторы обратно в память графического процессора, и мне нужно сопоставить их с другим набором дескрипторов, которые принадлежат эталонному изображению.

Например, вот так выглядит автономная функция сопоставления:

// Detection module returns a struct featureData, containing keypoints and descriptors 
// in featureData.kps and featureData.desc

uint64_t* d_desc;
cudaMalloc(&d_desc, 64 * featureData.kps.size());
cudaMemcpy(d_desc, &featureData., 64 * (featureData.kps.size()), cudaMemcpyHostToDevice);

cudaDeviceSetCacheConfig(cudaFuncCachePreferL1);
cudaDeviceSetSharedMemConfig(cudaSharedMemBankSizeEightByte);

// Create texture object for descriptors

struct cudaResourceDesc resDesc;
memset(&resDesc, 0, sizeof(resDesc));
resDesc.resType = cudaResourceTypeLinear;
resDesc.res.linear.devPtr = d_desc;
resDesc.res.linear.desc.f = cudaChannelFormatKindUnsigned;
resDesc.res.linear.desc.x = 32;
resDesc.res.linear.desc.y = 32;
resDesc.res.linear.sizeInBytes = 64 * featureData.kps.size();

struct cudaTextureDesc texDesc;
memset(&texDesc, 0, sizeof(texDesc));
texDesc.addressMode[0] = cudaAddressModeBorder;
texDesc.addressMode[1] = cudaAddressModeBorder;
texDesc.filterMode = cudaFilterModePoint;
texDesc.readMode = cudaReadModeElementType;
texDesc.normalizedCoords = 0;
cudaTextureObject_t tex_q = 0;
cudaCreateTextureObject(&tex_q, &resDesc, &texDesc, nullptr);

// Allocate space for match results
int* d_matches;
cudaMalloc(&d_matches, 4 * featureData.kps.size());

// Launch the matching kernel
CUDAmatch(d_descRef, static_cast<int>(refData.kps.size()), tex_q, static_cast<int>(featureData.kps.size()), d_matches, threshold);

// d_descRef is memory pointed to by a uint64_t* for the reference descriptors.

В этом контексте у меня есть несколько вопросов, так как это мой первый опыт разработки на базе графических процессоров.

  1. Дескрипторы и т. Д. Копируются в память устройства, когда пришло время выполнить сопоставление, а результаты копируются обратно. Должен ли я освободить указатели памяти устройства для них после выполнения сопоставления каждый раз и перераспределить (cudaMalloc()) при следующем обратном вызове? Длина дескрипторов будет меняться в зависимости от количества обнаруженных функций. Или есть более эффективный способ выделить память только один раз и использовать ее повторно?
  2. Функции обнаружения и сопоставления также используют такие объекты, как cudaResourceDesc и cudaTextureDesc, которые выходят из области видимости в конце каждого выполнения и, следовательно, должны быть уничтожены. Должен ли я обращаться с ними любым другим конкретным способом?
  3. Полагаю, мне нужно cudaDeviceSynchronize() после выполнения каждой из этих двух функций. Я прав?
  4. Могу ли я безопасно оставить «справочные» дескрипторы в памяти графического процессора и обновлять их только тогда, когда это необходимо?

1 Ответ

0 голосов
/ 02 мая 2018

Должен ли я освободить указатели памяти устройства для них после выполнения сопоставления каждый раз и перераспределить (cudaMalloc ()) при следующем обратном вызове?

Вероятно, нет. Это может показаться ненужным и трудоемким.

Или есть более эффективный способ выделить память только один раз и использовать ее повторно?

Возможно. Например, вы можете определить максимально необходимый размер, выделить его, затем передать указатель на него в цикл обработки событий и повторно использовать выделение.

Функции обнаружения и сопоставления также используют такие объекты, как cudaResourceDesc и cudaTextureDesc, которые выходят из области действия в конце каждого выполнения и, следовательно, должны быть уничтожены. Должен ли я обращаться с ними любым другим конкретным способом?

Точно так же вы можете создавать их в большем объеме и передавать ссылки на них в вашу систему обработки событий. Тем не менее, я думаю, что основным потребителем времени здесь будет копирование данных для заполнения текстуры и привязка текстуры. Это должно быть повторено в любом случае (предположительно). Но если у вас есть базовое выделение для резервного хранилища для объекта текстуры, то вам, вероятно, не потребуется перераспределять его, см. Предыдущие комментарии.

Полагаю, мне нужна cudaDeviceSynchronize () после выполнения каждой из этих двух функций. Я прав?

Для меня не очевидно, что это необходимо. Вы не показали полный пример, но если в конце ваших функций есть копия данных с устройства на хост, этого, вероятно, будет достаточно. cudaMemcpy является функцией блокировки. Если вы работаете в физически / логически объединенной ситуации с памятью, такой как TX1 / TX2, то да, вам может потребоваться точка синхронизации, чтобы убедиться, что данные действительны, прежде чем использовать их в коде хоста.

Могу ли я безопасно оставить «эталонные» дескрипторы в памяти графического процессора и обновлять их только тогда, когда это необходимо?

Не знаю, почему нет. Выделение, созданное с помощью cudaMalloc, не «выходит из области видимости» до тех пор, пока не будет завершено приложение или явно не будет разрешено с помощью cudaFree. Если вы копируете данные в такое распределение, они должны оставаться без изменений в течение всего срока действия вашего приложения, если только вы не перезаписаете их каким-либо образом (или не освободите базовое распределение).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...