2

我在玩 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'. 除了“聪明”之外,我认为另一个权衡必须是它更快。那么使用部分应用的函数会使代码运行得更快吗?如果是这样,为什么?

4

2 回答 2

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稍微复杂一些。然而,内联main6main7提升 lambda 抽象为一个提供了完全相同的核心。

由于 GHC 可以执行所有优化,我会对代码性能之间的任何差异感到非常惊讶。如果它是赞成的,那就更惊讶了count

Haskell 的整个禅意就是编写干净的程序并让编译器处理使其更快。如果非foldr版本更清晰,请写下。但是在阅读fold了几年之后,foldr版本看起来更清晰了,所以我会写那个。不过,最酷的部分是,您使用哪个并不重要,因为它们都被编译为相同的代码。

于 2013-10-30T02:19:56.330 回答
1

在以足够高的优化水平编译的程序中使用部分应用的函数几乎不能使它们更快,只会更慢。仅仅是因为在这种情况下,您向编译器提供的有关您的意图的信息较少。

例外情况是,如果部分应用的函数被赋予名称和INLINE编译指示(在某些特殊情况下)。这是对 GHC 的一种暗示。

于 2013-10-30T06:24:44.440 回答