RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1573732
Accepted
Кирилл
Кирилл
Asked:2024-03-29 00:52:16 +0000 UTC2024-03-29 00:52:16 +0000 UTC 2024-03-29 00:52:16 +0000 UTC

为什么施特拉森的算法在 2047 年之后速度急剧下降?

  • 772

我注意到维度从 1 到 2047 的矩阵的计算时间超过 6 秒

从 2048 开始,它们从 44 +- 秒开始。时间如此跳跃的原因可能是什么?

对于 2047 矩阵,RAM 升至 279 MB;对于 2048,RAM 升至 1042 MB

同时,有足够的RAM,她很难只数数

using System.Diagnostics;
using System.Text;
class Program
{
    static void Main(string[] args)
    {
        int m = 0, n = 0;
        while (true)
        {
            Stopwatch stopwatch = new Stopwatch();
            InputDate(ref m, "строк m");
            InputDate(ref n, "колонок n");

            int[][] matrix1 = GenerateMatrix(m, n);
            int[][] matrix2 = GenerateMatrix(m, n);
            int nn = GetNewDimension(ref m, ref n);

            int[][] aN = Addition2SquareMatrix(matrix1, nn, m, n);          
            int[][] bN = Addition2SquareMatrix(matrix2, nn, m, n);
           
            stopwatch.Start();
            int[][] temp = MultiStrassen(aN, bN, nn);

            stopwatch.Stop();

            //OutputMatrix(matrix1);
            //OutputMatrix(matrix2);
            //OutputMatrix(GetSubmatrix(temp, m, n));
            Console.WriteLine("\n" + stopwatch.Elapsed);
            Console.ReadKey();
        }
    }
    /// <summary>
    /// Ввод количество строк и столбцов
    /// </summary>
    /// <param name="number">запись числа в переменную</param>
    /// <param name="txt">передаваемый текст</param>
    static void InputDate(ref int number, string txt)
    {
        do
        {
            Console.Write($"Введите число {txt}: ");
            number = int.Parse(Console.ReadLine());
        }
        while (number <= 0);
    }
    
    /// <summary>
    /// получаем новую степень матрицы
    /// </summary>
    /// <param name="m">строка</param>
    /// <param name="n">столбец</param>
    /// <returns>возвращаем степень матрицы</returns>
    static int GetNewDimension(ref int m, ref int n) => 1 << (int)(Math.Log2(Math.Max(m, n))+1);
    
    /// <summary>
    /// Преобразуем матрицу к виду 2^n путём добавление дополнительных строк
    /// </summary>
    /// <param name="a">передаваемый массив</param>
    /// <param name="nn">наша новая степень матрицы (размерность)</param>
    /// <param name="m">количество строк</param>
    /// <param name="n">количество столбцов</param>
    /// <returns>возвращаем обновленную матрицу</returns>
    static int[][] Addition2SquareMatrix(int[][] a, int nn, int m, int n)
    {       
        int[][] result = new int[nn][];
        for (int i = 0; i < nn; i++)
        {
            result[i] = new int[nn];
            if (i < m)
            {
                for (int j = 0; j < n; j++)
                {
                    result[i][j] = a[i][j];
                }
            }
        }           
        return result;            
    }
    /// <summary>
    /// Алгоритм Штрассена
    /// </summary>
    /// <param name="a">1 матрица</param>
    /// <param name="b">2 матрица</param>
    /// <param name="n">размерность матрицы</param>
    /// <returns>возвращаем результат</returns>
    static int[][] MultiStrassen(int[][] a, int[][] b, int n)
    {
        if (n <= 64)
        {
            return Multiply(a, b);
        }

        n = n >> 1;

        int[][] a11 = new int[n][];
        int[][] a12 = new int[n][];
        int[][] a21 = new int[n][];
        int[][] a22 = new int[n][];

        int[][] b11 = new int[n][];
        int[][] b12 = new int[n][];
        int[][] b21 = new int[n][];
        int[][] b22 = new int[n][];

        for (int i = 0; i < n; i++)
        {
            a11[i] = new int[n];
            a12[i] = new int[n];
            a21[i] = new int[n];
            a22[i] = new int[n];

            b11[i] = new int[n];
            b12[i] = new int[n];
            b21[i] = new int[n];
            b22[i] = new int[n];
        }

        SplitMatrix(a, a11, a12, a21, a22);
        SplitMatrix(b, b11, b12, b21, b22);

        int[][] p1 = MultiStrassen(Summation(a11, a22), Summation(b11, b22), n);
        int[][] p2 = MultiStrassen(Summation(a21, a22), b11, n);
        int[][] p3 = MultiStrassen(a11, Subtraction(b12, b22), n);
        int[][] p4 = MultiStrassen(a22, Subtraction(b21, b11), n);
        int[][] p5 = MultiStrassen(Summation(a11, a12), b22, n);
        int[][] p6 = MultiStrassen(Subtraction(a21, a11), Summation(b11, b12), n);
        int[][] p7 = MultiStrassen(Subtraction(a12, a22), Summation(b21, b22), n);

        int[][] c11 = Summation(Summation(p1, p4), Subtraction(p7, p5));
        int[][] c12 = Summation(p3, p5);
        int[][] c21 = Summation(p2, p4);
        int[][] c22 = Summation(Subtraction(p1, p2), Summation(p3, p6));
        return CollectMatrix(c11, c12, c21, c22);
    }

    /// <summary>
    /// Разделение на матрицу на под матрицы
    /// </summary>
    /// <param name="a">исходная матрица</param>
    /// <param name="a11">матрицы а11</param>
    /// <param name="a12">матрица а12</param>
    /// <param name="a21">матрица а21</param>
    /// <param name="a22">матрица а22</param>
    static void SplitMatrix(int[][] a, int[][] a11, int[][] a12, int[][] a21, int[][] a22)
    {
        int n = a.Length >> 1;

        for (int i = 0; i < n; i++)
        {
            Array.Copy(a[i], 0, a11[i], 0, n);
            Array.Copy(a[i], n, a12[i], 0, n);
            Array.Copy(a[i + n], 0, a21[i], 0, n);
            Array.Copy(a[i + n], n, a22[i], 0, n);
        }
    }
    /// <summary>
    /// Объединение подматриц в одну матрицу
    /// </summary>
    /// <param name="a11"></param>
    /// <param name="a12"></param>
    /// <param name="a21"></param>
    /// <param name="a22"></param>
    /// <returns>возвращаем объединённую матрицу</returns>
    static int[][] CollectMatrix(int[][] a11, int[][] a12, int[][] a21, int[][] a22)
    {
        int n = a11.Length;
        var b = (n << 1);

        int[][] a = new int[b][];
        for (int i = 0; i < b; i++)
        {
            a[i] = new int[b];
        }

        for (int i = 0; i < n; i++)
        {
            Array.Copy(a11[i], 0, a[i], 0, n);
            Array.Copy(a12[i], 0, a[i], n, n);
            Array.Copy(a21[i], 0, a[i + n], 0, n);
            Array.Copy(a22[i], 0, a[i + n], n, n);
        }

        return a;
    }
    /// <summary>
    /// Разность между матрицами
    /// </summary>
    /// <param name="a">матрица 1</param>
    /// <param name="b">матрица 2</param>
    /// <returns>результат разности матриц</returns>
    static int[][] Subtraction(int[][] a, int[][] b)
    {
        int n = a.Length;
        int m = a[0].Length;
        int[][] c = new int[n][];

        for (int i = 0; i < n; i++)
        {
            c[i] = new int[m];
            for (int j = 0; j < m; j++)
            {
                c[i][j] = a[i][j] - b[i][j];
            }
        }
        return c;
    }
    /// <summary>
    /// Умножение матриц
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <returns>результат умножения </returns>
    static int[][] Multiply(int[][] a, int[][] b)
    {
        int rowsA = a.Length;
        int columnsB = b[0].Length;
        int columnsA_rowsB = a[0].Length;

        int[][] c = new int[rowsA][];
        for (int i = 0; i < rowsA; i++)
        {
            c[i] = new int[columnsB];
            for (int j = 0; j < columnsB; j++)
            {
                int sum = 0;
                for (int k = 0; k < columnsA_rowsB; k++)
                {
                    sum += a[i][k] * b[k][j];
                }
                c[i][j] = sum;
            }
        }
        return c;
    }

    /// <summary>
    /// Суммирование матриц
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <returns></returns>
    static int[][] Summation(int[][] a, int[][] b)
    {
        int n = a.Length;
        int m = a[0].Length;
        int[][] c = new int[n][];
        for (int i = 0; i < n; i++)
        {
            c[i] = new int[m];
        }

        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                c[i][j] = a[i][j] + b[i][j];
            }
        }

        return c;
    }
    /// <summary>
    /// Убирает лишние строки матриц
    /// </summary>
    /// <param name="a"></param>
    /// <param name="n"></param>
    /// <param name="m"></param>
    /// <returns>возвращаем матрицу</returns>
    static int[][] GetSubmatrix(int[][] a, int n, int m)
    {        
        int[][] result = new int[n][];
        for (int i = 0; i < n; i++)
        {
            result[i] = new int[m];
            Array.Copy(a[i], 0, result[i], 0, m);
        }
        return result;       
    }

    /// <summary>
    /// Генерация произвольного размера матрицы
    /// </summary>
    /// <param name="m">количество строк</param>
    /// <param name="n">количество столбцов</param>
    /// <returns>возврощаем матрицу</returns>
    static int[][] GenerateMatrix(int m, int n)
    {
        Random rand = new Random();
        int[][] matrix = new int[m][];

        for (int i = 0; i < m; i++)
        {
            matrix[i] = new int[n];
            for (int j = 0; j < n; j++)
            {
                matrix[i][j] = rand.Next(1, 100);
            }
        }
        return matrix;
    }
    /// <summary>
    /// Вывод на экран матриц
    /// </summary>
    /// <param name="matrix"></param>
    static void OutputMatrix(int[][] matrix)
    {
        var sb = new StringBuilder();
        foreach (int[] row in matrix)
        {
            foreach (int element in row)
            {
                sb.Append(element);
                sb.Append(' ');
            }
            sb.Length--;
            sb.AppendLine();
        }
        Console.WriteLine($"\n" + sb);
    }
}
    
c#
  • 2 2 个回答
  • 135 Views

2 个回答

  • Voted
  1. Best Answer
    aepot
    2024-03-29T08:10:15Z2024-03-29T08:10:15Z

    为了实验的纯粹性,我运行了你的代码。

    Введите число строк m: 2047
    Введите число колонок n: 2047
    
    00:00:15.4607334
    Введите число строк m: 2048
    Введите число колонок n: 2048
    
    00:01:46.4142958
    

    首先我会回答这个问题,然后我会提出改进建议。

    那么,为什么时间会增加:每次超过两个阈值的幂(2、4、16、32、...、1024、2048)时,执行时间会增加大约 7 倍,因为有一个额外的分支分成 7 个递归调用,因为出现了另一级递归嵌套。也就是说,在if (n <= 64)触发条件之前会发生额外的分支。对于矩阵大小的所有其他增加,时间根据循环迭代次数线性增加。

    现在谈谈问题。您创建了大量几乎一次性使用的数组。假设对于 2048x2048 矩阵来说,这是数千万。而未使用和丢弃的数组是垃圾收集器关注的问题。汇编器本身可能会在汇编期间减慢代码执行速度。在调试模式下,这不是那么明显,因为执行了大量调试加载,但如果应用程序被编译成发布程序集并启动,则构建器的影响将更加明显。

    为了使汇编器的工作更容易并可能加快代码速度,可以缓存矩阵。也就是说,我不会扔掉用过的矩阵,而是将其清理并放入集合中,当我需要新的矩阵时,我会查看缓存中是否有现成的我需要的大小的矩阵并从那里拿走它。

    这是一个简单的矩阵池实现:

    public class MatrixPool
    {
        private readonly Dictionary<(int, int), Stack<int[][]>> _cache = new();
        private int _cached = 0;
        private int _allocated = 0;
    
        public int[][] Rent(int rows, int cols)
        {
            if (_cache.TryGetValue((rows, cols), out Stack<int[][] > pool) && pool.Count > 0)
            {
                _cached++;
                return pool.Pop();
            }
            _allocated++;
            return CreateMatrix(rows, cols);
        }
    
        public void Return(int[][] matrix)
        {
            ClearMatrix(matrix);
            int rows = matrix.Length;
            int cols = matrix[0].Length;
            if (_cache.TryGetValue((rows, cols), out Stack<int[][]> pool))
            {
                pool.Push(matrix);
            }
            else
            {
                Stack<int[][]> newPool = new();
                newPool.Push(matrix);
                _cache[(rows, cols)] = newPool;
            }
        }
    
        private void ClearMatrix(int[][] matrix)
        {
            for (int i = 0; i < matrix.Length; i++)
            {
                Array.Clear(matrix[i]);
            }
        }
    
        private int[][] CreateMatrix(int rows, int cols)
        {
            int[][] result = new int[rows][];
            for (int i = 0; i < rows; i++)
            {
                result[i] = new int[cols];
            }
            return result;
        }
    
        public void Clear()
        {
            _cache.Clear();
    
            // статистика использования
            Console.WriteLine($"Пул матриц очищен: кешировано={_cached}, создано={_allocated}");
            _cached = 0;
            _allocated = 0;
        }
    }
    

    所以这就是发生的事情。

    class Program
    {
        static readonly MatrixPool _pool = new();
        static void Main(string[] args)
        {
            int m = 0, n = 0;
            while (true)
            {
                Stopwatch stopwatch = new Stopwatch();
                InputDate(ref m, "строк m");
                InputDate(ref n, "колонок n");
    
                int[][] matrix1 = GenerateMatrix(m, n);
                int[][] matrix2 = GenerateMatrix(m, n);
                int nn = GetNewDimension(ref m, ref n);
    
                int[][] aN = Addition2SquareMatrix(matrix1, nn, m, n);
                int[][] bN = Addition2SquareMatrix(matrix2, nn, m, n);
    
                stopwatch.Start();
                int[][] temp = MultiStrassen(aN, bN, nn);
    
                stopwatch.Stop();
    
                //OutputMatrix(matrix1);
                //OutputMatrix(matrix2);
                //OutputMatrix(GetSubmatrix(temp, m, n));
                Console.WriteLine("\n" + stopwatch.Elapsed);
                _pool.Clear();
                Console.ReadKey();
            }
        }
    
        /// <summary>
        /// Ввод количество строк и столбцов
        /// </summary>
        /// <param name="number">запись числа в переменную</param>
        /// <param name="txt">передаваемый текст</param>
        static void InputDate(ref int number, string txt)
        {
            do
            {
                Console.Write($"Введите число {txt}: ");
                number = int.Parse(Console.ReadLine());
            }
            while (number <= 0);
        }
    
        /// <summary>
        /// получаем новую степень матрицы
        /// </summary>
        /// <param name="m">строка</param>
        /// <param name="n">столбец</param>
        /// <returns>возвращаем степень матрицы</returns>
        static int GetNewDimension(ref int m, ref int n) => 1 << (int)(Math.Log2(Math.Max(m, n)) + 1);
    
        /// <summary>
        /// Преобразуем матрицу к виду 2^n путём добавление дополнительных строк
        /// </summary>
        /// <param name="a">передаваемый массив</param>
        /// <param name="nn">наша новая степень матрицы (размерность)</param>
        /// <param name="m">количество строк</param>
        /// <param name="n">количество столбцов</param>
        /// <returns>возвращаем обновленную матрицу</returns>
        static int[][] Addition2SquareMatrix(int[][] a, int nn, int m, int n)
        {
            int[][] result = _pool.Rent(nn, nn);
            for (int i = 0; i < nn; i++)
            {
                if (i < m)
                {
                    int[] rRow = result[i];
                    int[] aRow = a[i];
    
                    for (int j = 0; j < n; j++)
                    {
                        rRow[j] = aRow[j];
                    }
                }
            }
            return result;
        }
    
        /// <summary>
        /// Алгоритм Штрассена
        /// </summary>
        /// <param name="a">1 матрица</param>
        /// <param name="b">2 матрица</param>
        /// <param name="n">размерность матрицы</param>
        /// <returns>возвращаем результат</returns>
        static int[][] MultiStrassen(int[][] a, int[][] b, int n)
        {
            if (n <= 64)
            {
                return Multiply(a, b);
            }
    
            n = n >> 1;
    
            int[][] a11 = _pool.Rent(n, n);
            int[][] a12 = _pool.Rent(n, n);
            int[][] a21 = _pool.Rent(n, n);
            int[][] a22 = _pool.Rent(n, n);
    
            int[][] b11 = _pool.Rent(n, n);
            int[][] b12 = _pool.Rent(n, n);
            int[][] b21 = _pool.Rent(n, n);
            int[][] b22 = _pool.Rent(n, n);
    
            SplitMatrix(a, a11, a12, a21, a22);
            SplitMatrix(b, b11, b12, b21, b22);
    
            int[][] sum_a11_a22 = Summation(a11, a22);
            int[][] sum_b11_b22 = Summation(b11, b22);
            int[][] p1 = MultiStrassen(sum_a11_a22, sum_b11_b22, n);
            _pool.Return(sum_a11_a22);
            _pool.Return(sum_b11_b22);
    
            int[][] sum_a21_a22 = Summation(a21, a22);
            int[][] p2 = MultiStrassen(sum_a21_a22, b11, n);
            _pool.Return(sum_a21_a22);
    
            int[][] sub_b12_b22 = Subtraction(b12, b22);
            int[][] p3 = MultiStrassen(a11, sub_b12_b22, n);
            _pool.Return(sub_b12_b22);
    
            int[][] sub_b21_b11 = Subtraction(b21, b11);
            int[][] p4 = MultiStrassen(a22, sub_b21_b11, n);
            _pool.Return(sub_b21_b11);
    
            int[][] sum_a11_a12 = Summation(a11, a12);
            int[][] p5 = MultiStrassen(sum_a11_a12, b22, n);
            _pool.Return(sum_a11_a12);
    
            int[][] sub_a21_a11 = Subtraction(a21, a11);
            int[][] sum_b11_b12 = Summation(b11, b12);
            int[][] p6 = MultiStrassen(sub_a21_a11, sum_b11_b12, n);
            _pool.Return(sub_a21_a11);
            _pool.Return(sum_b11_b12);
    
            int[][] sub_a12_a22 = Subtraction(a12, a22);
            int[][] sum_b21_b22 = Summation(b21, b22);
            int[][] p7 = MultiStrassen(sub_a12_a22, sum_b21_b22, n);
            _pool.Return(sub_a12_a22);
            _pool.Return(sum_b21_b22);
    
            _pool.Return(a11);
            _pool.Return(a12);
            _pool.Return(a21);
            _pool.Return(a22);
    
            _pool.Return(b11);
            _pool.Return(b12);
            _pool.Return(b21);
            _pool.Return(b22);
    
            int[][] sum_p1_p4 = Summation(p1, p4);
            int[][] sub_p7_p5 = Subtraction(p7, p5);
            int[][] c11 = Summation(sum_p1_p4, sub_p7_p5);
            _pool.Return(sum_p1_p4);
            _pool.Return(sub_p7_p5);
    
            int[][] c12 = Summation(p3, p5);
            int[][] c21 = Summation(p2, p4);
    
            int[][] sub_p1_p2 = Subtraction(p1, p2);
            int[][] sum_p3_p6 = Summation(p3, p6);
            int[][] c22 = Summation(sub_p1_p2, sum_p3_p6);
            _pool.Return(sub_p1_p2);
            _pool.Return(sum_p3_p6);
    
            _pool.Return(p1);
            _pool.Return(p2);
            _pool.Return(p3);
            _pool.Return(p4);
            _pool.Return(p5);
            _pool.Return(p6);
            _pool.Return(p7);
    
            int[][] result = CollectMatrix(c11, c12, c21, c22);
    
            _pool.Return(c11);
            _pool.Return(c12);
            _pool.Return(c21);
            _pool.Return(c22);
    
            return result;
        }
    
        /// <summary>
        /// Разделение на матрицу на под матрицы
        /// </summary>
        /// <param name="a">исходная матрица</param>
        /// <param name="a11">матрицы а11</param>
        /// <param name="a12">матрица а12</param>
        /// <param name="a21">матрица а21</param>
        /// <param name="a22">матрица а22</param>
        static void SplitMatrix(int[][] a, int[][] a11, int[][] a12, int[][] a21, int[][] a22)
        {
            int n = a.Length >> 1;
    
            for (int i = 0; i < n; i++)
            {
                Array.Copy(a[i], 0, a11[i], 0, n);
                Array.Copy(a[i], n, a12[i], 0, n);
                Array.Copy(a[i + n], 0, a21[i], 0, n);
                Array.Copy(a[i + n], n, a22[i], 0, n);
            }
        }
    
        /// <summary>
        /// Объединение подматриц в одну матрицу
        /// </summary>
        /// <param name="a11"></param>
        /// <param name="a12"></param>
        /// <param name="a21"></param>
        /// <param name="a22"></param>
        /// <returns>возвращаем объединённую матрицу</returns>
        static int[][] CollectMatrix(int[][] a11, int[][] a12, int[][] a21, int[][] a22)
        {
            int n = a11.Length;
            int b = n << 1;
    
            int[][] a = _pool.Rent(b, b);
    
            for (int i = 0; i < n; i++)
            {
                Array.Copy(a11[i], 0, a[i], 0, n);
                Array.Copy(a12[i], 0, a[i], n, n);
                Array.Copy(a21[i], 0, a[i + n], 0, n);
                Array.Copy(a22[i], 0, a[i + n], n, n);
            }
    
            return a;
        }
    
        /// <summary>
        /// Разность между матрицами
        /// </summary>
        /// <param name="a">матрица 1</param>
        /// <param name="b">матрица 2</param>
        /// <returns>результат разности матриц</returns>
        static int[][] Subtraction(int[][] a, int[][] b)
        {
            int n = a.Length;
            int m = a[0].Length;
            int[][] c = _pool.Rent(n, m);
    
            for (int i = 0; i < n; i++)
            {
                int[] cRow = c[i];
                int[] aRow = a[i];
                int[] bRow = b[i];
    
                for (int j = 0; j < m; j++)
                {
                    cRow[j] = aRow[j] - bRow[j];
                }
            }
            return c;
        }
    
        /// <summary>
        /// Умножение матриц
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns>результат умножения </returns>
        static int[][] Multiply(int[][] a, int[][] b)
        {
            int rowsA = a.Length;
            int columnsB = b[0].Length;
            int columnsA_rowsB = a[0].Length;
    
            int[][] c = _pool.Rent(rowsA, columnsB);
            for (int i = 0; i < rowsA; i++)
            {
                int[] cRow = c[i];
                int[] aRow = a[i];
    
                for (int j = 0; j < columnsB; j++)
                {
                    int sum = 0;
                    for (int k = 0; k < columnsA_rowsB; k++)
                    {
                        sum += aRow[k] * b[k][j];
                    }
                    cRow[j] = sum;
                }
            }
            return c;
        }
    
        /// <summary>
        /// Суммирование матриц
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        static int[][] Summation(int[][] a, int[][] b)
        {
            int n = a.Length;
            int m = a[0].Length;
            int[][] c = _pool.Rent(n, m);
    
            for (int i = 0; i < n; i++)
            {
                int[] cRow = c[i];
                int[] aRow = a[i];
                int[] bRow = b[i];
    
                for (int j = 0; j < m; j++)
                {
                    cRow[j] = aRow[j] + bRow[j];
                }
            }
    
            return c;
        }
    
        /// <summary>
        /// Убирает лишние строки матриц
        /// </summary>
        /// <param name="a"></param>
        /// <param name="n"></param>
        /// <param name="m"></param>
        /// <returns>возвращаем матрицу</returns>
        static int[][] GetSubmatrix(int[][] a, int n, int m)
        {
            int[][] result = _pool.Rent(n, m);
            for (int i = 0; i < n; i++)
            {
                Array.Copy(a[i], 0, result[i], 0, m);
            }
            return result;
        }
    
        /// <summary>
        /// Генерация произвольного размера матрицы
        /// </summary>
        /// <param name="m">количество строк</param>
        /// <param name="n">количество столбцов</param>
        /// <returns>возврощаем матрицу</returns>
        static int[][] GenerateMatrix(int m, int n)
        {
            Random rand = new Random();
            int[][] matrix = _pool.Rent(m, n);
    
            for (int i = 0; i < m; i++)
            {
                int[] mRow = matrix[i];
                for (int j = 0; j < n; j++)
                {
                    mRow[j] = rand.Next(1, 100);
                }
            }
            return matrix;
        }
    
        /// <summary>
        /// Вывод на экран матриц
        /// </summary>
        /// <param name="matrix"></param>
        static void OutputMatrix(int[][] matrix)
        {
            var sb = new StringBuilder();
            foreach (int[] row in matrix)
            {
                foreach (int element in row)
                {
                    sb.Append(element);
                    sb.Append(' ');
                }
                sb.Length--;
                sb.AppendLine();
            }
            Console.WriteLine($"\n" + sb);
        }
    }
    

    Также применил кеширование массивов по индексам, в методах сложения, вычитания и умножения матриц.

    int[] cRow = c[i];
    int[] aRow = a[i];
    int[] bRow = b[i];
    

    Это позволяет не искать массив по индексу каждый раз при обращении к его элементу, что тоже добавляет скорости.

    Запускаю

    Введите число строк m: 2047
    Введите число колонок n: 2047
    
    00:00:12.5554179
    Пул матриц очищен: кешировано=92348, создано=90
    Введите число строк m: 2048
    Введите число колонок n: 2048
    
    00:01:28.8709426
    Пул матриц очищен: кешировано=646962, создано=107
    

    То есть ~99% использованных матриц - это использованные повторно. То есть нагрузка на сборщик мусора сократилась до минимума, её практически нет. Именно за счёт этого немного подросла производительность кода. Учтите, что код работы методов пула матриц тоже добавил свою нагрузку, но всё равно стало быстрее.

    Но самое интересное, здесь даже не скорость, а то что пик расхода памяти - 695 мегабайт в отладчике.

    С пулом надо выполнять всего-лишь одно правило. Если вы отдали матрицу в пул, то вы не должны далее её использовать.

    Кстати, пробовал переписать этот код на двухмерные int[,] массивы, но работает медленнее процентов на 20, поэтому не стал показывать этот вариант.


    Кстати, методы суммирования и вычитания легко векторизовать

    static int[][] Subtraction(int[][] a, int[][] b)
    {
        int n = a.Length;
        int m = a[0].Length;
        int[][] c = _pool.Rent(n, m);
    
        int vectorsCount = m / Vector<int>.Count;
        int remainderOffset = vectorsCount * Vector<int>.Count;
    
        for (int i = 0; i < n; i++)
        {
            int[] cRow = c[i];
            int[] aRow = a[i];
            int[] bRow = b[i];
    
            if (vectorsCount > 0)
            {
                Span<Vector<int>> cVectors = MemoryMarshal.Cast<int, Vector<int>>(cRow);
                Span<Vector<int>> aVectors = MemoryMarshal.Cast<int, Vector<int>>(aRow);
                Span<Vector<int>> bVectors = MemoryMarshal.Cast<int, Vector<int>>(bRow);
    
                for (int j = 0; j < vectorsCount; j++)
                {
                    cVectors[j] = aVectors[j] - bVectors[j];
                }
            }
                    
            for (int j = remainderOffset; j < m; j++)
            {
                cRow[j] = aRow[j] - bRow[j];
            }
        }
        return c;
    }
    
    static int[][] Summation(int[][] a, int[][] b)
    {
        int n = a.Length;
        int m = a[0].Length;
        int[][] c = _pool.Rent(n, m);
    
        int vectorsCount = m / Vector<int>.Count;
        int remainderOffset = vectorsCount * Vector<int>.Count;
    
        for (int i = 0; i < n; i++)
        {
            int[] cRow = c[i];
            int[] aRow = a[i];
            int[] bRow = b[i];
    
            if (vectorsCount > 0)
            {
                Span<Vector<int>> cVectors = MemoryMarshal.Cast<int, Vector<int>>(cRow);
                Span<Vector<int>> aVectors = MemoryMarshal.Cast<int, Vector<int>>(aRow);
                Span<Vector<int>> bVectors = MemoryMarshal.Cast<int, Vector<int>>(bRow);
    
                for (int j = 0; j < vectorsCount; j++)
                {
                    cVectors[j] = aVectors[j] + bVectors[j];
                }
            }
    
            for (int j = remainderOffset; j < m; j++)
            {
                cRow[j] = aRow[j] + bRow[j];
            }
        }
    
        return c;
    }
    

    Это добавит ещё немного производительности.

    Введите число строк m: 2047
    Введите число колонок n: 2047
    
    00:00:11.2596866
    Пул матриц очищен: кешировано=92348, создано=90
    Введите число строк m: 2048
    Введите число колонок n: 2048
    
    00:01:18.7540629
    Пул матриц очищен: кешировано=646962, создано=107
    

    На моём процессоре один вектор это 8 интов, то есть 8 сумм или разностей за одну итерацию цикла. Если не знакомы с векторами, почитайте что-нибудь про SIMD и AVX инструкции процессора.

    • 4
  2. Stanislav Volodarskiy
    2024-03-30T02:53:07Z2024-03-30T02:53:07Z

    Функция GetNewDimension(m, n) считает размер до которого надо растянуть матрицы, чтобы с ними мог работать алгоритм Штрассена. Это должна быть степень двойки:

    n (= m)   GetNewDimension(m, n)
     2047            2048
     2048            4096                      Зачем?
     2049            4096
    

    Зачем 2048 увеличивается до 4096? 2048 уже степень двойки. Можно было оставить без изменений:

    static int GetNewDimension2(ref int m, ref int n) =>
        1 << (int)Math.Ceiling(Math.Log2(Math.Max(m, n)));
    

    Теперь ступенька на 2048 пропала. Но она перебралась на 2049. Это нормально, алгоритм Штрассена переключается на следующую степень двойки и замедляется примерно в семь раз.

    Нам не нужно чтобы размер матрицы был степенью двойки. Достаточно чтобы размер матрицы делился пополам до тех пор пока он не станет ≤ 64. Матрицы маленьких размеров умножаются обычным алгоритмом умножения, который работает с любыми размерами матриц.

    Если 2049 увеличить до 2112, то алгоритм Штрассена поделит размер пополам шесть раз и получит матрицу размера 33 (= 2112 / 26), которую передаст обычному умножению.

    Вот новая функция:

    static int GetNewDimension(ref int m, ref int n)
    {
        int b = 64;
        int k = Math.Max(n, m);
        int f = 1 << (int)Math.Ceiling(Math.Log2((k + b - 1) / b));
        return f * ((k + f - 1) / f);
    }
    

    Времена работы на моей машине. Хотя ступеньки остались, они стали гораздо ниже:

    n (= m)   GetNewDimension(m, n)     Время работы
     2047            2048                  14.18
     2048            2048                  14.19
     2049            2112                  17.48
     2050            2112                  17.85
     ...
     2111            2112                  17.80
     2112            2112                  17.46
     2113            2176                  18.86
     2114            2176                  18.99
     ...
     2175            2176                  18.74
     2176            2176                  18.80
     2177            2240                  20.04
     2178            2240                  20.03
    

    P.S. От ступенек можно избавиться полностью, но потребуется много переделок в основной процедуре, а выигрыш в сравнении с текущим вариантом составит не более десяти процентов. А так дёшево и сердито.

    • 3

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5