注:这是我的线代报告,发这里算了
前言
引言
如今,线性代数在深度学习中的应用非常广泛。但是在计算机领域,使用传统CPU进行矩阵运算是非常缓慢的,因为CPU并不擅长高度并行计算。而对于擅长高度并行化计算的GPU而言,这是非常容易的,这也是为什么现代大模型的训练离不开大量显卡(计算卡)
本次报告目的
本文使用CUDA编程实现高效的矩阵操作(包括矩阵乘法、矩阵转置)
CUDA简介[1]
CUDA 是NVIDIA 发明的一种 并行 计算平台和编程模型。它通过利用
图形处理器(GPU)的处理能力,可大幅提升计算性能。
编程环境
操作系统 |
CUDA设备 |
IDE |
ubuntu 18 LTS |
tesla p4 |
Nsight |
windows 11 |
RTX 3060 laptop |
CLion |
基础知识
名词 |
中文 |
解释 |
shared memory |
共享显存 |
容量很小,速度很快,随机读写性能好,局部访问的缓存 |
global memory |
全局显存 |
容量很大,速度较慢,随机读写性能差,全局访问的缓存 |
矩阵转置
数学原理
我们学习的矩阵转置如下
若矩阵有Aij,则AT=Aji
但是在实际计算过程中,矩阵体积很大,只能存储在全局显存
中,而矩阵转置涉及到了大量的的随机读写,导致了性能显著下降。
要解决这个问题,就需要引入共享显存
,但是共享显存
的体积很小且只能局部访问,无法存储整个矩阵。所以,我们可以使用分块矩阵来优化这个过程。将子矩阵复制到共享显存
,再使用共享显存
完成子矩阵的转置,然后再复制回全局显存
。
分块矩阵转置:[ACBD]T=[ATBTCTDT]
CUDA代码实现
1 2 3 4 5 6 7 8 9 10 11 12
| __global__ void kernel_transpose(DTYPE *input, DTYPE *output, int num_rows, int num_cols) { __shared__ DTYPE buffer[BLOCK_SIZE][BLOCK_SIZE]; int column=blockIdx.x*BLOCK_SIZE+threadIdx.x; int row=blockIdx.y*BLOCK_SIZE+threadIdx.y; if(column>=num_cols||row>=num_rows){ return; } buffer[threadIdx.y][threadIdx.x]=input[row*num_cols+column]; __syncthreads(); output[column*num_rows+row]=buffer[threadIdx.y][threadIdx.x]; }
|
矩阵乘法
数学原理
我们学习的矩阵乘法如下
有(M)ij,(N)jm,P=M×N则(P)im=k=1∑jMikNkj
这样的确是最简单的算法,使用Python
语言的列表推导式能在一行内完成这个操作
1 2
| def mul(m,n): return [[sum([m[i][k]*n[k][j] for k in range(len(n))]) for j in range(len(n[0]))] for i in range(len(m))]
|
但是由于与上文相同的原因,大量的随机写入导致性能大幅下降。因此,必须使用分块矩阵乘法。数学原理与上文类似,不过是将每一个数字换成了矩阵,对于每一个小矩阵,放在共享显存
中完成运算,再复制回全局显存。这样大大减少了对全局显存的随机读取,大幅提高了计算效率。
CUDA代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| __global__ void kernel_matrix_multiply_block(float *M, float *N, float *P, int M_rows, int M_cols, int N_rows, int N_cols) { __shared__ float M_shared[BLOCK_SIZE][BLOCK_SIZE]; __shared__ float N_shared[BLOCK_SIZE][BLOCK_SIZE];
int bx = blockIdx.x; int by = blockIdx.y; int tx = threadIdx.x; int ty = threadIdx.y;
int row = by * BLOCK_SIZE + ty; int col = bx * BLOCK_SIZE + tx;
if (row >= M_rows || col >= N_cols) { return; }
float Pvalue = 0; for (int m = 0; m < (M_cols - 1) / BLOCK_SIZE+1; m++) { if (row < M_rows && m * BLOCK_SIZE + tx < M_cols) { M_shared[ty][tx] = M[row * M_cols + m * BLOCK_SIZE + tx]; } else { M_shared[ty][tx] = 0; } if (col < N_cols && m * BLOCK_SIZE + ty < N_rows) { N_shared[ty][tx] = N[(m * BLOCK_SIZE + ty) * N_cols + col]; } else { N_shared[ty][tx] = 0; } __syncthreads(); for (int k = 0; k < BLOCK_SIZE; ++k) { Pvalue += M_shared[ty][k] * N_shared[k][tx]; } __syncthreads(); } P[row * N_cols + col] = Pvalue; }
|
总结
世界上几乎不存在单核的、性能非常强劲的计算设备,想要分析非常大的数据,就不得不利用分布式计算设备进行分布式计算。对于矩阵而言,分块矩阵是逃不掉的基础理论,这是矩阵并行计算的基础。
参考资料
- 百度百科_CUDA https://baike.baidu.com/item/CUDA/1186262