From b69a652ca9a55560a4585a93a2ef21c8e45a14c3 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 30 Apr 2024 01:35:10 -0300 Subject: [PATCH] matmul cuda --- build/cpu.o | Bin 2400 -> 2824 bytes build/cuda.cu.o | Bin 27072 -> 35216 bytes build/libtensor.so | Bin 50336 -> 58920 bytes build/tensor.o | Bin 15248 -> 15880 bytes csrc/cpu.cpp | 13 +++++++++++++ csrc/cpu.h | 2 +- csrc/cuda.cu | 41 +++++++++++++++++++++++++++++++++++++++++ csrc/cuda.h | 3 +++ csrc/tensor.cpp | 35 ++++++++++++++++++++++------------- test.py | 2 +- 10 files changed, 81 insertions(+), 15 deletions(-) diff --git a/build/cpu.o b/build/cpu.o index b6465ab743793c6779de951de0a487b044c5134d..8eb61f6549ff4fbc80b9117295c230f2e5ea5abc 100644 GIT binary patch delta 593 zcmZWjJ4gdT5Z&Fo+$Esy8NY=nqJoHhunCGRTq6h;YN23bqk@8hh+l%DhM+i&phawL zR0IpF#KsgB5kxFwXDSabMbPLuo4tVG!0ybO_vX#y!n=)!-7I5X^&C;?r!^Z6!LZ}v z&5rk5andIop7_Gny(suhPBL~ZrAxNJ6HnNm=I<$&*NWo3DEYFoK}A+tTT-4@#ac1^ zLO6TrxG2$#eQc+5Y{RBmms>%U;U1~}vkuiIsV-F1N=#fnp2ne}qRYpyz%yd#m|ylh z@1KFQcP9<9xGqt@?ce&Mj6~6G;I$-%e7*sPW5S{Nj~~y2D|$f*Ot6!#793fXV;H>@ zKT%`tu)%6W>!4L;kaS|}^!l5P(DpKo@EBj1P?BU=V63Y|ZeT%gq7V2V=#japMR?Um z!)4CUltjnGK?G*PN!1|ILcSz}^T2a;t1m3GaAwv)(r9KG18$8do7A9PYXr;eu9U;7 zTrs0;Uy)ht9BS}sMnjjrR|*D#kx)$qG&lrUa(Ri#Y8#8<-$QE|~n1)0{Ja6~arHtjT50slW{3WlYWl@;X4a V0>Qe;E4j?MHb7P8FaRx=0|3_cFsA?j diff --git a/build/cuda.cu.o b/build/cuda.cu.o index 51d5b7ba47cc0b64180f82b35f48c8c1287a546a..693603724901aafb7bd7e624ff01bda72cde848e 100644 GIT binary patch literal 35216 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 QMt|wBPQ+d^c^UeD2jGlG761SM delta 6204 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 diff --git a/build/libtensor.so b/build/libtensor.so index 1f13ba8c8d93581dd91b7a9281c4f01a893fbb73..85e0f90722b33c5b2f4846f5aa1c8322fa54fb4c 100755 GIT binary patch literal 58920 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 delta 14869 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 delta 1794 zcmZ`&ZD?Cn7{2E=YBRdtq#wClBI{CA()_V$*EUVNEpr^X3<~X!g2MJA_yPOnCM{E3 zDK%_*xmHqNs{;|4h$y1#h?AiVk`hQ96*Cv4KL#cmCAGtS-*jhJVtxqoO{pn zJn!?q?>YCremZ?B9hQBui;?%*gOj>3cUd=PS9FtK*3E=ZPh~x(4|gkYt34h@e6ru+ z`gP++-6+&09A~mKF%s#fZqYEMJl;zNDB^VrFek$#FcV=`O4Vh9*Gyi6g@>M;FYr8B zSfWHYRcE6-KevyzRDe01*Pr=8&;2axtmJGK+m`K!>DxNHYD$Kj;(4}t5!Oo(@)1DNfFt8{t3xFDht3ukLulr7-<5T^F3P(ehD4TBziwl*ynI4 zs@f81Kj2LSZKqZ^zJJ84_t?9`@G0)jjo7;xSO|8){dn8o?!HA} diff --git a/csrc/cpu.cpp b/csrc/cpu.cpp index 93623e9..61a167c 100644 --- a/csrc/cpu.cpp +++ b/csrc/cpu.cpp @@ -25,6 +25,19 @@ void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_ } } +void matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) { + + for (int i = 0; i < tensor1->shape[0]; i++) { + for (int j = 0; j < tensor2->shape[1]; j++) { + float sum = 0.0; + for (int k = 0; k < tensor1->shape[1]; k++) { + sum += tensor1->data[i * tensor1->shape[1] + k] * tensor2->data[k * tensor2->shape[1] + j]; + } + result_data[i * tensor2->shape[1] + j] = sum; + } + } +} + void pow_tensor_cpu(Tensor* tensor, float power, float* result_data) { for (int i = 0; i < tensor->size; i++) { diff --git a/csrc/cpu.h b/csrc/cpu.h index 20c0f2b..4370dc8 100644 --- a/csrc/cpu.h +++ b/csrc/cpu.h @@ -1,5 +1,5 @@ void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); - +void matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void pow_tensor_cpu(Tensor* tensor, float power, float* result_data); \ No newline at end of file diff --git a/csrc/cuda.cu b/csrc/cuda.cu index d433677..6a953b2 100644 --- a/csrc/cuda.cu +++ b/csrc/cuda.cu @@ -6,6 +6,7 @@ #define THREADS_PER_BLOCK 128 __host__ void cpu_to_cuda(Tensor* tensor) { + float* data_tmp; cudaMalloc((void **)&data_tmp, tensor->size * sizeof(float)); cudaMemcpy(data_tmp, tensor->data, tensor->size * sizeof(float), cudaMemcpyHostToDevice); @@ -35,6 +36,7 @@ __host__ void cuda_to_cpu(Tensor* tensor) { } __global__ void add_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size) { + int i = blockIdx.x * blockDim.x + threadIdx.x; if (i < size) { result_data[i] = data1[i] + data2[i]; @@ -56,6 +58,7 @@ __host__ void add_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_da } __global__ void sub_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size) { + int i = blockIdx.x * blockDim.x + threadIdx.x; if (i < size) { result_data[i] = data1[i] - data2[i]; @@ -77,6 +80,7 @@ __host__ void sub_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_da } __global__ void elementwise_mul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size) { + int i = blockIdx.x * blockDim.x + threadIdx.x; if (i < size) { result_data[i] = data1[i] * data2[i]; @@ -97,7 +101,43 @@ __host__ void elementwise_mul_tensor_cuda(Tensor* tensor1, Tensor* tensor2, floa cudaDeviceSynchronize(); } +__global__ void matmul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int rows1, int cols1, int cols2) { + + int row = blockIdx.y * blockDim.y + threadIdx.y; + int col = blockIdx.x * blockDim.x + threadIdx.x; + + if (row < rows1 && col < cols2) { + float sum = 0.0; + for (int k = 0; k < cols1; k++) { + sum += data1[row * cols1 + k] * data2[k * cols2 + col]; + } + result_data[row * cols2 + col] = sum; + } + +} + +__host__ void matmul_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data) { + + int rows1 = tensor1->shape[0]; + int cols1 = tensor1->shape[1]; + int cols2 = tensor2->shape[1]; + + dim3 threadsPerBlock(16, 16); + dim3 numBlocks((cols2 + threadsPerBlock.x - 1) / threadsPerBlock.x, (rows1 + threadsPerBlock.y - 1) / threadsPerBlock.y); + matmul_tensor_cuda_kernel<<>>(tensor1->data, tensor2->data, result_data, rows1, cols1, cols2); + + + cudaError_t error = cudaGetLastError(); + if (error != cudaSuccess) { + printf("CUDA error: %s\n", cudaGetErrorString(error)); + exit(-1); + } + + cudaDeviceSynchronize(); +} + __global__ void pow_tensor_cuda_kernel(float* data, float power, float* result_data, int size) { + int i = blockIdx.x * blockDim.x + threadIdx.x; if (i < size) { result_data[i] = powf(data[i], power); @@ -118,3 +158,4 @@ __host__ void pow_tensor_cuda(Tensor* tensor, float power, float* result_data) { cudaDeviceSynchronize(); } + diff --git a/csrc/cuda.h b/csrc/cuda.h index c1a1404..1e83602 100644 --- a/csrc/cuda.h +++ b/csrc/cuda.h @@ -16,4 +16,7 @@ __global__ void pow_tensor_cuda_kernel(float* data, float power, float* result_data, int size); __host__ void pow_tensor_cuda(Tensor* tensor, float power, float* result_data); + __global__ void matmul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int rows1, int cols1, int cols2) + __host__ void matmul_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data); + #endif /* CUDA_KERNEL_H_ */ diff --git a/csrc/tensor.cpp b/csrc/tensor.cpp index 05baef0..c140cd0 100644 --- a/csrc/tensor.cpp +++ b/csrc/tensor.cpp @@ -264,9 +264,13 @@ extern "C" { exit(1); } - char* device = tensor1->device; - - // Calculate the shape of the result tensor + char* device = (char*)malloc(strlen(tensor1->device) + 1); + if (device != NULL) { + strcpy(device, tensor1->device); + } else { + fprintf(stderr, "Memory allocation failed\n"); + exit(-1); + } int ndim = tensor1->ndim + tensor2->ndim - 2; int* shape = (int*)malloc(ndim * sizeof(int)); if (shape == NULL) { @@ -291,17 +295,22 @@ extern "C" { exit(1); } - for (int i = 0; i < tensor1->shape[0]; i++) { - for (int j = 0; j < tensor2->shape[1]; j++) { - float sum = 0.0; - for (int k = 0; k < tensor1->shape[1]; k++) { - sum += tensor1->data[i * tensor1->shape[1] + k] * tensor2->data[k * tensor2->shape[1] + j]; - } - result_data[i * tensor2->shape[1] + j] = sum; - } - } + if (strcmp(tensor1->device, "cuda") == 0) { - return create_tensor(result_data, shape, ndim, device); + float* result_data; + cudaMalloc((void **)&result_data, size * sizeof(float)); + matmul_tensor_cuda(tensor1, tensor2, result_data); + return create_tensor(result_data, shape, ndim, device); + } + else { + float* result_data = (float*)malloc(size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + matmul_tensor_cpu(tensor1, tensor2, result_data); + return create_tensor(result_data, shape, ndim, device); + } } Tensor* pow_tensor(Tensor* tensor, float power) { diff --git a/test.py b/test.py index 32946a6..62959be 100644 --- a/test.py +++ b/test.py @@ -29,7 +29,7 @@ if __name__ == "__main__": #d = b-c - b = a ** 2 + b = a @ a print(b) #print(a ** 2)