2012-04-04 3 views
13

나는 haskell 연습에서 Andre Loh의 결정 론적 병렬 프로그래밍의 연습을하고있었습니다. 전략을 사용하여 N-Queens 순차 코드를 병렬로 변환하려고했지만 병렬 코드가 순차 코드보다 훨씬 느리게 실행되고 스택 공간이 부족하여 오류가 발생하는 것으로 나타났습니다. Haskell에서 병렬 전략을 사용할 때 속도 저하

import Control.Monad 
import System.Environment 
import GHC.Conc 
import Control.Parallel.Strategies 
import Data.List 
import Data.Function 

type PartialSolution = [Int] -- per column, list the row the queen is in 
type Solution = PartialSolution 

type BoardSize = Int 

chunk :: Int -> [a] -> [[a]] 
chunk n [] = [] 
chunk n xs = case splitAt n xs of 
     (ys, zs) -> ys : chunk n zs 

-- Generate all solutions for a given board size. 
queens :: BoardSize -> [Solution] 
--queens n = iterate (concatMap (addQueen n)) [[]] !! n 
queens n = iterate (\l -> concat (map (addQueen n) l `using` parListChunk (n `div`   numCapabilities) rdeepseq)) [[]] !! n 


-- Given the size of the problem and a partial solution for the 
-- first few columns, find all possible assignments for the next 
-- column and extend the partial solution. 
addQueen :: BoardSize -> PartialSolution -> [PartialSolution] 
addQueen n s = [ x : s | x <- [1..n], safe x s 1 ] 

-- Given a row number, a partial solution and an offset, check 
-- that a queen placed at that row threatens no queen in the 
-- partial solution. 
safe :: Int -> PartialSolution -> Int -> Bool 
safe x [] n = True 
safe x (c:y) n = x /= c && x /= c + n && x /= c - n && safe x y (n + 1) 

main = do 
     [n] <- getArgs 
     print $ length $ queens (read n) 

라인 (\l -> concat (map (addQueen n) l using parListChunk (n div numCapabilities) rdeepseq))는 I 원본 코드로 변경 무엇 병렬 N-퀸즈 코드이다. Simon Marlow의 솔루션을 보았지만 코드가 느려지고 오류가 발생하는 이유를 알고 싶었습니다.

미리 감사드립니다.

+1

어떻게 컴파일하고 실행 했습니까? – is7s

+3

'-O2'로 컴파일하고'-threaded -Nn' (여기서'n'은 cpu 카운트입니까?)로 실행합니까? –

+0

'-threaded'는 런타임 옵션이 아니라 컴파일 타임 옵션입니다. 더구나, 언제 베일리에 다시 올거야, 돈? 너 꼭꼭가있어. –

답변

4

당신은 너무 많은 일을하고 있습니다. parListChunk 매개 변수는 div n numCapabilities 일 것입니다. 시스템에서는 7 개 (코어 2 개, n ~ 14 개)입니다. 이 목록은 매우 빠르게 커지므로 이러한 작은 작업 단위를 촉발 할 필요가 없습니다. (이유는 그것을 n의 값으로 묶는 것이 타당하지 않습니다.)

10의 인수를 추가하면 (이 경우 스파크 장치 70을 작성) 단일 스레딩보다 성능이 훨씬 좋아집니다. 또한, 당신이 참조하는 스택 문제가 없습니다 - 당신의 parListChunk 값으로 변경되면 버리는 경우 버그로보고 싶습니다.

내가 800 번 청크를 만들면 5.375s 대 7.9s의 시간대가 끝납니다. 800 이상이되면 성능이 다시 악화되기 시작합니다.

편집 :

[[email protected] Test]$ ghc --version 
The Glorious Glasgow Haskell Compilation System, version 7.0.4 
[[email protected] Test]$ ghc -O2 so.hs -rtsopts -threaded -fforce-recomp ; time ./so 13 +RTS -N2 
[1 of 1] Compiling Main    (so.hs, so.o) 
Linking so ... 
73712 
real 0m5.404s 

[[email protected] Test]$ ghc -O2 so.hs -rtsopts -fforce-recomp ; time ./so 13 
[1 of 1] Compiling Main    (so.hs, so.o) 
Linking so ... 
73712 
real 0m8.134s 
+0

실수를 지적 해 주셔서 감사합니다. 실제로 부분 솔루션을 코어간에 균등하게 나누기를 원했습니다. 이제는 div (길이 l) numCapabilities'를 수정해야합니다. 심지어 그것을 수행 한 후에도 병렬 버전이 여전히 순차 버전 (-thread 옵션없이 컴파일 됨)보다 느리고 -N1 옵션에 대해 동일한 스택 오버플로 예외가 발생합니다. 내가 보드 크기 14 같은 시도해도, 순차 버전은 잘 작동하지만 병렬 버전은 메모리 부족 오류를 제공합니다. – prasannak

+0

이 정보가 충분하지 않을 수 있음을 알고 있습니다. 이러한 경우에 eventlog 파일을 첨부 할 수 있다면 도움이됩니까? – prasannak

+0

GHC 버전은 현재 사용하고있는 코드와 각각의 경우에 대해 컴파일하는 방법 ('-fforce-recomp' 플래그 포함)에 도움이 될 것입니다. 나는 '길이 l'을 사용할 것을 제안하지 않으며 단지 스파크가 중요하지 않은 값을 선택하지만 값이 작 으면 하나 또는 두 개의 코어 사이의 시간 차이를 알아 채지 못할 것입니다. 불꽃. –

관련 문제