i have been working on a very big project for some time. Within this project, I wrote my own CUDA-kernels to do various operations. One of them is to perform a sparse affine transformation to a list of sparse inputs.
Basically my input is a list of sparse vectors which are always either 1 or 0. I know for a fact that I can have at most 32 ones in a single vector.
v1 = [0, 0, 0, 1, 0, 0, 1, ...]
v2 = [1, 0, 0, 0, 0, 0, 1, ...]
...
My idea now was to wrap all these vectors into a sparse format like:
3 2 4 2 ...
-----------
1 2 3 2
5 4 4 4
9 . 5 .
. . 8 .
Its basically a matrix. The first row coresponds to the amount of non-zero entries. The values below are the indices of the non-zero entries.
Now when performing the matrix-vector multiplication, all I have to do for each output element is look at the input matrix, get the weights at the given indices and add them up.
So far so good. I wrote the following kernel:
__global__ void sparse_affine_kernel(
const float* __restrict__ mat,
const unsigned int* __restrict__ inp_col_indices,
const unsigned int inp_col_max_entries,
const float* __restrict__ bia,
float* __restrict__ res,
const unsigned int m,
const unsigned int n,
const unsigned int lda,
const unsigned int ldc){
// clang-format on
// compute which output value we are looking at
int col = blockIdx.x * blockDim.x + threadIdx.x;
int row = blockIdx.y * blockDim.y + threadIdx.y;
// skip out of bounds
if (col >= n || row >= m)
return;
// get the offset at which we look into our sparse input
int offset = col * (inp_col_max_entries + 1);
// check how many values we are going to read
int count = inp_col_indices[offset];
// track the sum
float sum = bia[row];
// start at offset + 1 (offset contains the amount of values to read)
for (int i = offset + 1; i < offset + 1 + count; i++) {
// get the sparse index (set row of the input)
auto b_row = inp_col_indices[i];
// get the corresponding weight
auto wgt = mat[MATRIX_INDEX(lda, row, b_row)];
sum += wgt;
}
res[MATRIX_INDEX(ldc, row, col)] = sum;
};
Now the code should be somewhat straight forward. Id like to know the following things:
- Do you see any concrete way of improving this somewhat straight forward operation?
- Is there anything directly related to CUDA which I could use to improve the performance of this code? Maybe using shared memory? I tried using some shared-memory some time ago and simply remembered that I wasnt able to improve the performance of the code.
I am very happy for a review and optimization-ideas for my code :)
Greetings Finn