Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading amd64上不必要的拆分堆栈_Multithreading_Go_64 Bit_Callstack_Rust - Fatal编程技术网

Multithreading amd64上不必要的拆分堆栈

Multithreading amd64上不必要的拆分堆栈,multithreading,go,64-bit,callstack,rust,Multithreading,Go,64 Bit,Callstack,Rust,似乎有一种观点认为,在64位体系结构上不需要使用“拆分堆栈”运行时模型。我说似乎是,因为我没有看到任何人真的这么说,只是绕着它跳舞: 典型多线程程序的内存使用率可以降低 值得注意的是,每个线程不需要最坏情况堆栈 尺寸。可以运行数百万个线程(无论是完整的NPTL 线程或联合例程)在32位地址空间中。 -- …这意味着64位地址空间已经可以处理它 而且 。。。拆分堆栈的恒定开销和狭窄的用例 (在32位体系结构上产生大量I/O绑定任务) 这是不可接受的。。。 -- 两个问题:这就是他们所说的吗?第二,

似乎有一种观点认为,在64位体系结构上不需要使用“拆分堆栈”运行时模型。我说似乎是,因为我没有看到任何人真的这么说,只是绕着它跳舞:

典型多线程程序的内存使用率可以降低 值得注意的是,每个线程不需要最坏情况堆栈 尺寸。可以运行数百万个线程(无论是完整的NPTL 线程或联合例程)在32位地址空间中。 --

…这意味着64位地址空间已经可以处理它

而且

。。。拆分堆栈的恒定开销和狭窄的用例 (在32位体系结构上产生大量I/O绑定任务) 这是不可接受的。。。 --


两个问题:这就是他们所说的吗?第二,如果是这样,为什么64位体系结构上不需要它们?

Go核心团队目前有可能在未来的Go版本中使用连续堆栈

拆分堆栈的方法很有用,因为堆栈可以更灵活地增长,但它还需要运行时分配相对较大的内存块,以便将这些堆栈分布到不同的位置。关于Go的内存使用有很多问题,部分原因在于此

制作连续但可增长(可重定位)的堆栈是一个选项,它将提供同样的灵活性,并可能减少对Go内存使用的混淆。以及在内存不足的机器上修复一些不好的角落案例(参见链接线程)


至于32位体系结构与64位体系结构的优缺点,我认为没有任何直接与分段堆栈的使用直接相关。

是的,他们是这么说的

在64位体系结构上(目前)不需要拆分堆栈,因为64位虚拟地址空间非常大,它可以包含数百万个堆栈地址范围,如果需要,每个堆栈都可以包含整个32位地址空间

在当今的应用中,从虚拟地址到物理内存位置的转换是在计算机的支持下完成的。事实证明,最好(也就是说,总体上更快)将64位虚拟地址空间的大块保留给您正在创建的每个新堆栈,而只将第一页(4kB)映射到实际RAM。这样,当操作系统重新配置MMU以将虚拟地址的每一页映射到RAM的实际空闲页时,堆栈将能够根据需要在连续的虚拟地址上增长和收缩(这意味着每个虚拟地址中的代码更少,这是一个大的优化),只要堆栈的增长或收缩高于/低于某些可配置的阈值,堆栈就能够根据需要增长和收缩

通过巧妙地选择阈值(例如,请参见理论),您可以在平均堆栈操作上实现O(1)复杂度,同时保留数百万个堆栈的好处,这些堆栈可以根据您的需要增长,并且只消耗它们使用的内存

注:目前的Go实施远远落后于以下任何一项:-)

更新Go 1.4(2014年第4季度)

:

在Go 1.4之前,运行时(垃圾收集器、并发支持、接口管理、映射、切片、字符串等)大部分是用C编写的,并带有一些汇编支持。
在1.4中,大部分代码已被翻译为Go,这样垃圾收集器就可以在运行时扫描程序堆栈,并获得关于哪些变量处于活动状态的准确信息

这种重写允许1.4中的垃圾收集器完全精确,这意味着它知道程序中所有活动指针的位置。这意味着堆将更小,因为不存在使非指针保持活动状态的误报。其他相关的更改也减少了堆的大小,相对于以前的版本,堆的大小总体上减小了10%-30%

结果是堆栈不再分段,从而消除了“热拆分”问题。当达到堆栈限制时,将分配一个新的、更大的堆栈,将goroutine的所有活动帧复制到该堆栈中,并更新堆栈中的所有指针


初步答复(2014年3月)

作者的“”一文也论述了这一问题

在堆栈边界恰好处于紧密循环的情况下,重复创建和销毁段的开销变得非常大。
这被称为围棋社区内部的“热分裂”问题

Go 1.3将通过实现连续堆栈来解决“热剥离”问题

现在,当堆栈需要增长时,会发生以下情况,而不是分配新的段:

  • 创建一个新的、稍微大一些的堆栈
  • 将旧堆栈的内容复制到新堆栈
  • 重新调整每个复制的指针以指向新地址
  • 销毁旧堆栈
  • 下面提到了一个主要出现在32位ARHCite结构中的问题:

    不过,还有一个挑战。
    1.2运行时不知道堆栈中指针大小的字是否为实际指针。可能存在浮点数和极少数的整数,如果将它们解释为指针,它们实际上会指向数据

    <强>由于缺乏这样的知识,垃圾收集器必须保守地考虑堆栈帧中的所有位置是根。这就留下了内存泄漏的可能性,特别是在32位体系结构上,因为它们的地址池要小得多

    但是,在复制堆栈时,必须避免这种情况,并且在重新调整时只应考虑实际指针

    并且现在嵌入到二进制文件中,可供运行时使用。
    这意味着1.3中的收集器不仅可以堆叠数据,而且现在可以重新调整堆栈指针


    理论上最大堆栈大小是如何定义的?它没有定义,只是语言或编译器的选择。我应该说得更清楚,我会编辑的。