返回介绍

5.9 并行算法:矩阵乘法

发布于 2024-08-21 22:20:21 字数 4517 浏览 0 评论 0 收藏 0

我在第一章中已经提到,Linus认为并行程序目前只有在服务端程序和图像处理领域有发展的空间。且不论这种说法是否正确,但从中也可以看出并发对于这两个应用领域的重要性。而对于图像处理来说,矩阵运行是其中必不可少的重要数学方法。当然,除了图像处理,矩阵运算在神经网络、模式识别等领域也有着广泛的用途。在这里,我将向大家介绍矩阵运算的典型代表——矩阵乘法的并行化实现。

在矩阵乘法中,第一个矩阵的列数和第二个矩阵的行数必须是相同的。如图5.16所示,矩阵A和矩阵B相乘,其中矩阵A为4行2列,矩阵B为2行4列,它们相乘后,得到的是4行4列的矩阵,并且新矩阵中每一个元素为矩阵A和B对应行列的乘积求和。

图5.16 矩阵相乘示意图

如果需要进行并行计算,一种简单的策略是可以将A矩阵进行水平分割,得到子矩阵A1和A2,B矩阵进行垂直分割,得到子矩阵B1和B2。此时,我们只要分别计算这些子矩阵的乘积,将结果进行拼接,就能得到原始矩阵A和B的乘积。如图5.17所示,展示了这种并行计算的策略。

图5.17 矩阵拆分进行并行计算

当然,这个过程是可以反复进行的。为了计算A1*B1,我们还可以进一步将A1和B1进行分解,直到我们认为子矩阵的大小已经在可接受范围内。

这里,我们使用ForkJoin框架来实现这个并行矩阵相乘的想法。为了方便矩阵计算,我们使用jMatrices开源软件,作为矩阵计算的工具。其中,使用的主要API如下:

· Matrix:代表一个矩阵

· MatrixOperator.multiply(Matrix, Matrix):矩阵相乘

· Matrix.row():获得矩阵的行数

· Matrix.getSubMatrix():获得矩阵的子矩阵

· MatrixOperator.horizontalConcatenation(Matrix,Matrix):将两个矩阵进行水平连接

· MatrixOperator.verticalConcatenation(Matrix,Matrix):将两个矩阵进行垂直连接

为了计算矩阵乘法,定义一个任务类MatrixMulTask。它会进行矩阵相乘的计算,如果输入矩阵的粒度比较大,则会再次进行任务分解:

01 public class MatrixMulTask extends RecursiveTask<Matrix> {
02   Matrix m1;
03   Matrix m2;
04   String pos;
05
06   public MatrixMulTask(Matrix m1, Matrix m2, String pos) {
07     this.m1 = m1;
08     this.m2 = m2;
09     this.pos = pos;
10   }
11
12   @Override
13   protected Matrix compute() {
14     //System.out.println(Thread.currentThread().getId()+":"+Thread.currentThread().
getName() + " is start");
15     if (m1.rows() <= PMatrixMul.granularity || m2.cols() <= PMatrixMul.granularity) {
16       Matrix mRe = MatrixOperator.multiply(m1, m2);
17       return mRe;
18     } else {
19       // 如果不是,那么继续分割矩阵
20       int rows;
21       rows = m1.rows();
22       // 左乘的矩阵横向分割
23       Matrix m11 = m1.getSubMatrix(1, 1, rows / 2, m1.cols());
24       Matrix m12 = m1.getSubMatrix(rows / 2 + 1, 1, m1.rows(), m1.cols());
25       // 右乘矩阵纵向分割
26       Matrix m21 = m2.getSubMatrix(1, 1, m2.rows(), m2.cols() / 2);
27       Matrix m22 = m2.getSubMatrix(1, m2.cols() / 2 + 1, m2.rows(), m2.cols());
28
29       ArrayList<MatrixMulTask> subTasks = new ArrayList<MatrixMulTask>();
30       MatrixMulTask tmp = null;
31       tmp = new MatrixMulTask(m11, m21, "m1");
32       subTasks.add(tmp);
33       tmp = new MatrixMulTask(m11, m22, "m2");
34       subTasks.add(tmp);
35       tmp = new MatrixMulTask(m12, m21, "m3");
36       subTasks.add(tmp);
37       tmp = new MatrixMulTask(m12, m22, "m4");
38       subTasks.add(tmp);
39       for (MatrixMulTask t : subTasks) {
40         t.fork();
41       }
42       Map<String, Matrix> matrixMap = new HashMap<String, Matrix>();
43       for (MatrixMulTask t : subTasks) {
44         matrixMap.put(t.pos, t.join());
45       }
46       Matrix tmp1 = MatrixOperator.horizontalConcatenation(matrixMap.get("m1"),
matrixMap.get("m2"));
47       Matrix tmp2 = MatrixOperator.horizontalConcatenation(matrixMap.get("m3"),
matrixMap.get("m4"));
48       Matrix reM = MatrixOperator.verticalConcatenation(tmp1, tmp2);
49       return reM;
50     }
51   }
52 }

MatrixMulTask类由三个参数构成,分别是需要计算的矩阵双方,以及计算结果位于父矩阵相乘结果中的位置,如图5.18所示。

图5.18 矩阵分解方式

MatrixMulTask中的成员变量m1和m2表示要相乘的两个矩阵,pos表示这个乘积结果在父矩阵相乘结果中所处的位置,有m1、m2、m3和m4等四种。代码第23~27行先对矩阵进行分割,分割后得到m11、m12、m21和m22等四个矩阵,并将它们按照如图5.18所示的规则进行子任务的创建。在第39~41行,计算这些子任务。在子任务返回后,在第42~48行将返回的四个矩阵m1、m2、m3和m4拼接成新的矩阵作为最终结果。

如果矩阵的粒度足够小就直接进行运算而不进行分解(第16行)。

使用这个任务类可以很容易地进行矩阵并行运算,下面是使用方法:

01 public static final int granularity=3;
02 public static void main(String[] args) throws InterruptedException, ExecutionException
{
03   ForkJoinPool forkJoinPool = new ForkJoinPool();
04   Matrix m1=MatrixFactory.getRandomIntMatrix(300, 300, null);
05   Matrix m2=MatrixFactory.getRandomIntMatrix(300, 300, null);
06   MatrixMulTask task=new MatrixMulTask(m1,m2,null);
07   ForkJoinTask<Matrix> result = forkJoinPool.submit(task);
08   Matrix pr=result.get();
09   System.out.println(pr);
10 }

上述代码中第4~5行创建两个300*300的随机矩阵。构造矩阵计算任务MatrixMulTask并将其提交给ForkJoinPool线程池。第8行执行ForkJoinTask.get()方法等待并获得最终结果。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文