В Haskell IORef a
ведет себя как изменяемый массив из одного элемента. определение из IORef
является следующим, без учета упаковки нового типа:
data IORef a = IORef (MutVar# RealWorld a)
Здесь MutVar# RealWorld a
является примитивным изменяемым ссылочным типом. Это указатель, который указывает на два слова, заголовок и полезную нагрузку, которая сама является указателем на обычный поднятый объект Haskell. Следовательно, издержки MutVar
составляют два слова (16 байт в 64-битных системах) и одно косвенное обращение.
Таким образом, служебная нагрузка MutVar#
- это одно дополнительное косвенное обращение и одно дополнительное слово заголовка. Это неизбежно. Напротив, издержки конструктора IORef
также составляют одно слово заголовка и одно косвенное обращение, но их можно устранить, распаковав IORef
:
data Foo a = Foo !(IORef a) a a
Здесь удар по IORef
вызывает базовый MutVar
должен быть распакован в Foo
. Но хотя эта распаковка работает всякий раз, когда мы определяем новые типы данных, она не работает, если мы используем какой-либо существующий параметризованный тип, например списки. В [IORef a]
мы оплачиваем полную стоимость двумя дополнительными косвенными ссылками.
IORef
также обычно распаковывается оптимизацией GH C, если она используется в качестве аргумента функции: IORef a -> b
will обычно распаковывается в MutVar# RealWorld a -> b
, если вы компилируете с оптимизацией.
Однако все вышеперечисленные издержки менее важны, чем издержки в сборке мусора , когда вы используете большое количество IORef
s. Чтобы избежать этого, рекомендуется использовать один изменяемый массив вместо множества IORef
-s.