我正在讨论Haskell,并想知道这两个功能之间的性能是否存在差异:
count :: (Eq a) => [a] -> a -> Int
count xs e = foldr countCheck 0 xs
where countCheck x
| x == e = (1+)
| otherwise = (0+)
count' :: (Eq a) => [a] -> a -> Int
count' xs e = foldr countCheck 0 xs
where countCheck x acc
| x == e = acc + 1
| otherwise = acc
我尝试使用以下代码作为基准:main = print (count [1..10000] 1)
,这导致第一个(使用部分应用的+
)函数平均稍快一些。
我主要想知道,因为对我而言,count
比count'
更令人困惑。除了“聪明”之外,我认为另一个权衡必须是它更快。那么使用部分应用的函数会使代码运行得更快吗?如果是这样,为什么?
答案 0 :(得分:5)
我看了以下程序的核心
main = print (count [1..100000] 1)
和
main = print (count' [1..100000] 1)
编译这些内容并对生成的内核进行调整会得到几乎相同的文件,我删除了关于[1..10000]
和print
的不感兴趣的内容,因为它们完全相同。
数:
main_e = __integer 1
main7 = \ x -> x
main6 =
\ a -> case a of _ { I# b -> I# (+# 1 b) }
main5 =
\ x ->
case eqInteger x main_e of _ {
False -> main7;
True -> main6
}
计数':
main_e = __integer 1
main5 =
\ x acc ->
case eqInteger x main_e of _ {
False -> acc;
True -> case acc of _ { I# x1 -> I# (+# x1 1) }
}
因此它实际上生成了几乎相同的核心,count
生成稍微复杂的核心。但是,内联main6
和main7
并将lambda抽象提升为一个可以提供完全相同的核心。
由于GHC可以执行所有优化,我会对代码性能之间的任何差异感到非常惊讶。如果赞成count
,那就更加惊讶了。
Haskell的全部禅就是编写干净的程序,让编译器处理它让它更快。如果非foldr
版本更清楚,那就写下来。但在阅读fold
几年后,foldr
版本看起来更清晰,所以我写了。但是很酷的部分,你使用它们并不重要,因为它们都编译成相同的代码。
答案 1 :(得分:1)
在使用足够高的优化级别编译的程序中使用部分应用的函数很难使它们更快,只是更慢。仅仅因为在这种情况下,您可以为编译器提供有关您的意图的更少信息。
除了例外,如果部分应用的函数被赋予名称和INLINE
pragma(在某些特殊情况下)。这是对GHC的一种暗示。