【问题标题】:Error in multiplying randomly generated matrices of huge sizes using C使用 C 将随机生成的大尺寸矩阵相乘时出错
【发布时间】:2020-09-24 06:06:37
【问题描述】:

我的主要目的是通过在各种环境中对 C 和 Java 中的矩阵乘法算法进行基准测试来展示虚拟化与容器化的不同之处,并得出一个合适的结论。

我选择这个算法的原因是因为矩阵乘法是各种计算机科学领域中非常常用的算法,因为它们中的大多数都处理大尺寸,我希望优化我的代码以能够执行高达至少 2000x2000 矩阵大小,这样两者之间的差异就很明显了。

我在 Linux 上使用 GCC,在 Windows 上的 Code::Blocks 中使用 C 的默认编译器(我不知道使用的是哪个版本的 GCC)。

问题是,当我在 Windows 上运行代码时,编译器只接受最大为 490x490 的大小,如果超过该大小,则转储内核。 Linux 设法克服了这个问题,但不能超过 590x590。

我一开始以为是我的设备内存造成的,于是请了几个机器好很多的朋友运行同样的代码,结果还是一样。

仅供参考:我正在运行 Pentium N3540 和 4GB DDR3 RAM。我的朋友们正在运行带有 16GB DDR4 的 i7-8750H,而另一个使用带有 8GB DDR4 的 i5-9300H。

这是我写的代码:

#include <stdio.h>
#include <stdlib.h>
#define MAX 10

int main()
{
    long i, j, k, m, n;
    printf("Enter the row dimension of the matrix: "); scanf("%ld", &m);
    printf("Enter the column dimension of the matrix: "); scanf("%ld", &n);
    long mat1[m][n], mat2[m][n], mat3[m][n];

    for(i=0; i<m; i++)
        for(j=0; j<n; j++)
        {
            mat1[i][j] = (long)rand()%MAX;
            mat2[i][j] = (long)rand()%MAX;
            mat3[i][j] = 0;
        }

    printf("\n\nThe matrix 1 is: \n");
    for(i=0; i<m; i++)
    {
        for(j=0; j<n; j++)
        {
            printf("%d\t", (int)mat1[i][j]);
        }
        printf("\n");
    }

    printf("\n\nThe matrix 2 is: \n");
    for(i=0; i<m; i++)
    {
        for(j=0; j<n; j++)
        {
            printf("%d\t", (int)mat2[i][j]);
        }
        printf("\n");
    }

    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            for (k = 0; k < n; k++)
                mat3[i][j] += mat1[i][k] * mat2[k][j];

    printf("\n\nThe resultant matrix is: \n");
    for(i=0; i<m; i++)
    {
        for(j=0; j<n; j++)
        {
            printf("%d\t", (int)mat3[i][j]);
        }
        printf("\n");
    }
    return 0;
}

【问题讨论】:

  • 使用堆而不是栈。参见 malloc
  • 有关您的问题的解释,请参阅本网站的名称 :-)
  • m 乘以n 以获得mat1mat2mat3 中的每一个所需的元素数。乘以3 以获得所有三个矩阵的元素总数。乘以 sizeof(long) (即 4 或 8)以获得所有数组所需的字节大小。将该大小与系统上堆栈的标准大小(Linux 上 8 MiB,Windows 上 1 MiB)进行比较。这应该可以帮助您了解问题所在。
  • printf("%d\t", (int)mat3[i][j]);代替printf("%ld\t", mat3[i][j]);
  • mat1[i][j] 的值应该是 0 到 MAX 还是 0 到 MAX-1

标签: c matrix optimization matrix-multiplication


【解决方案1】:

当你这样做时

long mat1[m][n], mat2[m][n], mat3[m][n];

您创建一个具有自动存储持续时间的对象(也称为变量)。这意味着该对象在函数执行时自动创建,并在函数退出时自动销毁。

C 标准没有描述如何做到这一点。这留给实施标准的系统。最常见的方法是使用所谓的堆栈。这是为您的程序预先分配的内存区域。每当您的程序调用函数时,函数内定义的任何变量都可以放在该堆栈上。这允许为此类变量非常简单和快速地分配内存。

但是,它有一个缺点 - 堆栈的大小有限(而且相当小)。所以如果一个函数使用了巨大的变量,你可能会用完堆栈内存。不幸的是,大多数系统直到为时已晚才检测到这一点。

避免这种情况的简单规则是:不要定义具有自动存储持续时间的大变量(也称为大函数局部变量)。

因此,对于您的具体示例,您应该替换:

long mat1[m][n]

long (*mat1)[n] = malloc(m * sizeof *mat1); // This defines mat1 as a pointer
if (mat1 == NULL)                           // to an array of n longs and
{                                           // allocate memory of m such arrays.
    // Out of memory                        // In other words:
    exit(1);                                // A m by n matrix of long
}

// From here on you can use mat1 as a 2D matrix
// example:
mat1[4][9] = 42;

...

// Once you are done using mat1, you need to free the memory. Like:
free(mat1);

【讨论】:

  • 非常感谢您指定这个!我从来没有考虑过使用动态内存分配的可能性(尽管我觉得我不应该错过这一点)。
【解决方案2】:

除了 @EdHeal 建议使用 malloc() 之外,您还可以简单地使用 static 使您的变量成为全局变量,强制将其放在 stack 之外的 heap - 假设你不介意不可变矩阵。

因为无论如何在这里您必须制作可变长度的数组,malloc() 自然适合您的用例。

堆栈用于小东西;大型事物的堆 - 用您自己的话说,这些是“巨大尺寸”的矩阵 :)

作为 C 编程经验丰富的人的旁注 - 为什么没有更多的 C 教科书教授堆栈堆理论和非malloc()ed auto 变量的大小限制? 为什么每个新手都必须趴着学习并在 S.O 上被告知这一点?

【讨论】:

    猜你喜欢
    • 2014-03-13
    • 1970-01-01
    • 2015-06-20
    • 1970-01-01
    • 2018-07-13
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    • 2017-06-15
    相关资源
    最近更新 更多