以下两个代码来自RWH书的并发章节:
force :: [a] -> ()
force xs = go xs `pseq` ()
where go (_:xs) = go xs
go [] = 1
randomInts :: Int -> StdGen -> [Int]
randomInts k g = let result = take k (randoms g)
in force result `seq` result
randomInts
是用于生成用于测试并行排序算法性能的随机数列表的函数。书中已经提到它们避免了上述代码中的一些潜在问题。这就是书中所说的:
不可见的数据依赖性。
当我们生成随机数列表时,只需打印 列表的长度不会执行足够的评估。这个啰嗦 评估列表的主干,但不评估其元素。实际上 在排序比较之前,不会评估随机数。
这可能会对性能产生严重影响。 a的价值 随机数取决于前一个随机数的值 列表,但我们已经将列表元素随机分散在我们的列表中 处理器核心。如果我们之前没有评估列表元素 排序,我们会遭受可怕的“乒乓”效应:不仅会 评估从一个核心反弹到另一个核心,性能会受到影响。
尝试从主体上方剪断力的施加: 你会发现并行代码很容易就会结束三次 比非并行代码慢。
所以基本上他们说通过使用force
函数他们避免了乒乓问题。但在解释force
函数时,他们再次描述它:
请注意,我们并不关心列表中的内容;我们走下脊柱 到最后,然后使用pseq一次。显然没有任何魔法 在这里:我们只是使用我们对Haskell的常规理解 评估模型。因为我们将在左手使用武力 par或pseq的一面,我们不需要返回有意义的值。
从force
函数的定义和上面的解释可以看出,不评估各个列表元素中的元素。那么randomInts
函数如何实际上避免了ping-pong
效应。这是书中的错误还是我理解错误?
答案 0 :(得分:0)
randomInts
实际上似乎没有ping-pong
效果。函数force
实际上不仅遍历列表的整个样条曲线,而且还评估列表的元素。
import Control.Parallel (par, pseq)
force :: [a] -> ()
force xs = go xs `pseq` ()
where go (_:xs) = go xs
go [] = 1
在ghci:
ghci > let a = [1..10]
ghci > :sprint a
a = _
ghci > force a
()
ghci > :sprint a
a = [1,2,3,4,5,6,7,8,9,10]
因此force
函数会完全评估列表,从乒乓效应中保存它。