haskell搞并行还是挺方便的。纯函数式没状态嘛,这儿锁啊钥匙啊什么的都不用管了。有个Control.Parallel,里貌似只有两个函数,一个par,表示两个并行计算;一个pseq,表示连续计算。参数都是两个名字。像处理个分治算法啥的,就再合适不过了。于是再次膜拜惰性求值,写起来真的太奇特了。
module Main where import Control.Parallel --the classic one qsort :: (Ord a) => [a] -> [a] qsort [] = [] qsort (x:xs) = (qsort lt) ++ [x] ++ (qsort gt) where lt = filter (<x) xs gt = filter (>=x) xs --the parallel one psort :: (Ord a) => [a] -> [a] psort [] = [] psort (x:xs) = sorted_lt `par` sorted_gt `pseq` (sorted_lt ++ [x] ++ sorted_gt) ----unbelieveable,isn't it? :> where sorted_lt = psort $ filter (<) xs sorted_gt = psort $ filter (>=x) xs main = do { list <- return [5000,4999..1]; print $ qsort list; print $ psort list; }
profile下,给ghc添加一个编译选项 -prof -O:
ghc --make -prof -O -auto-all quicksort.hs
执行程序,加一个选项 +RTS -p,它会在本目录下生成一个quicksort.prof文件
quicksort +RTS -p
quicksort.prof文件的部分内容:
Mon Jul 20 16:40 2009 Time and Allocation Profiling Report (Final)
quicksort +RTS -p -RTS
total time = 6.86 secs (343 ticks @ 20 ms)
total alloc = 1,401,627,416 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
qsort Main 69.1 49.9
psort Main 30.9 49.9
可见在这台双核的机器上,性能提高了一半多 :)
ps:测试的数据貌似不是很好(按说该用个随机数列),不过知道有这回事就行了~

让我想起了INTEL的Parallel Studio
记得intel貌似有个关于haskell并行计算的paper很有名…忘了叫什么名字
原来传说中的 FP 并行优势是这么用的……
有几个问题,前辈解释下。
Hoogle 了一下,英文太差,par is generally used when the value of a is likely to be required later 没看明白什么意思。这就是正文中所指的惰性求值?是不是推迟到第二个参数计算的时候再计算第一个参数的意思啊?
还有
sorted_lt `par` sorted_gt `pseq` (sorted_lt ++ [x] ++ sorted_gt)
这句是不是也可以写成这样:
par sorted_lt $ pseq sorted_gt $ sorted_lt ++ [x] ++ sorted_gt
最后一个问题,如果是
lt `par` gt `pseq` (psort lt ++ [x] ++ psort gt)
结果会怎样?是不是还是 1/2 时间?
@xpycc
haskell真的把惰性求值玩到极致了,我也是刚刚才搞明白 :P
在这个程序里,按照设定,会先并行计算sorted_lt和sorted_gt,在两个函数都求出值来之后再计算(sorted_lt ++ [x] ++ [sorted_gt])
若是没有惰性求值的命令式语言,在函数调用前先把参数的值都求出来,像sorted_lt `par` sorted_gt `pseq` (sorted_lt ++ [x] ++ sorted_gt)这样的语句,非但不会有并行,反而会执行两次sorted_lt和sorted_gt。不过这里有了惰性求值呢,参数中的sorted_lt和sorted_gt都不会立即执行,这样就可以将行为传递了。
par的类型声明:par :: a -> b -> b
par is generally used when the value of a is likely to be required later
“推迟到第二个参数计算的时候再计算第一个参数”,貌似就是这样。我想这句话的意思应该就是par的第一个参数在真正需要时不会进行计算,像sorted_lt `par` sorted_gt `pseq` (sorted_lt ++ [x] ++ sorted_gt)这句,只有在最后(sorted_lt++[x]++sorted_gt)用到了sorted_lt和sorted_gt时,它才会进行并行计算sorted_gt和sorted_lt。如果改成sorted_lt `par` sorted_gt `pseq` (1+1),我想sorted_lt和sorted_gt可能就永远不会被计算了。
反单引号`允许以中缀形式调用haskell的函数
sorted_lt `par` sorted_gt `pseq` (sorted_lt ++ [x] ++ sorted_gt)
与
par sorted_lt $ pseq sorted_gt $ sorted_lt ++ [x] ++ sorted_gt
就是等价的,感觉在这里用中缀的形式貌似更好看些 :)
关于
lt `par` gt `pseq` (psort lt ++ [x] ++ psort gt)
应该会先并行计算lt和gt的值(即filter),由于在这里计算的主要时间貌似都花在filter上,我想应该也会有性能提升,有时间试下 :)
@ssword 刚刚看了Hoogle 的 par 源码,一头雾水……
如果都有性能提升,那会是哪个提升得多一些呢?我感觉是后一个快一些,因为 sorted_lt 和 sorted_gt 的时间不一定是一致的,于是等慢的算完了,才合并。而 lt 和 gt 的时间肯定大致是一样的。
前来膜拜~
@xpycc
profile下试试就知道了,呵呵
@fledna
您的每次出现都是让我内牛满面啊,囧
毫无深度可言
@focus
膜拜有深度的大牛