11

我正在构建一个小型软件引擎,我想昂贵地使用堆栈来快速迭代大量集合。但是后来我想到这可能是一个坏主意,因为堆栈不像堆那样大的内存存储。但我被堆栈的速度和缺乏动态分配编码实践所吸引。

有没有办法找出我可以在给定平台上将堆栈推多远?我主要关注移动设备,但问题可能出现在任何平台上。

4

5 回答 5

7

在 *nix 上,使用getrlimit

   RLIMIT_STACK
          The maximum size of the process stack, in bytes.  Upon
          reaching this limit, a SIGSEGV signal is generated.  To handle
          this signal, a process must employ an alternate signal stack
          (sigaltstack(2)).

在 Windows 上,使用VirtualQuery

对于第一次调用,将堆栈上任何值的地址传递给它,以获取已提交堆栈空间的基地址和大小(以字节为单位)。在堆栈向下增长的 x86 机器上,再次从基地址和 VirtualQuery 中减去大小:这将为您提供为堆栈保留的空间的大小(假设您当时并没有精确到堆栈大小的限制)。将两者相加自然会得出总筹码量。

没有独立于平台的方法,因为堆栈大小在逻辑上留给实现和主机系统 - 在嵌入式 mini-SOC 上,分配的资源比在 128GB RAM 服务器上要少。但是,您可以通过特定于 API 的调用来影响所有操作系统上特定线程的堆栈大小。

于 2013-06-22T13:01:25.450 回答
6

一个可能的可移植解决方案是自己编写一个分配器。
您不必使用进程堆栈,只需在堆中模拟它即可。
一开始分配大量内存,在上面写一个栈分配器,一边分配一边使用。
谷歌“分配器要求”以获取有关如何在 C++ 中实现它的信息。

我不确定“堆栈分配器”一词是否规范,但我的意思是您必须对必须在哪里进行分配或解除分配进行堆栈式限制。
既然您说您的算法适合这种模式,我认为这很容易。

于 2013-06-22T13:28:07.943 回答
2

为了扩展已经给出的关于为什么没有可移植的方法来做到这一点的答案,实际堆栈的整个概念并不是标准的一部分。您可以编写一个 C 或 C++ 运行时,它除了函数调用记录(内部可能是一个链表或其他东西)之外根本不使用堆栈。

堆栈是特定机器/操作系统/编译器的实现细节。因此,任何访问堆栈指标的技术都将特定于机器/操作系统/编译器。

虽然不是对您的具体问题的实际答案(尼尔斯很好地涵盖了这一点),但作为对您的问题域的建议:只需在堆中分配一大块内存。除了方便之外,没有理由认为“真正的”堆栈有任何不同。高度递归(非尾递归)算法通常需要这样做以确保它们具有几乎无界的“堆栈”。想要确保它们给出运行时错误/异常而不是使主机应用程序崩溃的脚本语言也经常这样做。为了提高效率,您可以实现“拆分堆栈”(就像 astd::deque给您的那样),或者您可以确保预先分配一个足够大的堆栈以满足您的需要。

于 2013-06-22T19:04:19.067 回答
2

在标准 C++ 中,绝对不是。以便携的方式,可能不是。在特定的操作系统中,有时。如果不出意外,您可以打开自己的可执行文件大小并检查可执行文件的标题以查看它的堆栈大小。[下一个问题当然是“在这段代码之前使用了多少堆栈” - 这很难确定]。

如果您在单独的线程中运行代码,许多(低级)线程接口允许您指定堆栈(或堆栈大小),例如 Posix 线程pthread_set_stacksize或 MS _beginthread。同样,在到达实际线程代码之前,您并不确切知道已经用完多少空间 - 但它可能不是一个巨大的数量。

当然,在嵌入式系统(例如手机)中,stacksize 通常非常小,4K、12K 或 64KB 非常正常——有时甚至比某些系统中的小很多。

另一个潜在问题是您无法真正知道堆栈上实际使用了多少空间 - 您可以在编译后的系统中进行测量,当然,如果您有一个堆栈本地数组int array[25];,我们可以知道它需要至少向上25 * sizeof(int)- 但可能有填充,编译器将寄存器保存在堆栈上,等等。

编辑,作为事后的想法:我也没有真正看到拥有两个代码路径的好处:

 if (enough_stack_space_for_something)
      use_stack_based_algorithm();
 else
      use_heap_based_algorithm();

这会增加相当多的额外开销,并且在嵌入式/移动系统中,更多的代码通常不是一个好的计划。

Edit2:另外,如果分配内存是运行时的主要部分,也许看看为什么会这样,例如对象的块创建会有所帮助?

于 2013-06-22T13:01:11.380 回答
1

在语言中没有标准的方法来做到这一点。我什至不知道能够查询的文档扩展。

然而,一些编译器有设置堆栈大小的选项。平台可以指定它在启动进程时所做的事情,和/或提供设置新线程堆栈大小的方法,甚至可能操纵现有线程。

对于小型平台,通常会知道整个内存大小,将所有数据段放在一端,为堆设置一个大小的区域(可能为 0),其余的是堆栈,从另一侧接近。

于 2013-06-22T13:00:43.110 回答