У меня есть программа, которую я пытаюсь распараллелить (полная паста с исполняемым кодом here).Parallel Haskell - GHC GC'ing sparks
Я профилировал и обнаружил, что большая часть времени проведена в findNearest
, который по существу простой foldr
над большим Data.Map
.
findNearest :: RGB -> M.Map k RGB -> (k, Word32)
findNearest rgb m0 =
M.foldrWithKey' minDistance (k0, distance rgb r0) m0
where (k0, r0) = M.findMin m0
minDistance k r [email protected](_, d1) =
-- Euclidean distance in RGB-space
let d0 = distance rgb r
in if d0 < d1 then (k, d0) else x
parFindNearest
предполагается выполнить findNearest
параллельно над поддеревьев большего Map
.
parFindNearest :: NFData k => RGB -> M.Map k RGB -> (k, Word32)
parFindNearest rgb = minimumBy (comparing snd)
. parMap rdeepseq (findNearest rgb)
. M.splitRoot
К сожалению, большинство моих искр GHC GC, прежде чем они превратятся в полезный параллелизм.
Вот результат компиляции с ghc -O2 -threaded
и работает с +RTS -s -N2
839,892,616 bytes allocated in the heap
123,999,464 bytes copied during GC
5,320,184 bytes maximum residency (19 sample(s))
3,214,200 bytes maximum slop
16 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 1550 colls, 1550 par 0.23s 0.11s 0.0001s 0.0004s
Gen 1 19 colls, 18 par 0.11s 0.06s 0.0030s 0.0052s
Parallel GC work balance: 16.48% (serial 0%, perfect 100%)
TASKS: 6 (1 bound, 5 peak workers (5 total), using -N2)
SPARKS: 215623 (1318 converted, 0 overflowed, 0 dud, 198111 GC'd, 16194 fizzled)
INIT time 0.00s ( 0.00s elapsed)
MUT time 3.72s ( 3.66s elapsed)
GC time 0.34s ( 0.17s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 4.07s ( 3.84s elapsed)
Alloc rate 225,726,318 bytes per MUT second
Productivity 91.6% of total user, 97.1% of total elapsed
gc_alloc_block_sync: 9862
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 2103
Как вы можете видеть, большинство искр GC'd или выдохнуться перед преобразованием. Я пробовал экспериментировать с различной строгостью, имея findNearest
, возвращающий пользовательский строгий тип данных пары вместо кортежа или используя rdeepseq от Control.Parallel.Strategies
, но мои искры по-прежнему GC'd.
Я хотел бы знать,
- почему мои искры быть GC'd перед преобразованием?
- Как я могу изменить свою программу, чтобы воспользоваться параллелизмом?
http://www.haskell.org/haskellwiki/ThreadScope может помочь. –
1. 'splitRoot' генерирует обычно список с тремя элементами, левым деревом, корнем и правым деревом. Таким образом, вы используете 'parMap' над небольшим списком _very_. Сами элементы довольно большие, но опять-таки 'findNearest' не является параллельным. 2. Исключенное выражение GC'd, если оно не используется. Может быть, вы не используете результат в конце концов? – Zeta
@Zeta: да, размер списка небольшой (всего 3 элемента), но размер «Карты» большой (65 тыс. ~ 250 тыс. Элементов), поэтому даже разделение его на два значимых поддерева должно обеспечивать некоторый полезный параллелизм. – cdk