大数组,std :: vector和堆栈溢出

时间:2016-05-25 04:40:51

标签: c++ arrays c++11 vector stack

我有一个从大型数组中读取数据的程序,我最初在Visual Studio中将程序划分为两个独立的项目,每个项目分别工作正常,但是当我尝试将它们放在一起时,程序的行为很有趣,同时跳过了一些步骤调试。我是C ++的新手,所以我开始做一些研究,我发现也许我正在用那些巨大的数组填充堆栈,我应该尝试将它们放在堆上。

我决定更改std::vector的每个数组并以这种方式初始化它们:

std::vector<double> meanTimeAO = { 0.4437, 0.441, 0.44206, 0.44632, 0.4508, 0.45425,...}

但是当我尝试编译编译器崩溃时堆栈溢出后更改所有数组后,我以为我通过将数组更改为向量来释放堆栈中的内存空间,但似乎我得到了相反的结果,为什么会这样?

我应该如何处理这些大数组? (它们是固定不变的值或大小)

3 个答案:

答案 0 :(得分:9)

如果阵列大小固定,且其元素没有变化,则无需使用vector。您可以使用std::arrayconstconstexpr数组。

constexpr float meanTimeAO[] = { 0.4437f, 0.441f, 0.44206f, 0.44632f, 0.4508f, 0.45425f,...}

答案 1 :(得分:9)

正如@ Ajay的回答和@Cornstalks评论正确指出你可以通过在数组上使用staticconstexpr限定符完全避免堆栈和堆

const static std::array<float, 1000000> a1 = {}; // OK
constexpr    std::array<float, 1000000> a2 = {}; // OK in C++11 onwards

这会将数组存储在内存的数据初始化部分(good explanation here)中。 const仅用于禁止修改a1,并且不需要避免堆栈溢出。声明为constexpr的变量也会自动const,因此不需要限定符。

注意:你也可以通过制作数组全局变量来实现static的效果,尽管我推荐这个。

程序堆栈溢出

如果您的数据是非静态的,那么当元素数量非常大时,您应该使用std::vector(或其他类型的堆分配内存)。

std::array<float, 1000000> a = {};   // Causes stack-overflow on 32-bit MSVS 2015
std::vector<float> v(1000000);       // OK

这是因为默认堆栈大小约为1MB,而100万个浮点数需要大约4MB。堆的大小受系统可用内存(RAM)的限制。 More on the stack and heap here

std::vector的缺点是它比std::array慢一点(堆内存分配,解除分配和访问都比堆栈的速度慢),而且它是&#39}。不是固定的大小。但是,您可以将std::vector声明为const,以防止您(或其他人)意外更改其大小或元素。

const std::vector<float> v = {...}; 

现在,为什么你的std::vector导致堆栈溢出有点神秘。但是,当std::vector在堆上分配其元素时,它还会在堆栈上分配指针(32位上为4字节,64位上为8字节)。因此,如果您同时在范围内超过~250,000 std::vector,这也会导致堆栈溢出(或64位系统上的~125,000)。

编译器堆栈溢出

编译器像任何程序一样,分配内存 - 其中一些将在堆栈中。 MSVC上编译器堆栈溢出的官方错误是Fatal Error C1063

鉴于您的调试器表现得很奇怪,我的建议是尝试通过手动将代码拆分为模块化单元并单独编译来隔离有问题的代码。通过占用大量堆栈,例如可能少量代码可能导致错误。通过递归生成大量函数。

或者,可能是您的代码本质上非常复杂,自然需要比堆栈更多的内存。在这种情况下,拆分代码仍然有用,但您也可以尝试increasing the default stack size of MSVC

改进您的代码

要改进代码,您可以尝试将数据拆分为块。例如,你可以:读入大约256 KB的数组,处理它,将数组写回文件,然后转到下一个256 KB。您可以进一步选择块的大小小于L1缓存的大小(因此它可以一次存储),这可以通过最小化缓存未命中来提高性能。

备注

  1. MSVS 2015(更新2)在编译时产生内部编译器错误

    #include "stdafx.h"
    #include <array>
    int main()
    {
         constexpr std::array<int, 1000000> a = {};
         return 0;
    }
    

    static const变体工作正常,如果我将a移到main之外(使其成为全局变量),那么它也可以正常工作。

  2. 没有chkstk.asm是不寻常的。我的位于 C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src\i386\chkstk.asm。如果您错过了它,那么可以尝试重新安装MS Visual Studio。

答案 2 :(得分:2)

在上述答案中可能忽略了一些事情:

  1. “程序在调试时表现得很有趣,跳过了一些步骤”。 OP,你能提供一些关于你的意思的更多细节吗?也许,您正在调试发布版本,当您逐步执行代码时,您会发现正在执行的代码行不是您希望下一步执行的代码行(对于使用编译器优化的发布版本,这是完全正确的行为)。 / p>

  2. 问题表明编译器因堆栈溢出而崩溃。不是执行的程序。所以问题是编译器问题。当然,更改代码可能会使编译器崩溃,但上面关于在堆栈上分配std :: vector的注释与可能导致编译器崩溃的内容无关。

  3. 建议:您可以尝试查看您使用的编译器版本中是否存在任何已知错误(例如,查看您的编译器供应商是否发布了可能解决编译器崩溃问题的更新版本。)

    另外,特别注意你的“它们永远不会改变值或大小”注释,尝试将数据放入静态const双数组,而不是std :: vectors(甚至链接列表)。当你可能只使用double []时,一个不变的静态分配的只读链表是浪费时间的。静态const数据在编译/链接时而不是运行时初始化,并且存在于它自己的内存区域(严格来说,无论是堆栈还是堆栈)。