Объем памяти и производительность IORef - PullRequest
2 голосов
/ 04 мая 2020

Интересно, каков объем памяти переменной типа IORef a, если я знаю, что размер a равен x. Кроме того, какова ожидаемая производительность функции writeIORef, применяемой к целочисленному сравнению, чтобы сказать регулярное присвоение переменной (например, x = 3), скажем, в Java?

1 Ответ

3 голосов
/ 04 мая 2020

В 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.

...