似乎有一种观点认为,在64位架构上使用“拆分堆栈”运行时模型是不必要的。我说似乎是,因为我还没见过有人这么说,只是围着它跳舞:
典型多线程程序的内存使用量可以显著减少,因为每个线程不需要最坏的堆栈大小。可以在32位地址空间中运行数百万个线程(无论是完整的NPTL线程还是共同例程)。-- 伊恩·兰斯·泰勒
...implying,一个64位的地址空间已经可以处理它.
还有..。
..。分裂堆栈和狭窄用例的持续开销(在32位架构上产生大量的I/O绑定任务)是不可接受的.-- 布斯林
两个问题:他们是这么说的吗?第二,如果是这样的话,为什么他们在64位架构上不相关呢?
发布于 2013-10-18 14:11:39
是的,他们就是这么说的。
在64位架构上,拆分堆栈(目前)是不必要的,因为64位虚拟地址空间非常大,因此可以包含数百万堆栈地址范围,如果需要,每个堆栈地址空间都可以包含整个32位地址空间。
在目前使用的平面记忆模型中,虚拟地址到语音存储位置的转换是在硬件MMU的支持下完成的。在amd64上,将64位虚拟地址空间的大块保留到正在创建的每个新堆栈上更好(这意味着总体速度更快),而只将第一页(4kB)映射到实际内存。这样,栈将能够根据需要增长和缩小,超过连续的虚拟地址(意味着每个功能序言中的代码较少,这是一个很大的优化),而操作系统则重新配置MMU,将每一页虚拟地址映射到一个实际的空闲内存页面,每当堆栈在某些可配置阈值之上或以下扩展或缩小。
通过巧妙地选择阈值(例如,请参阅动态阵列理论),您可以在平均堆栈操作上实现O(1)复杂性,同时保留数百万堆栈的好处,这些堆栈可以根据您的需要增长,并且只消耗它们使用的内存。
PS:目前的Go实现远远落后于这其中的任何一个:-)
发布于 2013-10-18 13:43:01
发布于 2014-03-27 10:21:24
更新Go 1.4 (Q4,2014年)
更改为运行时
到1.4,运行时(垃圾收集器、并发支持、接口管理、映射、切片、字符串、.)大部分是用C编写的,有一些汇编程序支持。 在1.4中,已经翻译了很多代码,以便垃圾收集器能够在运行时扫描程序堆栈,并获得有关哪些变量是活动的的准确信息。 这种重写允许1.4中的垃圾收集器完全准确,这意味着它知道程序中所有活动指针的位置。这意味着堆将更小,因为不会出现假阳性,从而保持非指针活动。其他相关的更改也会减少堆的大小,与以前的版本相比,堆的大小总体上要小10%-30%。 其结果是堆栈不再被分割,从而消除了“热分割”问题。当达到堆栈限制时,将分配一个新的、更大的堆栈,在那里复制goroutine的所有活动帧,并更新堆栈中的任何指针。
初步答复(2014年3月)
Anastasopoulo的文章"围棋中的连续堆栈“也谈到了这个问题。
在这样的情况下,堆栈边界恰好落在一个紧密的循环中,重复创建和销毁段的开销就变得很大。 这被称为围棋社区内部的“热分割”问题。 “热分割”将在Go 1.3中通过实现连续堆栈来解决。 现在,当堆栈需要增长,而不是分配一个新段时,会发生以下情况:
下面提到一个主要出现在32位结构中的问题:
不过,也有一定的挑战。 1.2运行时不知道堆栈中指针大小的单词是否是实际的指针,或者不是。可能会有浮点数,而最少见的整数,如果被解释为指针,实际上会指向数据。 由于缺乏这样的知识,垃圾收集器必须保守地考虑堆栈帧中的所有位置为根。这就留下了内存泄漏的可能性,特别是在32位架构上,因为它们的地址池要小得多。 然而,在复制堆栈时,必须避免这种情况,并且在重新调整时只应考虑真正的指针。 虽然已经完成了工作和有关活动堆栈指针的信息现在嵌入到二进制文件中,并可用于运行时。 这不仅意味着1.3中的收集器可以http://talks.golang.org/2014/go1.3.slide#4堆栈数据,而且现在可以重新调整堆栈指针。
https://stackoverflow.com/questions/19450145
复制相似问题