Подтвердить что ты не робот

Частые GC предотвращают запуск искры параллельно

Я попробовал запустить первый пример здесь: http://chimera.labs.oreilly.com/books/1230000000929/ch03.html

Код: https://github.com/simonmar/parconc-examples/blob/master/strat.hs

import Control.Parallel
import Control.Parallel.Strategies (rpar, Strategy, using)
import Text.Printf
import System.Environment

-- <<fib
fib :: Integer -> Integer
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
-- >>

main = print pair
 where
  pair =
-- <<pair
   (fib 35, fib 36) `using` parPair
-- >>

-- <<parPair
parPair :: Strategy (a,b)
parPair (a,b) = do
  a' <- rpar a
  b' <- rpar b
  return (a',b')
-- >>

Я построил с помощью ghc 7.10.2 (в OSX, с многоядерной машиной), используя следующую команду:

ghc -O2 strat.hs -threaded -rtsopts -eventlog

И запустите с помощью:

./strat +RTS -N2 -l -s

Я ожидал, что вычисления 2 fibs будут выполняться параллельно (предыдущие главы работали так, как ожидалось, поэтому никаких проблем с настройкой), и я не получал никакого ускорения вообще, как показано здесь:

  % ./strat +RTS -N2 -l -s
(14930352,24157817)
   3,127,178,800 bytes allocated in the heap
       6,323,360 bytes copied during GC
          70,000 bytes maximum residency (2 sample(s))
          31,576 bytes maximum slop
               2 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0      5963 colls,  5963 par    0.179s   0.074s     0.0000s    0.0001s
  Gen  1         2 colls,     1 par    0.000s   0.000s     0.0001s    0.0001s

  Parallel GC work balance: 2.34% (serial 0%, perfect 100%)

  TASKS: 6 (1 bound, 5 peak workers (5 total), using -N2)

  SPARKS: 2 (0 converted, 0 overflowed, 0 dud, 1 GC'd, 1 fizzled)

  INIT    time    0.000s  (  0.001s elapsed)
  MUT     time    1.809s  (  1.870s elapsed)
  GC      time    0.180s  (  0.074s elapsed)
  EXIT    time    0.000s  (  0.000s elapsed)
  Total   time    1.991s  (  1.945s elapsed)

  Alloc rate    1,728,514,772 bytes per MUT second

  Productivity  91.0% of total user, 93.1% of total elapsed

gc_alloc_block_sync: 238
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0

-N1 получает похожие результаты (опущен).

Количество коллекций GC выглядело подозрительным, о чем говорили другие в # haskell-beginners, поэтому я попытался добавить -A16M при запуске. Результаты выглядели намного больше в соответствии с ожиданиями:

  % ./strat +RTS -N2 -l -s -A16M
(14930352,24157817)
   3,127,179,920 bytes allocated in the heap
         260,960 bytes copied during GC
          69,984 bytes maximum residency (2 sample(s))
          28,320 bytes maximum slop
              33 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0       115 colls,   115 par    0.105s   0.002s     0.0000s    0.0003s
  Gen  1         2 colls,     1 par    0.000s   0.000s     0.0002s    0.0002s

  Parallel GC work balance: 71.25% (serial 0%, perfect 100%)

  TASKS: 6 (1 bound, 5 peak workers (5 total), using -N2)

  SPARKS: 2 (1 converted, 0 overflowed, 0 dud, 0 GC'd, 1 fizzled)

  INIT    time    0.001s  (  0.001s elapsed)
  MUT     time    1.579s  (  1.087s elapsed)
  GC      time    0.106s  (  0.002s elapsed)
  EXIT    time    0.000s  (  0.000s elapsed)
  Total   time    1.686s  (  1.091s elapsed)

  Alloc rate    1,980,993,138 bytes per MUT second

  Productivity  93.7% of total user, 144.8% of total elapsed

gc_alloc_block_sync: 27
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0

Вопрос: почему это поведение? Даже с частым GC, я все еще интуитивно ожидаю, что 2 искры будут работать параллельно в других 90% времени выполнения.

4b9b3361

Ответ 1

Да, на самом деле это ошибка в GHC 8.0.1 и более ранних версиях (я работаю над ее исправлением для 8.0.2). Проблема в том, что выражения fib 35 и fib 36 являются постоянными, и поэтому GHC поднимает их на верхний уровень как CAF, а RTS ошибочно полагает, что CAFs недоступны и поэтому мусор собирает искры.

Вы можете обойти это, сделав выражения непостоянными, передав параметры в командной строке:

main = do
     [a,b] <- map read <$> getArgs
     let pair = (fib a, fib b) `using` parPair
     print pair

а затем запустите программу с помощью ./strat 35 36.