CUDA: сдвиг массивов в общей памяти
Я пытаюсь загрузить сглаженную 2D матрицу в общую память, сдвинуть данные по x, записать обратно в глобальную память, сдвигая также по y. Поэтому входные данные смещены вдоль x и y. Что я имею:
__global__ void test_shift(float *data_old, float *data_new)
{
uint glob_index = threadIdx.x + blockIdx.y*blockDim.x;
__shared__ float VAR;
__shared__ float VAR2[NUM_THREADS];
// load from global to shared
VAR = data_old[glob_index];
// do some stuff on VAR
if (threadIdx.x < NUM_THREADS - 1)
{
VAR2[threadIdx.x + 1] = VAR; // shift (+1) along x
}
__syncthreads();
// write to global memory
if (threadIdx.y < ny - 1)
{
glob_index = threadIdx.x + (blockIdx.y + 1)*blockDim.x; // redefine glob_index to shift along y (+1)
data_new[glob_index] = VAR2[threadIdx.x];
}
Звонок в ядро:
test_shift <<< grid, block >>> (data_old, data_new);
и grid и blocks (blockDim.x равен ширине матрицы, т.е. 64):
dim3 block(NUM_THREADS, 1);
dim3 grid(1, ny);
Я не могу этого достичь. Может ли кто-нибудь указать, что с этим не так? Должен ли я использовать пошаговый индекс или смещение?
2 ответа
Общие переменные, ну, совместно используются всеми потоками в одном блоке. Это означает, что у вас нет blockDim.y наборов общих переменных, а только один набор на блок.
uint glob_index = threadIdx.x + blockIdx.y*blockDim.x;
__shared__ float VAR;
__shared__ float VAR2[NUM_THREADS];
VAR = data_old[glob_index];
if (threadIdx.x < NUM_THREADS - 1)
{
VAR2[threadIdx.x + 1] = VAR; // shift (+1) along x
}
Это инструктирует все потоки в блоке записывать данные в одну переменную (VAR). Далее у вас нет синхронизации, и вы используете эту переменную во втором назначении. Это будет иметь неопределенный результат, потому что потоки из первой деформации читают из этой переменной, а потоки из второй деформации все еще пытаются что-то там записать. Вы должны изменить VAR на локальный или создать массив переменных общей памяти для всех потоков в блоке.
if (threadIdx.y < ny - 1)
{
glob_index = threadIdx.x + (blockIdx.y + 1)*blockDim.x;
data_new[glob_index] = VAR2[threadIdx.x];
}
В VAR2[0] у вас все еще есть мусор (вы никогда не писали там). threadIdx.y всегда равен нулю в ваших блоках.
И избегайте использования уртов. У них есть (или были) проблемы с производительностью.
На самом деле, для такой простой задачи вам не нужно использовать общую память
__global__ void test_shift(float *data_old, float *data_new)
{
int glob_index = threadIdx.x + blockIdx.y*blockDim.x;
float VAR;
// load from global to local
VAR = data_old[glob_index];
int glob_index_new;
// calculate only if we are going to output something
if ( (blockIdx.y < gridDim.y - 1) && ( threadIdx.x < blockDim.x - 1 ))
{
glob_index_new = threadIdx.x + 1 + (blockIdx.y + 1)*blockDim.x;
// do some stuff on VAR
} else // just write 0.0 to remove garbage
{
glob_index_new = ( (blockIdx.y == gridDim.y - 1) && ( threadIdx.x == blockDim.x - 1 ) ) ? 0 : ((blockIdx.y == gridDim.y - 1) ? threadIdx.x : (blockIdx.y)*blockDim.x );
VAR = 0.0;
}
// write to global memory
data_new[glob_index_new] = VAR;
}
VAR
не должен был быть объявлен как общий, потому что в текущей форме все потоки перебирают данные друг друга при загрузке из глобальной памяти: VAR = data_old[glob_index];
,
У вас также есть доступ за пределами доступа при доступе VAR2[threadIdx.x + 1]
таким образом, ваше ядро никогда не завершает работу (в зависимости от вычислительных возможностей устройства - устройства 1.x не проверяли доступ к общей памяти так строго).
Вы могли обнаружить последнее, проверив коды ошибок всех вызовов функций CUDA на наличие ошибок.