为了衡量这些参考文献的性能,我将GHC生成的程序集转储到以下代码中:
import Data.IORef
main = do
r <- newIORef 18
v <- readIORef r
print v我预计IORef会被完全优化,只留下一个syscall来编写字符串"18“的stdout。相反,我得到了250条生产线。你知道有多少人会被处决吗?我认为这个项目的核心是:
.globl Main.main1_info
Main.main1_info:
_c1Zi:
leaq -8(%rbp),%rax
cmpq %r15,%rax
jb _c1Zj
_c1Zk:
movq $block_c1Z9_info,-8(%rbp)
movl $Main.main2_closure+1,%ebx
addq $-8,%rbp
jmp stg_newMutVar#
_c1Zn:
movq $24,904(%r13)
jmp stg_gc_unpt_r1
.align 8
.long S1Zo_srt-(block_c1Z9_info)+0
.long 0
.quad 0
.quad 30064771104
block_c1Z9_info:
_c1Z9:
addq $24,%r12
cmpq 856(%r13),%r12
ja _c1Zn
_c1Zm:
movq 8(%rbx),%rax
movq $sat_s1Z2_info,-16(%r12)
movq %rax,(%r12)
movl $GHC.Types.True_closure+2,%edi
leaq -16(%r12),%rsi
movl $GHC.IO.Handle.FD.stdout_closure,%r14d
addq $8,%rbp
jmp GHC.IO.Handle.Text.hPutStr2_info
_c1Zj:
movl $Main.main1_closure,%ebx
jmp *-8(%r13)我很担心这个jmp stg_newMutVar#。它在程序集中没有其他地方,所以GHC可能会在稍后的链接阶段解决它。但它为什么会出现在这里,它能做什么呢?我可以在没有任何未解析的haskell符号的情况下转储最终程序集吗?
发布于 2016-08-29 10:23:21
从以下几个链接开始:
如果您还不熟悉cmm和原目,那么宏和原目的源代码就不是特别可读的了。不幸的是,我不知道有什么好的方法来查看为cmm primops生成的程序集,除非使用objdump或其他反汇编程序查看可执行文件。
不过,我可以总结一下IORef的运行时语义。
IORef是来自GHC.Prim的MutVar#的包装器。正如医生所说,MutVar#就像一个单元素可变数组.它占用两个机器单词,第一个是头,第二个是存储的值(它是指向GHC对象的指针)。MutVar#的值本身就是指向这个双单词对象的指针。
MutVar-s与普通的不可变对象不同,最显著的是参与了写屏障机制。GHC具有分代垃圾收集功能,因此在收集年轻一代时,任何生活在老一代的MutVar都必须是GC根,因为变异MutVar可能会使较年轻的对象变得可访问。因此,每当从第0代(最小的)提升MutVar时,就会将其添加到所谓的“可变列表”中,其中包含对所有此类可变对象的引用。可变列表在老一代的GC中重新生成。简而言之,老一代的MutVar-s总是出现在可变列表上。
这是处理可变变量的一种相当简单的方法,如果在老一代中有大量可变变量,那么小型垃圾收集就会因为庞大的可变列表而减慢速度,因此导致整个程序慢下来。
由于可变变量在生产代码中没有显着地使用,因此没有太多的需求或压力来优化RTS,因为它们的大量使用。
如果需要大量可变变量,则应该使用单个可变框数组,因为这只是可变列表上的单个引用,而且还具有一个位图优化,用于遍历可能已发生变异的元素。
而且,正如您所看到的,newMutVar#只是静态链接,而不是内联,尽管它是一个相当小的代码块。因此,它也没有被优化掉。这在很大程度上是由于对优化变异代码缺乏努力和关注。相比之下,分配和复制小型已知大小的基元数组目前是内嵌,并且进行了很大的优化,因为约翰·蒂贝尔在实现unordered-containers库时做了大量工作(以便使unordered-containers更快)。
https://stackoverflow.com/questions/39202738
复制相似问题