【问题标题】:Cannon's algorithm of matrix multiplicationCannon 的矩阵乘法算法
【发布时间】:2017-09-12 09:32:04
【问题描述】:

我尝试实现 Cannon 的矩阵乘法算法。 我阅读了关于提供下一个伪代码的wikipedia 的描述:

   row i of matrix a is circularly shifted by i elements to the left.
   col j of matrix b is circularly shifted by j elements up.
   Repeat n times:
       p[i][j] multiplies its two entries and adds to running total.
       circular shift each row of a 1 element left
       circular shift each col of b 1 element up

接下来我在 C# 上实现了它:

    public static void ShiftLeft(int[][] matrix, int i, int count)
    {
        int ind = 0;
        while (ind < count)
        {
            int temp = matrix[i][0];
            int indl = matrix[i].Length - 1;
            for (int j = 0; j < indl; j++)
                matrix[i][j] = matrix[i][j + 1];
            matrix[i][indl] = temp;
            ind++;
        }
    }
    public static void ShiftUp(int[][] matrix, int j, int count)
    {
        int ind = 0;
        while (ind < count)
        {
            int temp = matrix[0][j];
            int indl = matrix.Length - 1;
            for (int i = 0; i < indl; i++)
                matrix[i][j] = matrix[i + 1][j];
            matrix[indl][j] = temp;
            ind++;
        }
    }

    public static int[][] Cannon(int[][] A, int[][] B)
    {
        int[][] C = new int[A.Length][];
        for (int i = 0; i < C.Length; i++)
            C[i] = new int[A.Length];
        for (int i = 0; i < A.Length; i++)
            ShiftLeft(A, i, i);

        for (int i = 0; i < B.Length; i++)
            ShiftUp(B, i, i);

        for (int k = 0; k < A.Length; k++)
        {
            for (int i = 0; i < A.Length; i++)
            {
                for (int j = 0; j < B.Length; j++)
                {
                    var m = (i + j + k) % A.Length;
                    C[i][j] += A[i][m] * B[m][j];
                    ShiftLeft(A, i, 1);
                    ShiftUp(B, j, 1);
                }

            }
        };

        return C;

    }

此代码返回正确的结果,但执行速度很慢。甚至比简单的矩阵乘法算法慢得多。

对于矩阵 200x200,我得到了结果:

00:00:00.0490432 //naive algorithm
00:00:07.1397479 //Cannon's algorithm

我做错了什么?


编辑

感谢 SergeySlepov,平行进行是不好的尝试。当我回到顺序实现时,我得到了下一个结果:

Count       Naive               Cannon's

200    00:00:00.0492098    00:00:08.0465076

250    00:00:00.0908136    00:00:22.3891375

300    00:00:00.1477764    00:00:58.0640621

350    00:00:00.2639114    00:01:51.5545524

400    00:00:00.4323984    00:04:50.7260942

好的,这不是并行实现,但我怎样才能正确地做到这一点?

【问题讨论】:

  • 您不需要实际上移动元素。像A[(i + shift) % len] 这样的东西可以解决问题。
  • 这个算法是一个分布式算法。除非您有(很多)机器将其应用于比 200x200 大得多的矩阵,否则您不太可能看到改进。
  • @SergeySlepov 谢谢,我想过,但没能得到它(似乎很难正确地做到)
  • @IVlad 谢谢,我知道,但在我看来,差异很大。是不是太大了?
  • @SergeySlepov,是的,速度下降了两倍。所以,数组数组获胜:)

标签: c# algorithm matrix parallel-processing matrix-multiplication


【解决方案1】:

Cannon's algorithm 是为“Distributed Memory 机器”(处理器网格,每个处理器都有自己的内存)构建的。这与您运行它的硬件(一些具有共享内存的处理器)非常不同,这就是为什么您没有看到任何性能提升的原因。

您引用的伪代码中的“循环移位”实际上模拟了处理器之间的数据传输。在初始矩阵“倾斜”之后,网格中的每个处理器都会跟踪三个数字(a、b 和 c)并执行类似于以下的伪代码:

c += a * b;
pass 'a' to the processor to your left (wrapping around)
pass 'b' to the processor to 'above' you (wrapping around)
wait for the next iteration of k

我们可以使用 NxN 线程在 PC 上模拟这种行为,但上下文切换(或生成 Tasks)的开销会扼杀所有的乐趣。为了充分利用 PC 的 4 个(左右)CPU,我们可以在 i 上并行循环。 k 上的循环必须是连续的(与您的解决方案不同),否则您可能会面临竞争条件,因为 k 的每次迭代都会修改矩阵 A、B 和 C。在“分布式内存机器”中,竞争条件不是处理器不共享任何内存的问题。

【讨论】:

  • 感谢您的回答。对我来说还有一些不清楚的地方,我的意思是这部分 - “为了充分利用 PC 的 4 个(左右)CPU,我们可以在 i 上进行循环并行。k 上的循环需要是顺序的(与您的解决方案不同) , 否则您可能会面临竞争条件,因为 k 的每次迭代都会修改矩阵 A、B 和 C。”。无论如何,谢谢 - 我明天会测试它。
  • 什么不清楚?如果你能把你的想法变成一个问题,我也许可以帮助你。
  • 我的意思是,如果我只是将“i”的循环更改为“Parallel.For”,我仍然会遇到竞争条件,不是吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-27
  • 1970-01-01
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多