Haskell 折叠列表的运行时间截然不同

Haskell 折叠列表的运行时间截然不同,haskell,parallel-processing,Haskell,Parallel Processing,我把元素加在一个环上。在本文中,我将使用有理数: intList n = map (\x -> [x]) [1 .. n] toReciprocal xs = map (\x -> [[1], x]) xs addFrac [[a], [b]] [[c], [d]] = let num = a*d + b*c denom = b*d g

我把元素加在一个环上。在本文中,我将使用有理数:

intList n = map (\x -> [x]) [1 .. n]

toReciprocal xs = map (\x -> [[1], x]) xs

addFrac [[a], [b]] [[c], [d]] = let num = a*d + b*c
                                    denom = b*d
                                    g = gcd num denom
                                in [[num `div` g], [denom `div` g]]

addFracs xs
    | length xs == 1 = head xs
    | otherwise = (head xs) `addFrac` (addFracs $ tail xs)

main = print $ addFracs $ toReciprocal $ intList 20000
我知道这不是你想要添加有理数的方式,但我的环有点复杂,这是最简单的例子,复制了我看到的问题。上面的代码非常慢(在我的dekstop上大约11.0秒),因此受这篇文章()的启发,我决定尝试:

parAddFracs xs
    | len == 1 = head xs
    | otherwise = (ys `par` zs) `pseq` (ys `addFrac` zs)
    where len = length xs
          (ys', zs') = splitAt (len `div` 2) xs
          ys = parAddFracs ys'
          zs = parAddFracs zs'

main = print $ parAddFracs $ toReciprocal $ intList 20000
这几乎会立即返回(~0.1秒),即使我没有使用-threaded进行编译

我试着把分数增加到2000000

main = print $ parAddFracs $ toReciprocal $ intList 2000000
使用-threaded进行编译,+RTS-N1运行约17.8秒,+RTS-N4运行约15.6秒。当使用+RTS-N4运行时,我看到cpu使用率在~120%到~350%之间,但尽管cpu使用率有所增加,但运行时间似乎没有什么改善

问题1:为什么使用parAddFracs比addFracs运行得更快(数量级),即使我没有使用线程支持进行编译

编辑:@DanielWagner指出,这种速度差异是由于两者在评估顺序上的差异造成的。我仍然不确定问题2的答案

问题2:在使用线程支持进行编译时,为什么从+RTS-N1移动到+RTS-N4对运行时间几乎没有影响,尽管它似乎对所使用的处理能力有很大影响


谢谢

一个猜想:
整数
当两个操作数的大小更接近时,乘法和加法等运算速度稍快。也许在进行分治时(如在第二个解决方案中)比在前后操作时(如在第一个解决方案中)更经常发生这种情况。快速更新:在我的测试中,
fold(+)0[1/x | x在这两个示例中都不要使用
length
。这既慢又难看。@DanielWagner你是对的,这在我发布的代码中解释了这个问题。事实上,foldb在我的原始代码上运行得比简单的折叠要快得多,尽管ParadFracs在添加我的环元素时仍然是foldb的两倍(我的对象的系数从来都不是很大).Edit:你知道为什么线程似乎占用了这么多cpu,但没有提高速度吗?我原本认为创建线程的开销可能会抵消额外的cpu,但我在帖子中提到的链接似乎在简单地添加数字时速度大幅提高。@augustss在第二个例子中,你能提供一个建议吗关于如何在不使用长度的情况下将列表一分为二?我理解如何在第一个示例中删除它。一个猜想:
Integer
当两个操作数的大小更接近时,乘法和加法之类的运算会稍微快一点。也许在进行除法运算时,这种情况会更常见(如在第二个解决方案中)比从前到后操作(如在第一个解决方案中)更快。快速更新:在我的测试中,
fold(+)0[1/x | x在这两个示例中都不要使用
length
。这既慢又难看。@DanielWagner你是对的,这在我发布的代码中解释了这个问题。事实上,foldb在我的原始代码上运行得比简单的折叠要快得多,尽管ParadFracs在添加我的环元素时仍然是foldb的两倍(我的对象的系数从来都不是很大).Edit:你知道为什么线程似乎占用了这么多cpu,但没有提高速度吗?我原本认为创建线程的开销可能会抵消额外的cpu,但我在帖子中提到的链接似乎在简单地添加数字时速度大幅提高。@augustss在第二个例子中,你能提供一个建议吗关于如何在不使用长度的情况下将列表一分为二?我理解如何在第一个示例中删除它。