Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added nofib benchmark "coins". The performance is comparable to Versi…
…on 7 on Harpertown windows.
- Loading branch information
Showing
1 changed file
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
{-# LANGUAGE BangPatterns #-} | ||
|
||
import Data.List | ||
import System.Environment | ||
import Control.Parallel | ||
import Control.Parallel.Strategies | ||
import Control.Applicative | ||
import Control.Monad.Par | ||
|
||
-- Rough results, GHC 6.13: (val=777) | ||
-- V1 (SDM): 2.2s | ||
-- V2 (SDM): 2.7s | ||
-- V3 (SDM, parallel): 1.0s on 7 cores | ||
-- V4 (original): got bored waiting | ||
-- V5 (HWL assoc): 5.2s | ||
-- V6 (SDM, Int result): 0.9s | ||
-- V7 (SDM, parallel): 0.2s on 7 cores | ||
|
||
----------------------------------------------------------------------------- | ||
-- Version 1: returns results as a list of list of coins | ||
|
||
payL :: Int -> [(Int,Int)] -> [Int] -> [[Int]] | ||
payL 0 coins acc = [acc] | ||
payL _ [] acc = [] | ||
payL val ((c,q):coins) acc | ||
| c > val = payL val coins acc | ||
| otherwise = left ++ right | ||
where | ||
left = payL (val - c) coins' (c:acc) | ||
right = payL val coins acc | ||
|
||
coins' | q == 1 = coins | ||
| otherwise = (c,q-1) : coins | ||
|
||
----------------------------------------------------------------------------- | ||
-- Version 2: uses a custom AList type to avoid repeated appends | ||
|
||
-- The idea here is that by avoiding the append we might be able to | ||
-- parallelise this more easily by just forcing evaluation to WHNF at | ||
-- each level. I haven't parallelised this version yet, though (V5 | ||
-- below is much easier) --SDM | ||
|
||
data AList a = ANil | ASing a | Append (AList a) (AList a) | ||
|
||
lenA :: AList a -> Int | ||
lenA ANil = 0 | ||
lenA (ASing _) = 1 | ||
lenA (Append l r) = lenA l + lenA r | ||
|
||
append ANil r = r | ||
append l ANil = l -- ** | ||
append l r = Append l r | ||
|
||
-- making append less strict (omit ** above) can make the algorithm | ||
-- faster in sequential mode, because it runs in constant space. | ||
-- However, ** helps parallelism. | ||
|
||
payA :: Int -> [(Int,Int)] -> [Int] -> AList [Int] | ||
payA 0 coins acc = ASing acc | ||
payA _ [] acc = ANil | ||
payA val ((c,q):coins) acc | ||
| c > val = payA val coins acc | ||
| otherwise = append left right -- strict in l, maybe strict in r | ||
where | ||
left = payA (val - c) coins' (c:acc) | ||
right = payA val coins acc | ||
coins' | q == 1 = coins | ||
| otherwise = (c,q-1) : coins | ||
|
||
----------------------------------------------------------------------------- | ||
-- Version 3: parallel version of V2 | ||
|
||
payA_par :: Int -> Int -> [(Int,Int)] -> [Int] -> AList [Int] | ||
payA_par 0 val coins acc = payA val coins acc | ||
payA_par _ 0 coins acc = ASing acc | ||
payA_par _ _ [] acc = ANil | ||
payA_par depth val ((c,q):coins) acc | ||
| c > val = payA_par depth val coins acc | ||
| otherwise = res | ||
|
||
where | ||
res = unEval $ pure append <*> rpar left <*> rwhnf right | ||
|
||
left = payA_par (if q == 1 then (depth-1) else depth) (val - c) coins' (c:acc) | ||
right = payA_par (depth-1) val coins acc | ||
|
||
coins' | q == 1 = coins | ||
| otherwise = (c,q-1) : coins | ||
|
||
----------------------------------------------------------------------------- | ||
-- Version 4: original list-of-list version (very slow) | ||
|
||
pay :: Int -> Int -> [Int] -> [Int] -> [[Int]] | ||
pay _ 0 coins accum = [accum] | ||
pay _ val [] _ = [] | ||
pay pri val coins accum = | ||
res | ||
where -- | ||
coins' = dropWhile (>val) coins | ||
coin_vals = nub coins' | ||
res = concat ( map | ||
( \ c -> let | ||
new_coins = | ||
((dropWhile (>c) coins')\\[c]) | ||
in | ||
pay (pri-1) | ||
(val-c) | ||
new_coins | ||
(c:accum) | ||
) | ||
coin_vals ) | ||
|
||
|
||
----------------------------------------------------------------------------- | ||
-- Version 5: assoc-list version (by HWL?) | ||
|
||
-- assoc-list-based version; still multiple list traversals | ||
pay1 :: Int -> Int -> [(Int,Int)] -> [(Int,Int)] -> [[(Int,Int)]] | ||
pay1 _ 0 coins accum = [accum] | ||
pay1 _ val [] _ = [] | ||
pay1 pri val coins accum = res | ||
where -- | ||
coins' = dropWhile ((>val) . fst) coins | ||
res = concat ( | ||
map | ||
( \ (c,q) -> let | ||
-- several traversals | ||
new_coins = | ||
filter (not . (==0) . snd) $ | ||
map (\ x'@(c',q') -> if c==c' then (c',q'-1) else x') $ | ||
dropWhile ((>c) . fst) $ | ||
coins' | ||
new_accum = | ||
map (\ x'@(c',q') -> if c==c' then (c',q'+1) else x') accum | ||
in | ||
pay1 (pri-1) | ||
(val-c) | ||
new_coins | ||
new_accum | ||
) | ||
coins' ) | ||
|
||
----------------------------------------------------------------------------- | ||
-- Version 6: just return the number of results, not the results themselves | ||
|
||
payN :: Int -> [(Int,Int)] -> Int | ||
payN 0 coins = 1 | ||
payN _ [] = 0 | ||
payN val ((c,q):coins) | ||
| c > val = payN val coins | ||
| otherwise = left + right | ||
where | ||
left = payN (val - c) coins' | ||
right = payN val coins | ||
|
||
coins' | q == 1 = coins | ||
| otherwise = (c,q-1) : coins | ||
|
||
----------------------------------------------------------------------------- | ||
-- Version 7: parallel version of payN | ||
|
||
payN_par :: Int -> Int -> [(Int,Int)] -> Int | ||
payN_par 0 val coins = payN val coins | ||
payN_par _ 0 coins = 1 | ||
payN_par _ _ [] = 0 | ||
payN_par depth val ((c,q):coins) | ||
| c > val = payN_par depth val coins | ||
| otherwise = res | ||
|
||
where | ||
res = right `par` left `pseq` left + right | ||
|
||
left = payN_par (if q == 1 then (depth-1) else depth) (val - c) coins' | ||
right = payN_par (depth-1) val coins | ||
|
||
coins' | q == 1 = coins | ||
| otherwise = (c,q-1) : coins | ||
|
||
----------------------------------------------------------------------------- | ||
|
||
----------------------------------------------------------------------------- | ||
-- Version 8: monad-par version of payN | ||
-- Competitive with Version 7. | ||
|
||
payN_mp :: Int -> Int -> [(Int,Int)] -> Int | ||
payN_mp depth val coins = | ||
runPar $ | ||
payN_mpM depth val coins | ||
|
||
payN_mpM :: Int -> Int -> [(Int,Int)] -> Par Int | ||
payN_mpM 0 val coins = return $ payN val coins | ||
payN_mpM _ 0 coins = return 1 | ||
payN_mpM _ _ [] = return 0 | ||
payN_mpM depth val ((c,q):coins) | ||
| c > val = payN_mpM depth val coins | ||
| otherwise = res | ||
|
||
where | ||
res = | ||
do lv <- spawn $ left | ||
r <- right | ||
l <- get lv | ||
return (l + r) | ||
|
||
left = payN_mpM (if q == 1 then (depth-1) else depth) (val - c) coins' | ||
right = payN_mpM (depth-1) val coins | ||
|
||
coins' | q == 1 = coins | ||
| otherwise = (c,q-1) : coins | ||
|
||
----------------------------------------------------------------------------- | ||
|
||
|
||
-- driver | ||
|
||
main = do | ||
let vals = [250, 100, 25, 10, 5, 1] | ||
-- let quants = [1, 3, 2, 5, 7, 12] -- small setup | ||
-- let quants = [5, 8, 8, 9, 12, 17] -- std setup | ||
let quants = [55, 88, 88, 99, 122, 177] -- large setup | ||
|
||
let coins = concat (zipWith replicate quants vals) | ||
coins1 = zip vals quants | ||
|
||
[n, arg] <- fmap (fmap read) getArgs | ||
|
||
case n of | ||
-- sequential, list of results | ||
1 -> print $ length $ payL arg coins1 [] | ||
-- sequential, append-list of results | ||
2 -> print $ lenA $ payA arg coins1 [] | ||
-- parallel, append-list of results | ||
3 -> print $ lenA $ payA_par 4 arg coins1 [] | ||
|
||
4 -> print $ length (pay 0 arg coins []) | ||
5 -> print $ length (pay1 0 arg coins1 (map (\(c,q) -> (c,0)) coins1)) | ||
6 -> print $ payN arg coins1 | ||
7 -> print $ payN_par 4 arg coins1 | ||
8 -> print $ payN_mp 4 arg coins1 | ||
|