haskell - Data.Set 成员并行查找
问题描述
我正在尝试使用以下代码中的函数在 Data.Set 中进行并行(多核)查找member
:
import Control.Parallel.Strategies (parMap, rpar)
import qualified Data.Set as S
cntSeq :: S.Set Int -> [Int] -> Int
cntSeq set xs = foldl (\c x -> c + (mmbr set x)) 0 xs
where
mmbr st x | S.member x st = 1
| otherwise = 0
cntPar :: Int -> S.Set Int -> [Int] -> Int
cntPar n set xs =
let
chnks = chunksOf n xs
tr = transpose chnks
in sum $ parMap rpar (cntSeq set) tr
但似乎cntPar
实际上并没有从多核中受益。
如果我只用常规列表查找替换 Set ,则并行版本的加速非常重要。
我需要用类似-threaded
选项的东西重新编译 Data.Set 吗?
(顺便说一句,我确实意识到这intersection
将是一个选项,只是我对并行化集合查找感兴趣——这只是不同问题的简化版本。)
解决方案
您不需要重新编译Data.Set
。-threaded
使用标志编译最终程序并使用+RTS -N
或类似方式运行它就足够了。
但是,您的测试用例的一个大问题是,我相信执行时间完全由xs
列表chunksOf
的遍历支配transpose
。您看到了列表查找的不同之处,因为它们非常慢,但是任何实际大小的集合查找都太快了,您无法观察到任何好处。
另外,作为一个旁注,parMap rpar
并没有真正的意义。 parMap
已经并行触发了计算,因此rpar
是多余的(并引入了一些额外的微不足道的开销,因为它可能会重新触发计算)。相反,使用,这是一种通过将每个元素评估为 WHNF ( ) 来parMap rseq
并行计算列表元素 ( ) 的策略。parMap
rseq
要查看一些好处,您可以尝试以下操作。这会传入列表列表以定义避免遍历(甚至可能实际创建)列表的块。在我的 16 核机器上编译-O2 -threaded
和运行+RTS -N
,它在大约 90 毫秒内运行串行和单块并行版本。具有 10 个块的并行版本运行速度要快得多,大约需要 30 毫秒,而 100 个块的版本则需要 20 毫秒。
import Criterion.Main
import Data.List (transpose)
import Data.List.Split (chunksOf)
import Control.Parallel.Strategies (parMap, rseq)
import qualified Data.Set as S
cntSeq :: S.Set Int -> [Int] -> Int
cntSeq set xs = sum (map (mmbr set) xs)
where
mmbr st x | S.member x st = 1
| otherwise = 0
cntPar :: S.Set Int -> [[Int]] -> Int
cntPar set = sum . parMap rseq (cntSeq set)
main = do
let evens = S.fromList [0,2..200000000]
defaultMain
[ bench "serial" $ whnf (cntSeq evens) [0..999999]
, bench "parallel1" $ whnf (cntPar evens) [[0..999999]] -- one chunk
, bench "parallel10" $ whnf (cntPar evens) [[i..i+99999] | i <- [0,100000..900000]] -- 10 chunks
, bench "parallel100" $ whnf (cntPar evens) [[i..i+9999] | i <- [0,10000..990000]] -- 100 chunks
]
我机器上的基准输出:
benchmarking serial
time 90.63 ms (90.21 ms .. 91.02 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 92.47 ms (91.81 ms .. 93.48 ms)
std dev 1.372 ms (807.8 μs .. 2.023 ms)
benchmarking parallel1
time 91.13 ms (90.50 ms .. 91.77 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 91.73 ms (91.38 ms .. 92.26 ms)
std dev 696.0 μs (427.3 μs .. 905.7 μs)
benchmarking parallel10
time 31.25 ms (23.77 ms .. 37.88 ms)
0.897 R² (0.745 R² .. 0.987 R²)
mean 54.53 ms (43.81 ms .. 81.93 ms)
std dev 29.69 ms (18.88 ms .. 40.02 ms)
benchmarking parallel100
time 19.99 ms (19.81 ms .. 20.19 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 20.13 ms (19.99 ms .. 20.62 ms)
std dev 536.7 μs (111.9 μs .. 1.043 ms)
推荐阅读
- swift - 测试是否符合 RawRepresentable 协议并强制转换为 RawRepresentable 协议
- kubernetes - Kubernetes 中的分布式 JMeter 增加堆大小
- android - Ionic 设备准备好不会仅在 Android 上触发
- kotlin - 将列表添加到另一个列表的末尾
- javascript - 是否可以在 dc.js 中使用该折线图的不同颜色部分绘制单个折线图
- reactjs - 反应useRef没有被分配
- javascript - TypeError:无法读取未定义的属性“ss”
- php - PHP - 从多个 CSV 文件中插入多行,在 mysql 中具有一致的列
- java - 如何在生产中运行corda节点
- python-3.x - 通过yandex账户发送邮件的python脚本是什么?