【问题标题】:Stack overflow when passing a large object by reference通过引用传递大对象时的堆栈溢出
【发布时间】:2014-04-11 22:12:57
【问题描述】:

我有一个 c++ 类,它包含表示预设长度的向量和预设大小的矩阵的结构。每个向量只是一个双精度数组,每个矩阵都是一个向量数组。我选择不使用 C++ 提供的 Vector 类,因为我根本不需要调整向量的大小,也不会使用向量的任何实例方法。我只是在寻找我的双数组的包装器。

本课程的目标是执行 2 个大矩阵 (512x512) 的矩阵乘法,方法是将矩阵分解为更小的块,然后使用 MPI 在本地计算集群上的多个节点上执行乘法运算。当我试图将矩阵分解成更小的块时,我遇到了堆栈溢出异常的问题。这是一些代码:

// Vector Structs
struct Vec512 { double values[512]; };
struct Vec256 { double values[256]; };
struct Vec128 { double values[128]; };
struct Vec64 { double values[64]; };

// Matrix Structs
struct Mat512 { 
    Vec512 rows[512]; 
    Mat512(){}
    Mat512(MatrixInitEnum e){
        switch(e){
            case Empty:
                for(int row = 0; row < 512; row++){
                Vec512 temp;
                for(int col = 0; col < 512; col++){
                    temp.values[col] = 0;
                }
                rows[row] = temp;
            }
            break;
            case Random:
                for(int row = 0; row < 512; row++){
                    Vec512 temp;
                    for(int col = 0; col < 512; col++){
                        temp.values[col] = myRandom();
                    }
                    rows[row] = temp;
                }
                break;
            }
    }
    Vec512 GetRow(int row){
        return rows[row];
    }
    Vec512 GetColumn(int col){
        Vec512 column;
        for(int i = 0; i < 512; i++){
            column.values[i] = rows[i].values[col];
        }
        return column;
    }
    void SetValue(int row, int col, double value){
        rows[row].values[col] = value;
    }
    double GetValue(int row, int col){
        return rows[row].values[col];
    }
};
// Analogous structs for Mat256, Mat128, Mat64

/*Decomposes the big matrix into 4 256x256 matrices in row-major fashion*/
Mat256* DecomposeMatrix256(Mat512 *bigMat){
    Mat256 matArray[4];
    int beginRow, endRow, beginCol, endCol, rowOffset, colOffset;

    for(int it = 0; it < 4; it++){
        beginRow = (it/2) * 256;
        endRow = beginRow + 256;
        beginCol = (it % 2) * 256; 
        endCol = beginCol + 256;
        rowOffset = (it / 2) * 256;
        colOffset = (it % 2) * 256;

        for(int row = beginRow; row < endRow; row++){
            for(int col = beginCol; col < endCol; col++){
                double val = bigMat->GetValue(row, col);
                matArray[it].SetValue(row - rowOffset, col - colOffset, val);
            }
        }
    }

    return matArray;
}

// Analogous methods for breaking into 16 128x128 Mat128s and 64 64x64 Mat64s

那我的主要方法很简单

int main(int argc, char* argv[])
{
    cout << "Welcome, the program is now initializing the matrices.\n";

    Mat512* bigMat = new Mat512(Random);     // Creates this just fine
    Mat256* mats256 = DecomposeMatrix256(bigMat);    // Gets here and can step to the signature of the method above without issue

    // MPI code to split up the multiplication and to 
    // wait until user is ready to exit

    return 0;
}

这是我的问题所在: 我可以创建我的随机值的大 Mat512 没问题。我在创建大矩阵的点设置了一个断点,并验证它是否已成功创建。然后我进入了对 DecomposeMatrix256(Mat512 * bigMat) 的调用,发现我正在使用该方法没有问题。此外,当悬停在 bigMat 对象上时,visual studio 向我显示它确实在接收大矩阵。当我尝试单步进入该方法时,我立即得到了堆栈溢出异常。

我感到困惑的是,为什么我什至在创建另一个新对象(如 4 个 256x256 矩阵的数组)之前就会发生堆栈溢出。我很确定我是通过引用而不是值传递矩阵(我习惯于 C# 而不是 C++,所以我很高兴听到我只是在引用传递时做错了)所以我认为不会简单地传递对大矩阵的引用是一个很大的开销。

我能够通过进入项目配置设置并将堆栈保留大小从 1MB(默认值)增加到 8MB(可能有点矫枉过正,但我​​只是希望它用于我的调试目的)来解决我的问题。

有人可以解释为什么我只是传递对大矩阵的引用而不是矩阵本身(按值)时会出现溢出吗?同样,我通过增加堆栈大小使其工作,但我不明白为什么当我通过引用而不是通过值传递对象时这是必要的。

感谢您的阅读和输入。我很乐意发布与帮助理解我的问题相关的任何其他内容。

【问题讨论】:

  • 可能不相关:您在 getRow 中返回副本 - 返回引用可能更好更快
  • 谢谢,这是一个有效的观点。我会尝试改变它,看看它是否有帮助。我犹豫是否认为这是整体问题,因为当我遇到溢出时我不会调用该方法。

标签: c++ matrix stack-overflow


【解决方案1】:

这是因为在 DecomposeMatrix256 方法中,您正在堆栈上创建自动变量:

Mat256* DecomposeMatrix256(Mat512 *bigMat){
    Mat256 matArray[4];

它的大小是4*256*256*sizeof(double),在x64位机器上是4*256*256*82 097 152 字节太多,无法放入堆栈,因此会溢出。

【讨论】:

  • 谢谢lizusek。我之所以选择这个作为答案,是因为您清楚地解释了为什么它超过了 1MB 默认堆栈大小。我想我可能会从使用双精度数改为使用浮点数来减小尺寸,因为我不需要双精度数。
  • @user1816718 我有点晚了,因为计算正确的尺寸; ) 是的,使用较小的字体绝对是一个不错的决定。如果大小很大,请记住在堆上分配
  • 他已将堆栈增加到 8MB
【解决方案2】:

DecomposeMatrix256() 在堆栈上创建一个由四个 Mat256 对象组成的数组。这很可能是导致溢出的原因,因为它需要大量的堆栈空间。您传递的参数不是溢出的原因。

另一个问题是,函数返回一个指向局部变量的指针,该变量将在函数结束时超出范围。该指针将不再指向有效对象。

【讨论】:

  • 谢谢……也许我弄错了我得到的方法有多远。您是对的,创建该数组会为堆栈添加很多内容。至于局部变量指针,当它返回时,我确实还有数组中的 4 个矩阵。我现在刚刚测试了它,我确实可以使用 4 个矩阵中的每一个。
  • @user1816718:数组的内容可能仍然存在,也可能不存在。当您调用其他函数、创建新变量、更改编译器优化级别或执行其他任何操作时,它也可以部分或完全覆盖。所以你会得到损坏的数据或崩溃。技术术语是“未定义的行为”。
  • @user1816718 函数返回后无法访问此地址,行为未定义
  • 这是一个有趣的观点,我不确定我是否完全理解。我知道我正在创建一个局部变量,当我返回时它会超出范围。我只是幸运地仍然可以访问其成员吗?也许更好的解决方案是将我需要的矩阵数组设置为全局变量,这样我就可以确保它们保持在范围内?感谢你们两位对此的意见。
  • @user1816718:是的,你很幸运。指针指向局部变量曾经所在的内存位置。如果那个记忆没有改变,一切看起来都很好。但是内存很可能会在程序中稍后被其他东西重用和覆盖,从而破坏曾经属于局部变量的内容。在这种情况下,最好的方法可能是使用new 分配矩阵或整个数组并传递该指针。这样的堆分配对象将一直存在,直到您手动 delete 它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-29
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
  • 2019-03-02
  • 2015-04-04
相关资源
最近更新 更多