Haskell中是否定义了部分或curried函数的性能?

Osc*_*kin 22 performance profiling haskell partial-application lazy-evaluation

在以下代码中:

ismaxl :: (Ord a) => [a] -> a -> Bool
ismaxl l x = x == maxel
           where maxel = maximum l

main = do
  let mylist = [1, 2, 3, 5]
  let ismax = ismaxl mylist
  --Is each call O(1)?  Does each call remember maxel?
  let c1 = ismax 1
  let c2 = ismax 2
  let c3 = ismax 3
  let c5 = ismax 5
  putStrLn (show [c1, c2, c3, c5])
Run Code Online (Sandbox Code Playgroud)

部分函数是否为max,计算maxel?特别是,有人可以指出关于Haskell中部分函数的复杂性的规则吗?在上面的例子中,编译器必须只调用一次最大值吗?换句话说,部分函数是否保留了内部where子句的先前调用的引用?

我有一些CPU限制的代码不能令人满意,我正在寻找可能的错误,我的理由是复杂性.

C. *_*ann 17

作为您可以从分析Haskell代码中学到的东西的演示,这里是对代码进行一些小修改的结果.首先,我把它换成mylist[0..10000000],以确保它需要一段时间来计算最大.

在运行该版本之后,以下是分析输出中的一些行:

COST CENTRE                    MODULE               %time %alloc

ismaxl                         Main                  55.8    0.0
main                           Main                  44.2  100.0

                                                         individual    inherited
COST CENTRE              MODULE         no.    entries  %time %alloc   %time %alloc

MAIN                     MAIN            1           0   0.0    0.0   100.0  100.0
 CAF:main_c5             Main          225           1   0.0    0.0    15.6    0.0
  main                   Main          249           0   0.0    0.0    15.6    0.0
   ismaxl                Main          250           1  15.6    0.0    15.6    0.0
 CAF:main_c3             Main          224           1   0.0    0.0    15.6    0.0
  main                   Main          246           0   0.0    0.0    15.6    0.0
   ismaxl                Main          247           1  15.6    0.0    15.6    0.0
 CAF:main_c2             Main          223           1   0.0    0.0    14.3    0.0
  main                   Main          243           0   0.0    0.0    14.3    0.0
   ismaxl                Main          244           1  14.3    0.0    14.3    0.0
 CAF:main_c1             Main          222           1   0.0    0.0    10.4    0.0
  main                   Main          239           0   0.0    0.0    10.4    0.0
   ismaxl                Main          240           1  10.4    0.0    10.4    0.0
 CAF:main8               Main          221           1   0.0    0.0    44.2  100.0
  main                   Main          241           0  44.2  100.0    44.2  100.0
Run Code Online (Sandbox Code Playgroud)

这显然是重新计算最大值.

现在,替换ismaxl为:

ismaxl :: (Ord a) => [a] -> a -> Bool
ismaxl l = let maxel = maximum l in (== maxel)
Run Code Online (Sandbox Code Playgroud)

......再次分析:

COST CENTRE                    MODULE               %time %alloc

main                           Main                  60.5  100.0
ismaxl                         Main                  39.5    0.0

                                                         individual    inherited
COST CENTRE              MODULE         no.    entries  %time %alloc   %time %alloc

MAIN                     MAIN            1           0   0.0    0.0   100.0  100.0
 CAF:main_c5             Main          227           1   0.0    0.0     0.0    0.0
  main                   Main          252           0   0.0    0.0     0.0    0.0
   ismaxl                Main          253           1   0.0    0.0     0.0    0.0
 CAF:main_c3             Main          226           1   0.0    0.0     0.0    0.0
  main                   Main          249           0   0.0    0.0     0.0    0.0
   ismaxl                Main          250           1   0.0    0.0     0.0    0.0
 CAF:main_c2             Main          225           1   0.0    0.0     0.0    0.0
  main                   Main          246           0   0.0    0.0     0.0    0.0
   ismaxl                Main          247           1   0.0    0.0     0.0    0.0
 CAF:main_c1             Main          224           1   0.0    0.0     0.0    0.0
 CAF:main_ismax          Main          223           1   0.0    0.0    39.5    0.0
  main                   Main          242           0   0.0    0.0    39.5    0.0
   ismaxl                Main          243           2  39.5    0.0    39.5    0.0
 CAF:main8               Main          222           1   0.0    0.0    60.5  100.0
  main                   Main          244           0  60.5  100.0    60.5  100.0
Run Code Online (Sandbox Code Playgroud)

......这次它大部分时间用在一次通话中ismaxl,其他时间太快甚至没有注意到,所以它必须在这里只计算一次最大值.


sig*_*fpe 11

这是您的代码的修改版本,可以让您查看是否maxel重用:

import Debug.Trace

ismaxl :: (Ord a) => [a] -> a -> Bool
ismaxl l x = x == maxel
           where maxel = trace "Hello" $ maximum l

main = do
  let mylist = [1, 2, 3, 5]
  let ismax = ismaxl mylist
  --Is each call O(1)?  Does each call remember maxel?
  let c1 = ismax 1
  let c2 = ismax 2
  let c3 = ismax 3
  let c5 = ismax 5
  putStrLn (show [c1, c2, c3, c5])
Run Code Online (Sandbox Code Playgroud)

你会发现maxel应用程序之间没有"记住".

通常,在所有参数都提供给函数之前,您不应期望Haskell开始执行缩减.

另一方面,如果您启用了激进的优化,那么很难预测特定编译器实际会做什么.但是,您可能不应该依赖编译器的任何部分,当您可以轻松地重写代码以显示您想要的内容时,很难预测.


Joh*_*n L 6

基于其他好的答案,GHC并不急于根据我的经验进行这种优化.如果我不能轻易地创造出无点的东西,我经常会在LHS和lambda上使用混合绑定变量来编写:

ismaxl :: (Ord a) => [a] -> a -> Bool
ismaxl l = \x -> x == maxel
           where maxel = maximum l
Run Code Online (Sandbox Code Playgroud)

我并不特别喜欢这种风格,但确实可以确保maxel在部分应用的调用之间共享ismaxl.

  • 更明确地说:`ismaxl l = let maxel =最大l in\x - > x == maxel`.对于编译器来说它或多或少是相同的,但在我看来,似乎更明显的是"let"在lambda之外. (3认同)