Компиляция IORef и STRef
Чтобы измерить производительность этих ссылок, я поместил сборку, созданную GHC, в следующий код:
import Data.IORef
main = do
r <- newIORef 18
v <- readIORef r
print v
Я ожидал, что IORef будет полностью оптимизирован, оставив только системный вызов для записи stdout со строкой "18". Вместо этого я получаю 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?
1 ответ
Начиная с пары ссылок:
-
MutVar
определение объекта. -
cmm
код дляnewMutVar
, - Неполная, но полезная сводка по макету объекта GHC.
cmm
а также C
источники не особенно читабельны, если вы еще не знакомы с макросами и праймпами. К сожалению, я не знаю хорошего способа просмотра сборки, сгенерированной для cmm
primops, если не смотреть на исполняемый файл с помощью objdump или другого дизассемблера.
Тем не менее, я могу обобщить семантику времени выполнения IORef
,
IORef
это обертка вокруг MutVar#
от GHC.Prim
, Как говорит док, MutVar#
это как одноэлементный изменяемый массив. Он занимает два машинных слова, первое - заголовок, второе - сохраненное значение (которое является указателем на объект GHC). Значение MutVar#
сам по себе является указателем на этот объект из двух слов.
MutVar
-s отличаются от обычных неизменяемых объектов наиболее заметно тем, что участвуют в механизме барьера записи. GHC имеет сборку мусора поколений, поэтому любой MutVar
который живет в старшем поколении, должен также быть корнем GC при сборе младшего поколения, поскольку MutVar
может привести к тому, что более молодые объекты станут доступными. Поэтому всякий раз, когда MutVar
продвигается из поколения 0 (самое молодое), оно добавляется в так называемый "изменяемый список", который содержит ссылки на все такие изменяемые объекты. Изменяемый список перестраивается во время GC старых поколений. Короче, MutVar
-s в старых поколениях всегда присутствуют в списке изменяемых.
Это довольно упрощенный способ работы с изменяемыми переменными, и если у нас их много в старых поколениях, незначительная сборка мусора замедляется из-за раздутого изменяемого списка, и в результате вся программа замедляется.
Поскольку изменяемые переменные не используются в производственном коде на видном месте, не было особого спроса или давления для оптимизации RTS для их интенсивного использования.
Если вам нужно большое количество изменяемых переменных, вам следует вместо этого использовать один изменяемый массив в штучной упаковке, потому что это всего лишь одна ссылка в списке изменяемых элементов, а также имеется оптимизация на основе растровых изображений для обхода элементов GC, которые могли быть изменены.
Кроме того, как вы видите newMutVar#
является только статически связанным, но не встроенным, хотя это довольно небольшой кусок кода. В результате это также не оптимизировано далеко. Это опять-таки в основном из-за отсутствия усилий и внимания для оптимизации изменяющегося кода. В отличие от этого, выделение и копирование небольших примитивных массивов известного размера в настоящее время встроено и значительно оптимизировано, потому что Йохан Тибелл, который проделал большую работу по реализации unordered-containers
библиотека сделала это так (чтобы сделать unordered-containers
Быстрее).