2

私の理解でparは、実行する別のコアにスレッドが作成されます。

しかし、1つのスレッドしか実行されていないように見える結果が表示されたため、次のテストコードでこの理解を証明できませんでした。

ここで何が問題なのかを理解するのを手伝ってもらえますか?

import Control.Monad
import Control.Parallel
import Control.Concurrent
import System.IO.Unsafe

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

test :: String -> [Int] -> IO () 
test _ [] = return () 
test name (a:xs) = do 
    tid <- myThreadId 
    print $ (show tid) ++ "==>" ++ (show a) ++ "==>" ++ (show $ fib a) ++ "==>" ++ name
    x `par` y 
  where x = test "2" xs
        y = test "3" (tail xs)

main = test "1" [10..35]

コンパイル済み:

ghc --make -threaded -rtsopts test-par.hs
time ./test-par +RTS -N2

結果

"ThreadId 3==>10==>89==>1"
"ThreadId 3==>12==>233==>3"
"ThreadId 3==>14==>610==>3"
"ThreadId 3==>16==>1597==>3"
"ThreadId 3==>18==>4181==>3"
"ThreadId 3==>20==>10946==>3"
"ThreadId 3==>22==>28657==>3"
"ThreadId 3==>24==>75025==>3"
"ThreadId 3==>26==>196418==>3"
"ThreadId 3==>28==>514229==>3"
"ThreadId 3==>30==>1346269==>3"
"ThreadId 3==>32==>3524578==>3"
"ThreadId 3==>34==>9227465==>3"

real    0m1.131s
user    0m0.668s
sys 0m0.492s

コアはいくつありますか?

cat /proc/cpuinfo | grep processor | wc -l
2

- - - - - - - - - - - - - - - - アップデート

サイモン・マーロウによるこの論文は、そのような初心者の質問の良い参考になると思います。

4

1 に答える 1

13

いいえ、par別のスレッドを作成することを保証するものではありません。

ランタイムにスパークを登録します。これにより、マシンの動的ワークロードに応じて、計算が別のスレッドで実行される可能性があります。

Sparkの作成は非常に安価であるため、スレッドよりも多く(1000x)を作成でき、ランタイムはすべてのコアをビジー状態に保とうとします。

あなたの場合、あなたのx計算は火花として登録され、すぐに破棄されます(あなたはそれを二度と参照することはありません)。したがって、ガベージコレクタはそれを削除できます。

再帰関数を並列化するには、通常、parある程度の深さまで使用する必要があります。

例-カットオフ深度を持つ再帰関数:

import Control.Parallel
import Control.Monad
import Text.Printf

cutoff = 35

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

fib :: Int -> Integer
fib n | n < cutoff = fib' n
      | otherwise  = r `par` (l `pseq` l + r)
 where
    l = fib (n-1)
    r = fib (n-2)

main = forM_ [0..45] $ \i ->
            printf "n=%d => %d\n" i (fib i)

実行:

$ ghc -O2 -threaded --make A.hs -rtsopts -fforce-recomp
$ ./A +RTS -N16 -s

シーケンシャル(つまり12.48x)よりも1248.9%の並列ワークロードが得られます。

                                   Tot time (elapsed)  Avg pause  Max pause
  Gen  0      6264 colls,  6263 par    5.23s    0.41s     0.0001s    0.0109s
  Gen  1         1 colls,     1 par    0.00s    0.00s     0.0002s    0.0002s

  Parallel GC work balance: 7.09 (1797194 / 253525, ideal 16)

                        MUT time (elapsed)       GC time  (elapsed)
  Task  0 (worker) :    7.56s    (  9.36s)       0.92s    (  0.89s)
  Task  1 (worker) :    0.12s    ( 10.21s)       0.02s    (  0.05s)
  Task  2 (bound)  :    8.39s    (  9.51s)       0.70s    (  0.74s)
  Task  3 (worker) :    0.00s    (  0.00s)       0.00s    (  0.00s)
  Task  4 (worker) :    7.17s    (  9.60s)       0.53s    (  0.66s)
  Task  5 (worker) :    7.28s    (  9.55s)       0.56s    (  0.71s)
  Task  6 (worker) :    7.48s    (  9.52s)       0.56s    (  0.74s)
  Task  7 (worker) :    7.11s    (  9.54s)       0.66s    (  0.72s)
  Task  8 (worker) :    7.41s    (  9.62s)       0.70s    (  0.64s)
  Task  9 (worker) :    7.69s    (  9.48s)       0.66s    (  0.78s)
  Task 10 (worker) :    7.56s    (  9.51s)       0.56s    (  0.75s)
  Task 11 (worker) :    7.69s    (  9.42s)       0.86s    (  0.84s)
  Task 12 (worker) :    7.42s    (  9.40s)       0.92s    (  0.86s)
  Task 13 (worker) :    7.28s    (  9.39s)       0.91s    (  0.86s)
  Task 14 (worker) :    7.44s    (  9.38s)       0.91s    (  0.87s)
  Task 15 (worker) :    7.25s    (  9.33s)       1.11s    (  0.93s)
  Task 16 (worker) :    7.94s    (  9.33s)       0.97s    (  0.93s)
  Task 17 (worker) :    7.59s    (  9.37s)       1.06s    (  0.88s)

  SPARKS: 597 (446 converted, 0 dud, 1 GC'd, 150 fizzled)

  Productivity  96.1% of total user, 1245.3% of total elapsed

597個のスパークを作成し、そのうち446個をスレッドに変換しました。

手動でスレッドの作成と通信を明示的に行いたい場合は、とを介して行うことができforkIOますMVars

于 2012-05-23T17:34:47.830 に答える