Хобрук: Ваш путь к мастерству в программировании

CUDA: использование общей памяти для возврата массива из функций устройства

можно ли выделить общую память для ядра (внутри или снаружи) и использовать ее в других функциях устройства, вызываемых из ядра? Особенно интересно для меня будет, если/как я могу использовать его как возвращаемый параметр/массив.

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

Когда я использую его как возвращаемый параметр, у меня возникает несколько проблем:

  • Я могу запустить программу, когда она была построена из конфигурации отладки.

  • Но я не могу его отладить -> он падает в функциях устройства, когда я использую общую память

  • Также я получаю ошибки с cuda-memchecker -> неверным чтением __global__, потому что адрес выходит за пределы и он читается из общего адресного пространства.

Так можно ли использовать разделяемую память для возврата массивов из функций устройства в ядро?

РЕДАКТИРОВАТЬ:

Я написал очень простой пример, чтобы исключить другие ошибки, сделанные мной.

#define CUDA_CHECK_RETURN(value) {                                      \
    cudaError_t _m_cudaStat = (value);                                  \
    if (_m_cudaStat != cudaSuccess) {                                   \
        printf( "Error %s at line %d in file %s\n",                     \
                cudaGetErrorString(_m_cudaStat), __LINE__, __FILE__);   \
        exit(-1);                                                       \
    } }

__device__ void Function( const int *aInput, volatile int *aOutput )
{
    for( int i = 0; i < 10; i++ )
        aOutput[i] = aInput[i] * aInput[i];
}

__global__ void Kernel( int *aInOut )
{
     __shared__ int aShared[10];

    for(int i=0; i<10; i++)
        aShared[i] = i+1;

    Function( aShared, aInOut );
}

int main( int argc, char** argv )
{
    int *hArray = NULL;
    int *dArray = NULL;

    hArray = ( int* )malloc( 10*sizeof(int) );
    CUDA_CHECK_RETURN( cudaMalloc( (void**)&dArray, 10*sizeof(int) ) );

    for( int i = 0; i < 10; i++ )
            hArray[i] = i+1;

    CUDA_CHECK_RETURN( cudaMemcpy( dArray, hArray, 10*sizeof(int), cudaMemcpyHostToDevice ) );
    cudaMemcpy( dArray, hArray, 10*sizeof(int), cudaMemcpyHostToDevice );

    Kernel<<<1,1>>>( dArray );

    CUDA_CHECK_RETURN( cudaMemcpy( hArray, dArray, 10*sizeof(int), cudaMemcpyDeviceToHost ) );
    cudaMemcpy( hArray, dArray, 10*sizeof(int), cudaMemcpyDeviceToHost );

    free( hArray );
    CUDA_CHECK_RETURN( cudaFree( dArray ) );
    cudaFree( dArray );

    return 0;
}

Я запускаю ядро ​​по одному блоку потоков и по одному потоку на блок. Собрать программу и запустить ее не проблема. Я получаю ожидаемые результаты. Но если программа тестируется cuda-memchecker, она завершает работу ядра и появляется следующий журнал.

Error unspecified launch failure at line 49 in file ../CuTest.cu
========= Invalid __global__ read of size 4
=========     at 0x00000078 in /home/strautz/Develop/Software/CuTest/Debug/../CuTest.cu:14:Function(int const *, int volatile *)
=========     by thread (0,0,0) in block (0,0,0)
=========     Address 0x01000000 is out of bounds
=========     Device Frame:/home/strautz/Develop/Software/CuTest/Debug/../CuTest.cu:25:Kernel(int*) (Kernel(int*) : 0xd0)
=========     Saved host backtrace up to driver entry point at kernel launch time
=========     Host Frame:/usr/lib/libcuda.so (cuLaunchKernel + 0x34b) [0x55d0b]
=========     Host Frame:/usr/lib/libcudart.so.5.0 [0x8f6a]
=========
========= Program hit error 4 on CUDA API call to cudaMemcpy 
=========     Saved host backtrace up to driver entry point at error
=========     Host Frame:/usr/lib/libcuda.so [0x24e129]
=========     Host Frame:/usr/lib/libcudart.so.5.0 (cudaMemcpy + 0x2bc) [0x3772c]
=========     Host Frame:[0x5400000]
=========
========= ERROR SUMMARY: 2 errors

Должна ли разделяемая память выравниваться, нужно ли делать что-то еще или ее можно игнорировать - не думаю?


  • Кажется, у вашего Memcpy тоже есть проблема. Как вы выделяете память устройства и запускаете ядро? Будет идеально, если вы покажете полный файл .cu. 14.01.2013
  • Я предполагаю, что вы не делаете проверка ошибок cuda. 14.01.2013
  • Я еще раз обновляю свой пост со всей программой. Но я не на своем рабочем компьютере, и в настоящее время на этом компьютере у меня даже не установлена ​​cuda. Таким образом, синтаксис может быть неправильным (в данный момент я не могу его проверить), но это моя небольшая тестовая программа - я обновлю пост еще раз с правильным синтаксисом завтра, когда вернусь к своему рабочему компьютеру. 14.01.2013
  • В тестовой программе я не проверяю ошибки, правильно - завтра добавлю. Но в большой программе я это делаю. Может быть, я забыл его в некоторых местах, я не надеюсь. Завтра тоже проверю. 14.01.2013
  • за исключением нескольких синтаксических ошибок (например, cudaMemCpy -> cudaMemcpy), код, который вы ввели в свой вопрос, компилируется и выполняется без ошибок, а cuda-memcheck сообщает об отсутствии ошибок. Поэтому я не думаю, что этот образец представляет какую-либо проблему, с которой вы столкнулись. Я очень подозрительно отношусь к коду, который создает проблему, связанную с тем, что cudaMalloc, связанный с глобальным массивом, который вы передаете ядру, выполняется неправильно, и вы не проверяете его на наличие ошибок. 14.01.2013
  • Да, извините за синтаксические ошибки, которые я опубликовал, но у меня нет приложения cuda на моем компьютере. Но завтра на компе на работе буду. Вы собирали его под Linux с помощью nsight eclipse или под Windows с Visual Studio? До сих пор я тестировал его только под Linux и получил эту ошибку проверки памяти и для этой короткой программы. 14.01.2013
  • В исходном посте теперь опубликован весь cu-файл теста, но, как я уже сказал, я все еще получаю ошибку с cuda-memchecker, и, поскольку я добавил проверку ошибок cuda, я получаю неспецифический сбой запуска вторым cudaMemcpy? У вас есть идеи, почему вы не можете проверить это без ошибок, а я не могу это сделать? Я пытаюсь извлечь некоторый код исходной программы. Это будет сложно, потому что это большая программа, и мне не разрешено показывать весь код. 15.01.2013
  • @hubs, вы можете использовать @username, чтобы информировать других о том, что вы прокомментировали их. Поскольку у вас есть unspecific launch failure, я предлагаю вам копать оттуда. Кажется, ваше ядро ​​​​на самом деле не работает. Добавьте CUDA_CHECK_RETURN(cudaDeviceSynchronize()); сразу после запуска ядра. Попробуйте профилировать программу и проверить временную шкалу. Покажите нам свои настройки запуска, включая реальный номер сетки/блока. Это не <<<1,1>>> в вашем коде с ошибками, верно? 17.01.2013
  • @Eric: извините, я здесь новенький, но большое спасибо за ваши советы и вашу помощь. Я могу запустить ядро ​​с любыми настройками, и я получу эту ошибку. Ошибка memcheck с ошибкой out ofbound и ошибка с errorcheck code=4(cudaErrorLaunchFailure) "cudaDeviceSynchronize(). Но я получу это, только если не буду использовать обертку. 17.01.2013
  • @Eric: обнаружен сбой. Как здесь описана проблема с драйвером. Обновил до актуального драйвера и теперь все нормально. 17.01.2013

Ответы:


1

см. установочный файл CUDA 5.0 /usr/local/cuda-5.0/samples/6_Advanced/reduction/doc/reduction.ppt

sdata — это локальная переменная функции устройства warpReduce(). Он хранит адрес общей памяти. Общая память может быть прочитана/записана по адресу в функции устройства. Затем окончательный результат сокращения считывается из общей памяти за пределами warpReduce().

template <unsigned int blockSize>
__device__ void warpReduce(volatile int *sdata, unsigned int tid) {
    if (blockSize >=  64) sdata[tid] += sdata[tid + 32];
    if (blockSize >=  32) sdata[tid] += sdata[tid + 16];
    if (blockSize >=  16) sdata[tid] += sdata[tid +  8];
    if (blockSize >=   8) sdata[tid] += sdata[tid +  4];
    if (blockSize >=   4) sdata[tid] += sdata[tid +  2];
    if (blockSize >=   2) sdata[tid] += sdata[tid +  1];
}
template <unsigned int blockSize>
__global__ void reduce6(int *g_idata, int *g_odata, unsigned int n) {
    extern __shared__ int sdata[];
    unsigned int tid = threadIdx.x;
    unsigned int i = blockIdx.x*(blockSize*2) + tid;
    unsigned int gridSize = blockSize*2*gridDim.x;
    sdata[tid] = 0;

    while (i < n) { sdata[tid] += g_idata[i] + g_idata[i+blockSize];  i += gridSize;  }
    __syncthreads();

    if (blockSize >= 512) { if (tid < 256) { sdata[tid] += sdata[tid + 256]; } __syncthreads(); }
    if (blockSize >= 256) { if (tid < 128) { sdata[tid] += sdata[tid + 128]; } __syncthreads(); }
    if (blockSize >= 128) { if (tid <  64) { sdata[tid] += sdata[tid +  64]; } __syncthreads(); }

    if (tid < 32) warpReduce(sdata, tid);
    if (tid == 0) g_odata[blockIdx.x] = sdata[0];
}
14.01.2013
  • К сожалению, в образцах, которые я установил (производственный выпуск 5.0.35), в уменьшенном образце больше нет функции устройства warpReduce - она ​​также изменена в версии из зоны разработчиков nvidia. Сокращение выполняется непосредственно в ядре. Поэтому я сделал простой пример, чтобы проверить его (я опубликую его позже), и у меня все еще есть некоторые из моих первоначальных проблем. 14.01.2013
  • Я должен отредактировать свой первоначальный пост, потому что это был мой первый пост в жизни. 14.01.2013
  • Я обнаружил использование общей памяти в качестве возвращаемого значения из функций устройства в образце сканирования. Я попытаюсь выяснить, в чем отличие от моей программы, потому что в этом примере sdk я не получаю ошибок с memcheck. 15.01.2013
  • Хорошо, похоже, вам нужно создать оболочку, чтобы избежать этой ошибки. Но я не знаю, почему.. 16.01.2013
  • @hubs код в вашем ответе использует оболочку, а в моем нет. Судя по вашему коду, это скорее ошибка memcheck. 16.01.2013

  • 2

    Как описано здесь, это была просто проблема с драйвером. После того, как я обновился до актуальной, все работает нормально.

    16.01.2013
    Новые материалы

    Получение стоковых обновлений с помощью Python
    Для начинающего финансового аналитика Введение Описание: Этот проект Python создает скрипт для получения текущих обновлений акций с финансового веб-сайта Yahoo. Для этого проекта мы..

    Это все, что вам нужно знать о Kotlin в 2022 году
    Добро пожаловать! Kotlin — это язык программирования, популярность которого, кажется, растет, его действительно можно использовать для создания чего угодно, и если вы хотите узнать о Kotlin,..

    Текстовый графический интерфейс с Lanterna на Java
    Мой опыт работы с компьютерами (и текстовыми графическими пользовательскими интерфейсами) начался еще в восьмидесятых, когда я был ребенком, на дне рождения друга. Это был «новенький» Amstrad..

    Перезарядите свой мозг: умопомрачительный потенциал мозговых компьютерных интерфейсов
    Способность читать свои мысли и управлять объектами разумом долгое время были предметом человеческого любопытства, ограниченного областью научной фантастики… то есть до сих пор? С технологией,..

    Основы C# — Нулевой оператор объединения (??)
    Оператор ?? называется null-coalescing operator . Этот оператор используется для предоставления значения по умолчанию, если значение операнда в левой части оператора равно null ...

    Сравнение номеров версий в C++ с использованием синтаксического анализа строк
    Номера версий обычно используются для обозначения развития или обновлений программного обеспечения или любого другого продукта. При работе с номерами версий в C++ может быть полезно сравнить две..

    В мир искусственного интеллекта…
    ИИ — это новое топливо в современном мире. Куда бы вы ни обратились, с кем бы вы ни разговаривали — они, как правило, упоминают об ИИ хотя бы раз в ходе разговора. ИИ гудит повсюду. У каждого..