Ошибка транспонирования CUDA на месте

Я реализую программу CUDA для переноса изображения. Я создал 2 ядра. Первое ядро ​​неуместно транспонирует и отлично работает для любого размера изображения.

Затем я создал ядро ​​для перемещения квадратных изображений на месте. Однако вывод неверный. Нижний треугольник изображения транспонируется, но верхний треугольник остается прежним. Результирующее изображение имеет диагональный узор в виде лестницы, а размер каждой ступеньки лестницы равен размеру 2D-блока, который я использовал для своего ядра.

Неуместное ядро:

Отлично работает для изображений любого размера, если src и dst отличаются.

template<typename T, int blockSize>
__global__ void kernel_transpose(T* src, T* dst, int width, int height, int srcPitch, int dstPitch)
{
    __shared__ T block[blockSize][blockSize];

    int col = blockIdx.x * blockSize + threadIdx.x;
    int row = blockIdx.y * blockSize + threadIdx.y;

    if((col < width) && (row < height))
    {
        int tid_in = row * srcPitch + col;
        block[threadIdx.y][threadIdx.x] = src[tid_in];
    }

    __syncthreads();

    col = blockIdx.y * blockSize + threadIdx.x;
    row = blockIdx.x * blockSize + threadIdx.y;

    if((col < height) && (row < width))
    {
        int tid_out = row * dstPitch + col;
        dst[tid_out] = block[threadIdx.x][threadIdx.y];
    }
}

Ядро на месте:

template<typename T, int blockSize>
__global__ void kernel_transpose_inplace(T* srcDst, int width, int pitch)
{
    __shared__ T block[blockSize][blockSize];

    int col = blockIdx.x * blockDim.x + threadIdx.x;
    int row = blockIdx.y * blockDim.y + threadIdx.y;

    int tid_in = row * pitch + col;
    int tid_out = col * pitch + row;

    if((row < width) && (col < width))
        block[threadIdx.x][threadIdx.y] = srcDst[tid_in];

    __threadfence();

    if((row < width) && (col < width))
        srcDst[tid_out] = block[threadIdx.x][threadIdx.y];
}

Функция обертки:

int transpose_8u_c1(unsigned char* pSrcDst, int width,int pitch)
{
    //pSrcDst is allocated using cudaMallocPitch

    dim3 block(16,16);
    dim3 grid;
    grid.x = (width + block.x - 1)/block.x;
    grid.y = (width + block.y - 1)/block.y;

    kernel_transpose_inplace<unsigned char,16><<<grid,block>>>(pSrcDst,width,pitch);

    assert(cudaSuccess == cudaDeviceSynchronize());

    return 1;
}

Пример ввода и неправильный вывод:

введите здесь описание изображениявведите здесь описание изображения

Я знаю, что эта проблема как-то связана с логикой транспонирования на месте. Это связано с тем, что мое неуместное транспонированное ядро, которое отлично работает для разных источников и мест назначения, также дает тот же неправильный результат, если я передаю ему один указатель для источника и места назначения.

Что я делаю неправильно? Помогите мне в исправлении In-place ядра.


person sgarizvi    schedule 05.01.2013    source источник
comment
Не могли бы вы добавить фотографии до, после-правильно и после-неправильно. Это помогает визуализировать проблему. Также, если бы вы могли включить код для неуместного ядра   -  person 1-----1    schedule 05.01.2013
comment
@ ks6g10... Добавил оба. Ну... Разве не очевидно, что такое афтеркоррект?.   -  person sgarizvi    schedule 05.01.2013


Ответы (1)


Ваше ядро ​​на месте перезаписывает данные в образе, который впоследствии будет выбран другим потоком для использования в операции транспонирования. Таким образом, для квадратного изображения следует буферизовать целевые данные перед их перезаписью, а затем поместить целевые данные в правильное транспонированное место. Поскольку с помощью этого метода мы фактически делаем 2 копии на поток, необходимо использовать только вдвое меньше потоков. Что-то вроде этого должно работать:

template<typename T, int blockSize>
__global__ void kernel_transpose_inplace(T* srcDst, int width, int pitch)
{

    int col = blockIdx.x * blockDim.x + threadIdx.x;
    int row = blockIdx.y * blockDim.y + threadIdx.y;

    int tid_in = row * pitch + col;
    int tid_out = col * pitch + row;

    if((row < width) && (col < width) && (row<col)) {

        T temp = srcDst[tid_out];

        srcDst[tid_out] = srcDst[tid_in];
        srcDst[tid_in] = temp;
        }
}
person Robert Crovella    schedule 05.01.2013
comment
Большое спасибо. Это действительно решило проблему. Но одно смущает? Разве я уже не буферизовал данные в разделяемой памяти? Чтобы убедиться, что все потоки буферизовали данные, я использовал threadfence(). - person sgarizvi; 06.01.2013
comment
Я подумал, что у вас могут возникнуть вопросы по поводу __threadfence() Это своего рода барьер, но он не распространяется на все устройство. Это только барьер для конкретного потока. Если бы это действовало как барьер для всех потоков одновременно, тогда ваш метод мог бы работать. Но в CUDA нет глобального барьера, кроме как через запуск/выход из ядра. Вы можете прочитать описание< /а>. В вашем случае это гарантировало только то, что запись в разделяемую память была видна другим потокам в блоке до продолжения выполнения. - person Robert Crovella; 06.01.2013
comment
Да я читал описание __threadfence() и со второго пункта в его документации я всегда думал что это как __syncthreads() для всей сетки. - person sgarizvi; 06.01.2013
comment
Ключевая фраза создается вызывающим потоком. Это означает, что он блокирует выполнение любого потока, в котором он находится, и блокирует выполнение только до тех пор, пока не будут выполнены эти два условия видимости. Он немного отличается от __syncthreads(), и, как я уже говорил, в CUDA нет механизма глобальной синхронизации. В отличие от __syncthreads(), он не заставляет несколько потоков достигать барьера до того, как любой поток сможет продолжить выполнение. - person Robert Crovella; 06.01.2013
comment
Привет, ребята, похоже, это не работает для неквадратных матриц. Почему это так? - person bge0; 18.10.2014
comment
Квадратная матрица имеет диагональ, которая делит изображение пополам. Подход на месте в вопросе и мой ответ используют это. Я полагаю, что для неквадратных матриц исходное неуместное транспонирование, указанное в вопросе, вероятно, сработает. Должно быть совершенно очевидно, что для опубликованного ответа требуется квадратная матрица, поскольку индексы строк и столбцов проверяются на соответствие одному и тому же измерению/параметру (width). - person Robert Crovella; 18.10.2014