IORef 的内存占用和性能

Memory footprint and performance of IORef

如果我知道 a 的大小是 x,我想知道来自类型 IORef a 的变量的内存占用量是多少。 另外,应用于整数的函数 writeIORef 的预期性能与说 Java 中的常规变量赋值(如 x = 3)相比的预期性能是多少?

在 Haskell 中,IORef a 的行为类似于 single-element 可变数组。 IORefdefinition 如下,忽略新类型包装:

data IORef a = IORef (MutVar# RealWorld a) 

这里,MutVar# RealWorld a是原始可变引用类型。它是一个指向两个字的指针,一个header,以及一个有效载荷,它本身就是一个指向正常提升的Haskell object的指针。因此 MutVar 的开销是两个字(在 64 位系统上为 16 字节)和一个间接寻址。

因此 MutVar# 的开销是一个额外的间接寻址和一个额外的 header 字。这是不可避免的。相比之下,IORef构造函数的开销也是一个header字和一个间接寻址,但是可以通过解包来消除IORef:

data Foo a = Foo !(IORef a) a a 

在这里,IORef 上的 bang 导致基础 MutVar 被解包到 Foo。但是,当我们定义新的数据类型时,这种解包工作,但如果我们使用任何现有的参数化类型,如列表,它就不起作用。在 [IORef a] 中,我们通过两次额外的间接访问来支付全部费用。

IORef 如果用作函数的参数,通常也会被 GHC 优化解包:如果使用优化进行编译,IORef a -> b 通常会被解包为 MutVar# RealWorld a -> b .

但是,当您使用大量 IORef-s 时,上述所有开销都没有 garbage collection 中的开销重要。为避免这种情况,建议使用单个可变数组而不是许多 IORef-s.