简体   繁体   English

Haskell是部分应用的功能更快吗?

[英]Haskell are partially applied functions faster?

I was messing around with Haskell, and wondered if there were any differences in performances between these two functions: 我在和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

I tried the following code as a benchmark: main = print (count [1..10000] 1) , which resulted in the first (using partially applied + ) function being slightly faster on average. 我尝试使用以下代码作为基准: main = print (count [1..10000] 1) ,这导致第一个(使用部分应用+ )函数的平均速度略快。

I mainly wondered because, to me, count is way more confusing to read than count' . 我主要想知道,因为对我而言, count比“ count'更令人困惑。 Other than being "clever", I figured that another tradeoff must be that it's faster. 除了“聪明”之外,我还认为必须要权衡的是它更快。 So does using partially applied functions make code run faster? 那么使用部分应用的函数是否会使代码运行更快? If so, why? 如果是这样,为什么?

Well I took a look at the core from the following programs 好吧,我从以下程序中了解了核心

main = print (count [1..100000] 1)

and

main = print (count'  [1..100000] 1)

Compiling these and prettying up the resulting core gives almost identical files, I've removed the not interesting bits about [1..10000] and print since they're identical. 编译这些文件并修饰生成的内核将得到几乎相同的文件,由于它们是相同的,因此我删除了关于[1..10000]print的不感兴趣的部分。

count: 计数:

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
    }

count': 计数':

main_e = __integer 1
main5 =
  \ x acc ->
    case eqInteger x main_e of _ {
      False -> acc;
      True -> case acc of _ { I# x1 -> I# (+# x1 1) }
    }

So it actually generates almost identical core, with count generating slightly more complicated core. 因此它实际上生成几乎相同的核心,而count生成稍微复杂一些的核心。 However inlining main6 and main7 and lifting the lambda abstraction into one gives completely identical core. 但是,将main6main7内联并把lambda抽象提升为一个,就可以得到完全相同的核心。

Since GHC can perform all the optimizations I'd be very surprised at any difference between the code's performance. 由于GHC可以执行所有优化,因此我对代码性能之间的任何差异感到非常惊讶。 And even more surprised if it was in favour of count . 如果它支持count那就更加惊讶了。

The whole zen of Haskell is to just write clean programs and let the compiler deal with making it faster. Haskell的整个禅意就是编写干净的程序,并让编译器处理得更快。 If the non- foldr version is clearer, write that. 如果非文件foldr版本更清晰,请写下。 But after reading fold s for a few years the foldr version looks clearer so I'd write that. 但是在阅读fold几年之后, foldr版本看起来更清晰了,所以我写了。 The cool part though, it doesn't matter which you use since they both are compiled to the same code. 不过很酷的部分是,使用哪种都无所谓,因为它们都被编译为相同的代码。

Using partially applied functions in programs compiled with sufficiently high level of optimization could hardly make them faster, only slower. 在经过足够高的优化程度编译的程序中使用部分应用的函数很难使它们变快,而只会变慢。 Simply because in this case you provide the compiler with less information about your intents. 只是因为在这种情况下,您为编译器提供了有关意图的较少信息。

With the exception, if the partially applied function is given the name and INLINE pragma (in some special cases). 例外情况是,如果为部分应用的函数指定了名称和INLINE编译指示(在某些特殊情况下)。 This is a kind of hint for GHC. 这是对GHC的一种暗示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM