|
@@ -357,14 +357,6 @@ c_t=f_t\cdot c_{(t-1)}+i_t\cdot g_t\\
|
|
|
h_t=o_t\cdot \text{tanh}(c_t)
|
|
|
$$
|
|
|
|
|
|
-##### 并行算法描述
|
|
|
-
|
|
|
-###### 依赖关系分析
|
|
|
-
|
|
|
-从上面公式来看,输出$c_t,h_t$依赖于$c_{(t-1)},f_t,g_t,i_t,o_t$,而$i_t,f_t,g_t,o_t$又依赖于$h_{(t-1)}$,对于这种随时间的迭代计算,不同时间$t$之间不能并行计算,因而考虑$i_t,f_t,g_t,o_t$可以并行计算,而在$i_t,f_t,g_t,o_t$内有矩阵乘加计算,也可以使用分块矩阵的并行计算。
|
|
|
-
|
|
|
-###### MPI+OpenMP设计
|
|
|
-
|
|
|
对于上面公式的矩阵乘加计算,使用增广矩阵形式,化为矩阵乘。
|
|
|
$$
|
|
|
i_t=\text{sigmoid}(W_{ii}x_t+W_{hi}h_{(t-1)})\\
|
|
@@ -374,17 +366,141 @@ o_t=\text{sigmoid}(W_{io}x_t+W_{ho}h_{(t-1)})\\
|
|
|
c_t=f_t\cdot c_{(t-1)}+i_t\cdot g_t\\
|
|
|
h_t=o_t\cdot \text{tanh}(c_t)
|
|
|
$$
|
|
|
-对于LSTM的各个时间点的计算,使用MPI分配在不同的节点上,使用流水化设计,节点间只需传输$h_t,c_t$
|
|
|
+###### 串行伪代码
|
|
|
+
|
|
|
+```pascal
|
|
|
+输入:增广输入向量x[1..T],增广矩阵形式权值W_ii,W_if,W_ig,W_io
|
|
|
+输出:预测向量h[1..T]
|
|
|
+for t = 1 to T
|
|
|
+ i[t] = sigmoid(W_ii * x[t] + W_hi * h[t-1])
|
|
|
+ f[t] = sigmoid(W_if * x[t] + W_hf * h[t-1])
|
|
|
+ g[t] = tanh(W_ig * x[t] + W_hc * h[t-1])
|
|
|
+ o[t] = sigmoid(W_io * x[t] + W_ho * h[t-1])
|
|
|
+ c[t] = f[t] .* c[t-1] + i[t] .* g[t]
|
|
|
+ h[t] = o[t] .* tanh(c[t])
|
|
|
+end for
|
|
|
+```
|
|
|
|
|
|
-对于LSTM的一个时间点内的计算,考虑在同一个节点上使用MPI分为$i_t,f_t,g_t,o_t$4个部分计算,需要广播$x_t$
|
|
|
+##### 并行算法描述
|
|
|
+
|
|
|
+###### 依赖关系分析
|
|
|
+
|
|
|
+从上面公式和串行伪代码来看,依赖关系非常明显,输出$c_t,h_t$流依赖于$c_{(t-1)},f_t,g_t,i_t,o_t$,而$i_t,f_t,g_t,o_t$又六依赖于$h_{(t-1)}$,对于这种随时间的迭代计算,不同时间$t$之间不能并行计算,因而考虑$i_t,f_t,g_t,o_t$可以并行计算,而在$i_t,f_t,g_t,o_t$内有矩阵乘加计算,也可以使用分块矩阵的并行计算。
|
|
|
+
|
|
|
+###### MPI+OpenMP设计
|
|
|
+
|
|
|
+
|
|
|
+对于LSTM的各个时间点的计算,无法并行
|
|
|
|
|
|
-$i_t,f_t,g_t,o_t$每个部分内的矩阵乘法使用OpenMP计算,OpenMP的线程数设置为,一个节点上的处理器个数/4
|
|
|
+对于LSTM的一个时间点内的计算,考虑在同一个节点上使用MPI分为$i_t,f_t,g_t,o_t$4个部分在4个节点上计算,需要广播$x_t,h_{(t-1)}$,最后由0号节点收集结果,再在0号节点上计算$c_t,h_t$
|
|
|
|
|
|
+$i_t,f_t,g_t,o_t$每个部分内的矩阵乘法使用OpenMP计算
|
|
|
|
|
|
##### MPI+OpenMP实现
|
|
|
|
|
|
+###### 矩阵乘,矩阵加,激活,OpenMP实现
|
|
|
+
|
|
|
+```c++
|
|
|
+void MatrixMult(float *a, float *b, float *c, int n, bool init = false) {
|
|
|
+ #pragma omp parallel for num_threads(OMP_THREADS)
|
|
|
+ for (int i = 0; i < n; ++i) {
|
|
|
+ for (int j = 0; j < n; ++j) {
|
|
|
+ if (init) {
|
|
|
+ c[i * n + j] = 0.0f;
|
|
|
+ }
|
|
|
+ for (int k = 0; k < n; ++k) {
|
|
|
+ c[i * n + j] += a[i * n + k] * b[k * n + j];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void MatrixDot(float *a, float *b, float *c, int n) {
|
|
|
+ #pragma omp parallel for num_threads(OMP_THREADS)
|
|
|
+ for (int i = 0; i < n * n; ++i) {
|
|
|
+ c[i] = a[i] * b[i];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void MatrixAdd(float *a, float *b, float *c, int n) {
|
|
|
+ #pragma omp parallel for num_threads(OMP_THREADS)
|
|
|
+ for (int i = 0; i < n * n; ++i) {
|
|
|
+ c[i] = a[i] + b[i];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void tanh(float *x, float *y, int n) {
|
|
|
+ #pragma omp parallel for num_threads(OMP_THREADS)
|
|
|
+ for (int i = 0; i < n; ++i) {
|
|
|
+ y[i] = std::tanh(x[i]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void sigmoid(float *x, float *y, int n) {
|
|
|
+ #pragma omp parallel for num_threads(OMP_THREADS)
|
|
|
+ for (int i = 0; i < n; ++i) {
|
|
|
+ y[i] = 1.0f / (1.0f + std::exp(-x[i]));
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+###### 随时间迭代计算,MPI实现
|
|
|
+
|
|
|
+```c++
|
|
|
+ // computing
|
|
|
+ for (int i = 0; i < seq_len; ++i) {
|
|
|
+ // broadcast x
|
|
|
+ if (rank == 0) {
|
|
|
+ memcpy(sub_x, x + i * n * n, n * n * sizeof(float));
|
|
|
+ }
|
|
|
+ MPI_Bcast(sub_x, n * n, MPI_FLOAT, 0, MPI_COMM_WORLD);
|
|
|
+ // broadcast h
|
|
|
+ if (i == 0) {
|
|
|
+ for (int i = 0; i < n * n; ++i) {
|
|
|
+ sub_h[i] = 0.0f;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ MPI_Bcast(sub_h, n * n, MPI_FLOAT, 0, MPI_COMM_WORLD);
|
|
|
+ }
|
|
|
+ // matrix multply
|
|
|
+ MatrixMult(sub_w, sub_x, ifgo, n, true);
|
|
|
+ MatrixMult(sub_w + n * n, sub_h, ifgo, n);
|
|
|
+ // active
|
|
|
+ if (rank == 2) {
|
|
|
+ tanh(ifgo, ifgo, n * n);
|
|
|
+ } else {
|
|
|
+ sigmoid(ifgo, ifgo, n * n);
|
|
|
+ }
|
|
|
+ // gather result
|
|
|
+ if (rank == 0) {
|
|
|
+ MPI_Recv(ft, n * n, MPI_FLOAT, 1, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
|
|
|
+ MPI_Recv(gt, n * n, MPI_FLOAT, 2, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
|
|
|
+ MPI_Recv(ot, n * n, MPI_FLOAT, 3, 3, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
|
|
|
+ } else if (rank == 1) {
|
|
|
+ MPI_Send(ifgo, n * n, MPI_FLOAT, 0, 1, MPI_COMM_WORLD);
|
|
|
+ } else if (rank == 2) {
|
|
|
+ MPI_Send(ifgo, n * n, MPI_FLOAT, 0, 2, MPI_COMM_WORLD);
|
|
|
+ } else if (rank == 3) {
|
|
|
+ MPI_Send(ifgo, n * n, MPI_FLOAT, 0, 3, MPI_COMM_WORLD);
|
|
|
+ }
|
|
|
+ // compute ct ht
|
|
|
+ if (rank == 0) {
|
|
|
+ MatrixDot(ft, c, c, n);
|
|
|
+ MatrixDot(ifgo, gt, gt, n);
|
|
|
+ MatrixAdd(c, gt, c, n);
|
|
|
+ tanh(c, sub_h, n * n);
|
|
|
+ MatrixDot(ot, sub_h, sub_h, n);
|
|
|
+ memcpy(h + i * n * n, sub_h, n * n * sizeof(float));
|
|
|
+ }
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+*完整代码见附件*
|
|
|
+
|
|
|
##### 性能结果
|
|
|
|
|
|
+
|
|
|
+
|
|
|
## 分组实验
|
|
|
|
|
|
| 章 | 15 | 19 | 22 |
|
|
@@ -393,3 +509,33 @@ $i_t,f_t,g_t,o_t$每个部分内的矩阵乘法使用OpenMP计算,OpenMP的线
|
|
|
| 分到的程序号 | 1 | 1 | 1 |
|
|
|
| 分到的程序 | closure | gauss | fft |
|
|
|
|
|
|
+### closure
|
|
|
+
|
|
|
+##### 性能结果
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+##### 改进
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### gauss
|
|
|
+
|
|
|
+##### 性能结果
|
|
|
+
|
|
|
+| 问题规模 | 任务数 | 总运行时间 | 分发数据时间 | 并行计算时间 |
|
|
|
+| -------- | ------ | ---------- | ------------ | ------------ |
|
|
|
+| 512 | 1 | 0.387903 | 0.112848 | 0.275056 |
|
|
|
+| 1024 | 2 | 1.265614 | 0.335754 | 0.929859 |
|
|
|
+| 2048 | 4 | 4.077509 | 1.161536 | 2.915974 |
|
|
|
+| 4096 | 8 | 19.073884 | 4.172863 | 14.901021 |
|
|
|
+| 8192 | 16 | 100.614923 | 16.937205 | 83.677718 |
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+##### 改进
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### fft
|
|
|
+
|