Базовая загрузка CUDA и транспонирование деформации - PullRequest
0 голосов
/ 03 декабря 2018

Я хочу реализовать базовую блокировку загрузки и транспонирования деформации, используя операции перемешивания в CUDA 9.0.Мне известны реализации cub и trove, но я ограничен компиляцией с помощью nvrtc, а стандартный заголовок делает эти библиотеки трудными для обслуживания.Я не ищу ничего необычного, просто какое-то целое число, число с плавающей запятой и двойное перемешивание данных с размером степени 2.

Визуализируя пример с размером деформации 8, я хочу перейти от:

             correlation
             0    1    2    3

lane 0       0    8   16   24
lane 1       1    9   17   25
lane 2       2   10   18   26
lane 3       3   11   19   27
lane 4       4   12   20   28
lane 5       5   13   21   29
lane 6       6   14   22   30 
lane 7       7   15   23   31 

до этой структуры:

             correlation
             0    1    2    3

lane 0       0    1    2    3
lane 1       8    9   10   11
lane 2       16  17   18   19
lane 3       24  25   26   27 
lane 4       4    5    6    7
lane 5       12  13   14   15
lane 6       20  21   22   23
lane 7       28  29   30   31 

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

int loads[ncorrs];
int values[ncorrs];
int lane_id = threadIdx.x & (warp_size - 1);
// 0 0 0 0 4 4 4 4 8 8 8 8 ....
int base_idx = lane_id & (warp_size - ncorrs);
// 0 1 2 3 0 1 2 3 0 1 2 3
int src_corr = lane_id & (ncorrs - 1);

for(int corr=0; corr < ncorrs; ++corr)
{
    int src_lane = base_idx + corr;
    values[corr] = __shfl_sync(mask, loads[src_corr],
                                 src_lane, warp_size);
}

Поэтому, учитывая приведенные выше данные примера, если мы находимся на 5-й полосе, я ожидаю, что должно произойти следующее индексирование:

base_idx == 4;
src_corr == 1;

corr == [0, 1, 2, 3]
src_lane == [4, 5, 6, 7]
values == [12, 13, 14 15]

Но вместо этого происходит следующее (33 даны позже в данных):

             correlation
             0    1    2    3

lane 0       0    0    0    0
lane 1       4    4    4    4
lane 2       12  12   12   12
lane 3       16  16   16   16
lane 4       20  20   20   20
lane 5       24  24   24   24
lane 6       28  28   28   28 
lane 7       33  33   33   33 

Что я делаю неправильно?Полная реализация для размера основы 32:

#include <cstdlib>
#include <cstdio>

#include "cuda.h"

#define ncorr 4
#define warp_size 32

template <int ncorrs>
__global__ void kernel(
    int * input,
    int * output,
    int N)
{
    // This should provide 0 0 0 0 4 4 4 4 8 8 8 8 ...
    #define base_idx(lane_id) (lane_id & (warp_size - ncorrs))
    // This should provide 0 1 2 3 0 1 2 3 0 1 2 3
    #define corr_idx(lane_id) (lane_id & (ncorrs - 1))


    int n = blockIdx.x*blockDim.x + threadIdx.x;
    int lane_id = threadIdx.x & (warp_size - 1);

    if(n >= N)
        { return; }

    // Input correlation handled by this thread
    int src_corr = corr_idx(lane_id);
    int mask = __activemask();

    if(threadIdx.x == 0)
        { printf("mask %d\n", mask); }

    int loads[ncorrs];
    int values[ncorrs];

    #pragma unroll (ncorrs)
    for(int corr=0; corr < ncorrs; ++corr)
        { loads[corr] = input[n + corr*N]; }

    __syncthreads();

    printf("[%d, %d] %d %d %d %d\n",
           lane_id, base_idx(lane_id),
           loads[0], loads[1],
           loads[2], loads[3]);

    #pragma unroll (ncorrs)
    for(int corr=0; corr < ncorrs; ++corr)
    {
        int src_lane = base_idx(lane_id) + corr;
        values[corr] = __shfl_sync(mask, loads[src_corr],
                                     src_lane, warp_size);
    }

    printf("[%d, %d] %d %d %d %d\n",
           lane_id, base_idx(lane_id),
           values[0], values[1],
           values[2], values[3]);


    #pragma unroll (ncorrs)
    for(int corr=0; corr < ncorrs; ++corr)
        { output[n + corr*N] = values[corr]; }
}

void print_data(int * data, int N)
{
    for(int n=0; n < N; ++n)
    {
        printf("% -3d: ", n);
        for(int c=0; c < ncorr; ++c)
        {
            printf("%d ", data[n*ncorr + c]);
        }
        printf("\n");
    }
}

int main(void)
{
    int * host_input;
    int * host_output;

    int * device_input;
    int * device_output;
    int N = 32;

    host_input = (int *) malloc(sizeof(int)*N*ncorr);
    host_output = (int *) malloc(sizeof(int)*N*ncorr);

    printf("malloc done\n");

    cudaMalloc((void **) &device_input, sizeof(int)*N*ncorr);
    cudaMalloc((void **) &device_output, sizeof(int)*N*ncorr);

    printf("cudaMalloc done\n");

    for(int i=0; i < N*ncorr; ++i)
        { host_input[i] = i; }

    print_data(host_input, N);

    dim3 block(256, 1, 1);
    dim3 grid((block.x + N - 1) / N, 1, 1);

    cudaMemcpy(device_input, host_input,
               sizeof(int)*N*ncorr, cudaMemcpyHostToDevice);

    printf("memcpy done\n");

    kernel<4> <<<grid, block>>> (device_input, device_output, N);

    cudaMemcpy(host_output, device_output,
               sizeof(int)*N*ncorr, cudaMemcpyDeviceToHost);

    print_data(host_output, N);

    cudaFree(device_input);
    cudaFree(device_output);

    free(host_input);
    free(host_output);
}

Редактировать 1 : Уточнено, что визуальный пример имеет размер основы 8, в то время как полный код обслуживает размер основы 32

1 Ответ

0 голосов
/ 05 декабря 2018

Что я делаю неправильно?

TL; DR: Короче говоря, вы передаете одно и то же входное значение нескольким выходным значениям.Вот один пример в этой строке кода:

    values[corr] = __shfl_sync(mask, loads[src_corr],
                                 src_lane, warp_size);

Величина, представленная loads[src_corr], является инвариантной к циклу .Поэтому вы передаете это значение на 4 полосы деформации (через 4 итерации цикла), что означает, что значение занимает 4 выходных значения (что в точности соответствует вашим данным распечатки).Это не может быть правильным для транспонирования.

Взяв несколько более длинный взгляд, с другим примером из вашего кода:

Я не уверен, что могу прочитать ваши мысли, но, возможно, вы можетебыть сбитым с толку насчет операции деформацииВозможно, вы предположили, что целевая дорожка может выбрать, какое значение из массива исходной дорожки loads[] желательно.Это не вариант.Линия назначения может выбирать только то значение, которое предоставлено исходной полосой.Давайте посмотрим на ваш цикл:

// This should provide 0 0 0 0 4 4 4 4 8 8 8 8 ...
#define base_idx(lane_id) (lane_id & (warp_size - ncorrs))
// This should provide 0 1 2 3 0 1 2 3 0 1 2 3
#define corr_idx(lane_id) (lane_id & (ncorrs - 1))


int n = blockIdx.x*blockDim.x + threadIdx.x;
int lane_id = threadIdx.x & (warp_size - 1);

...

// Input correlation handled by this thread
int src_corr = corr_idx(lane_id);
int mask = __activemask();

...

int loads[ncorrs];
int values[ncorrs];

...

#pragma unroll (ncorrs)
for(int corr=0; corr < ncorrs; ++corr)
{
    int src_lane = base_idx(lane_id) + corr;
    values[corr] = __shfl_sync(mask, loads[src_corr], src_lane, warp_size);
}

На первом проходе вышеупомянутого цикла src_lane для линий варпа 0, 1, 2 и 3 будут равны 0. ЭтоЭто видно из приведенного выше кода или распечатайте его, если вы не уверены.Это означает, что полосы варпа 0-3 будут запрашивать независимо от того, какое значение предоставлено линией деформации 0. Значение, предоставляемое линией деформации 0, равно loads[src_corr], но интерпретация src_corr здесь является любым значениемон имеет для полосы деформации 0. Поэтому одно и только одно значение будет распределено по полосам деформации 0-3.Это не может быть правильным для транспонирования;никакое входное значение не отображается в 4 местах на выходе.

Чтобы исправить это, нам нужно будет изменить вычисления как src_lane, так и src_corr.Нам также нужно будет изменить место хранения (индекс) для каждой полосы деформации при каждом прохождении цикла (я называю эту новую переменную dest.) Мы можем думать о src_lane как об определении целевого значениячто мой поток получит.Мы можем думать о src_corr как о том, какое из моих значений я опубликую в другом потоке на этой итерации цикла.dest - это место в моем массиве values[], в котором я буду хранить текущее полученное значение.Мы можем вывести необходимый шаблон, тщательно изучив взаимосвязь между входным значением в loads[], требуемым выходным местоположением в values[], принимая во внимание соответствующие полосы движения для источника и назначения.На первом проходе цикла нам нужен этот шаблон:

warp lane: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 ... 
src_lane:  0  8 16 24  1  9 17 25  2 10 18 26  3 11 19 27  4 ... (where my data comes from)
src_corr:  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1  2 ... (which value I am transmitting)
dest:      0  1  2  3  0  1  2  3  0  1  2  3  0  1  2  3  0 ... (where I store the received value)

На втором проходе цикла нам нужен этот шаблон:

warp lane: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 ... 
src_lane:  8 16 24  0  9 17 25  1 10 18 26  2 11 19 27  3 19 ... (where my data comes from)
src_corr:  3  3  3  3  3  3  3  3  0  0  0  0  0  0  0  0  1 ... (which value I am transmitting)
dest:      1  2  3  0  1  2  3  0  1  2  3  0  1  2  3  0  1 ... (where I store the received value)

с соответствующими изменениями для 3-гои 4-й проход петли.Если мы реализуем эти шаблоны в коде для вашего цикла случайного воспроизведения, он может выглядеть примерно так:

$ cat t352.cu
#include <cstdlib>
#include <cstdio>

#include <assert.h>
#define ncorr 4
#define warp_size 32

template <int ncorrs>
__global__ void kernel(
    int * input,
    int * output,
    int N)
{
    // This should provide 0 0 0 0 4 4 4 4 8 8 8 8 ...
    #define base_idx(lane_id) (lane_id & (warp_size - ncorrs))
    // This should provide 0 1 2 3 0 1 2 3 0 1 2 3
    #define corr_idx(lane_id) (lane_id & (ncorrs - 1))


    int n = blockIdx.x*blockDim.x + threadIdx.x;
    int lane_id = threadIdx.x & (warp_size - 1);

    if(n >= N)
        { return; }

    // Input correlation handled by this thread
    int mask = __activemask();

    if(threadIdx.x == 0)
        { printf("mask %d\n", mask); }

    int loads[ncorrs];
    int values[ncorrs];

    #pragma unroll (ncorrs)
    for(int corr=0; corr < ncorrs; ++corr)
        { loads[corr] = input[n + corr*N]; }

    __syncthreads();

    printf("[%d, %d] %d %d %d %d\n",
           lane_id, base_idx(lane_id),
           loads[0], loads[1],
           loads[2], loads[3]);
    #pragma unroll (ncorrs)
    for(int corr=0; corr < ncorrs; ++corr)
    {
        int src_lane = ((lane_id+corr)%ncorrs)*(warp_size/ncorrs) + (lane_id/ncorrs);
        int src_corr = ((ncorrs-corr)+(lane_id/(warp_size/ncorrs)))%ncorrs;
        int dest = (lane_id+corr)%ncorrs;
        values[dest] = __shfl_sync(mask, loads[src_corr],
                                     src_lane, warp_size);
    }

    printf("[%d, %d] %d %d %d %d\n",
           lane_id, base_idx(lane_id),
           values[0], values[1],
           values[2], values[3]);


    #pragma unroll (ncorrs)
    for(int corr=0; corr < ncorrs; ++corr)
        { output[n + corr*N] = values[corr]; }
}

void print_data(int * data, int N)
{
    for(int n=0; n < N; ++n)
    {
        printf("% -3d: ", n);
        for(int c=0; c < ncorr; ++c)
        {
            printf("%d ", data[n*ncorr + c]);
        }
        printf("\n");
    }
}

int main(void)
{
    int * host_input;
    int * host_output;

    int * device_input;
    int * device_output;
    int N = 32;

    host_input = (int *) malloc(sizeof(int)*N*ncorr);
    host_output = (int *) malloc(sizeof(int)*N*ncorr);

    printf("malloc done\n");

    cudaMalloc((void **) &device_input, sizeof(int)*N*ncorr);
    cudaMalloc((void **) &device_output, sizeof(int)*N*ncorr);

    printf("cudaMalloc done\n");

    for(int i=0; i < N*ncorr; ++i)
        { host_input[i] = i; }

    print_data(host_input, N);

    dim3 block(256, 1, 1);
    dim3 grid((block.x + N - 1) / N, 1, 1);

    cudaMemcpy(device_input, host_input,
               sizeof(int)*N*ncorr, cudaMemcpyHostToDevice);

    printf("memcpy done\n");

    kernel<4> <<<grid, block>>> (device_input, device_output, N);

    cudaMemcpy(host_output, device_output,
               sizeof(int)*N*ncorr, cudaMemcpyDeviceToHost);

    print_data(host_output, N);
    cudaFree(device_input);
    cudaFree(device_output);

    free(host_input);
    free(host_output);
}
$ nvcc -o t352 t352.cu
$ cuda-memcheck ./t352
========= CUDA-MEMCHECK
malloc done
cudaMalloc done
 0 : 0 1 2 3
 1 : 4 5 6 7
 2 : 8 9 10 11
 3 : 12 13 14 15
 4 : 16 17 18 19
 5 : 20 21 22 23
 6 : 24 25 26 27
 7 : 28 29 30 31
 8 : 32 33 34 35
 9 : 36 37 38 39
 10: 40 41 42 43
 11: 44 45 46 47
 12: 48 49 50 51
 13: 52 53 54 55
 14: 56 57 58 59
 15: 60 61 62 63
 16: 64 65 66 67
 17: 68 69 70 71
 18: 72 73 74 75
 19: 76 77 78 79
 20: 80 81 82 83
 21: 84 85 86 87
 22: 88 89 90 91
 23: 92 93 94 95
 24: 96 97 98 99
 25: 100 101 102 103
 26: 104 105 106 107
 27: 108 109 110 111
 28: 112 113 114 115
 29: 116 117 118 119
 30: 120 121 122 123
 31: 124 125 126 127
memcpy done
mask -1
[0, 0] 0 32 64 96
[1, 0] 1 33 65 97
[2, 0] 2 34 66 98
[3, 0] 3 35 67 99
[4, 4] 4 36 68 100
[5, 4] 5 37 69 101
[6, 4] 6 38 70 102
[7, 4] 7 39 71 103
[8, 8] 8 40 72 104
[9, 8] 9 41 73 105
[10, 8] 10 42 74 106
[11, 8] 11 43 75 107
[12, 12] 12 44 76 108
[13, 12] 13 45 77 109
[14, 12] 14 46 78 110
[15, 12] 15 47 79 111
[16, 16] 16 48 80 112
[17, 16] 17 49 81 113
[18, 16] 18 50 82 114
[19, 16] 19 51 83 115
[20, 20] 20 52 84 116
[21, 20] 21 53 85 117
[22, 20] 22 54 86 118
[23, 20] 23 55 87 119
[24, 24] 24 56 88 120
[25, 24] 25 57 89 121
[26, 24] 26 58 90 122
[27, 24] 27 59 91 123
[28, 28] 28 60 92 124
[29, 28] 29 61 93 125
[30, 28] 30 62 94 126
[31, 28] 31 63 95 127
[0, 0] 0 8 16 24
[1, 0] 32 40 48 56
[2, 0] 64 72 80 88
[3, 0] 96 104 112 120
[4, 4] 1 9 17 25
[5, 4] 33 41 49 57
[6, 4] 65 73 81 89
[7, 4] 97 105 113 121
[8, 8] 2 10 18 26
[9, 8] 34 42 50 58
[10, 8] 66 74 82 90
[11, 8] 98 106 114 122
[12, 12] 3 11 19 27
[13, 12] 35 43 51 59
[14, 12] 67 75 83 91
[15, 12] 99 107 115 123
[16, 16] 4 12 20 28
[17, 16] 36 44 52 60
[18, 16] 68 76 84 92
[19, 16] 100 108 116 124
[20, 20] 5 13 21 29
[21, 20] 37 45 53 61
[22, 20] 69 77 85 93
[23, 20] 101 109 117 125
[24, 24] 6 14 22 30
[25, 24] 38 46 54 62
[26, 24] 70 78 86 94
[27, 24] 102 110 118 126
[28, 28] 7 15 23 31
[29, 28] 39 47 55 63
[30, 28] 71 79 87 95
[31, 28] 103 111 119 127
 0 : 0 32 64 96
 1 : 1 33 65 97
 2 : 2 34 66 98
 3 : 3 35 67 99
 4 : 4 36 68 100
 5 : 5 37 69 101
 6 : 6 38 70 102
 7 : 7 39 71 103
 8 : 8 40 72 104
 9 : 9 41 73 105
 10: 10 42 74 106
 11: 11 43 75 107
 12: 12 44 76 108
 13: 13 45 77 109
 14: 14 46 78 110
 15: 15 47 79 111
 16: 16 48 80 112
 17: 17 49 81 113
 18: 18 50 82 114
 19: 19 51 83 115
 20: 20 52 84 116
 21: 21 53 85 117
 22: 22 54 86 118
 23: 23 55 87 119
 24: 24 56 88 120
 25: 25 57 89 121
 26: 26 58 90 122
 27: 27 59 91 123
 28: 28 60 92 124
 29: 29 61 93 125
 30: 30 62 94 126
 31: 31 63 95 127
========= ERROR SUMMARY: 0 errors
$
  1. Я считаю, что приведенный выше код довольно четко демонстрирует транспонирование 32x4 -> 4x32.Я думаю, что это «ближе всего» к коду, который вы представили.Он не выполняет набор транспозиций 4x8, который вы изображали на своих диаграммах.

  2. Я признаю, что вычисления src_corr, src_lane и dest не полностью оптимизированы.Но они генерируют правильную индексацию.Я предполагаю, что вы можете решить, как оптимально генерировать их из шаблонов, которые у вас уже есть.

  3. Я думаю, что вполне возможно, что в приведенном выше коде есть ошибки для других измерений.Я не пробовал это ни на чем кроме случая 32x4Тем не менее, я думаю, что указал, что в корне не так с вашим кодом, и продемонстрировал путь к правильной индексации.

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