Я пытался понять параллелизм, и я пытался решить, что лучше, одна большая IORef
блокировка или много TVar
с. Я пришел к следующим рекомендациям, комментарии будут оценены относительно того, являются ли они примерно правильными или я упустил суть.
Предположим, что наша параллельная структура данных представляет собой карту m
, доступ к которой осуществляется как m[i]
. Допустим также, что у нас есть две функции, f_easy
и f_hard
. f_easy
работает быстро, f_hard
занимает много времени. Предположим, что аргументы f_easy/f_hard
являются элементами m
.
(1)Если ваши транзакции выглядят примерно так m[f_easy(...)] = f_hard(...)
, используйте IORef
с atomicModifyIORef
. Лень гарантирует, что m
будет заблокирован только на короткое время, поскольку он обновляется с помощью преобразователя. Вычисление индекса эффективно блокирует структуру (, так как что-то будет обновлено, но мы пока не знаем, что ), но как только известно, что это за элемент, преобразователь по всей структуре перемещается только к преобразователю. над этим конкретным элементом, и тогда только этот конкретный элемент «заблокирован».
(2)Если ваши транзакции выглядят примерно так m[f_hard(...)] = f_easy(...)
и не сильно конфликтуют, используйте много TVar
s. Использование IORef
в этом случае эффективно сделает приложение однопоточным, так как вы не сможете вычислить два индекса одновременно (, поскольку во всей структуре будет неразрешенный преобразователь). TVar
позволяет вам обрабатывать два индекса одновременно, однако недостатком является то, что если две параллельные транзакции обращаются к одному и тому же элементу, и одна из них является записью, одна транзакция должна быть отменена, что приводит к пустой трате времени (. ], который мог быть использован в другом месте). Если это случается часто,вам может быть лучше с блокировками, которые приходят (через черную дыру)из IORef
, но если этого не происходит очень часто, вы получите лучший параллелизм с TVar
s.
В основном, в случае (2), с IORef
вы можете получить 100% эффективность (без лишней работы), но использовать только потоки 1.1, но с TVar
, если у вас небольшое число конфликтов вы можете получить 80% эффективности, но использовать 10 потоков, так что вы все равно в конечном итоге в 7 раз быстрее, даже с потраченной впустую работой.