自己定义严格的应用程序($!)并不会产生相同的性能

Mar*_*ier 19 haskell ghc

我最近正在阅读Michael Snoyman撰写的这篇博客文章.在那里建议的练习中,我试图自己定义$!运算符:

import Prelude hiding ( ($!) )

($!) :: (a -> b) -> a -> b
($!) f x = x `seq` f x

mysum :: [Int] -> Int
mysum list0 =
  go list0 0
  where
    go [] total = total
    go (x:xs) total = go xs $! total + x

main = print $ mysum [1..1000000]
Run Code Online (Sandbox Code Playgroud)

虽然记忆的使用很糟糕,但我认为这种方法效果很好.我的第一个问题是这个.为什么这不好用?

然后,我在Prelude中检查了它的定义.它写道:

($!)                    :: (a -> b) -> a -> b
f $! x                  = let !vx = x in f vx  -- see #2273
Run Code Online (Sandbox Code Playgroud)

所以,我把它复制到我的代码中:

{-# LANGUAGE BangPatterns #-}

import Prelude hiding ( ($!) )

($!) :: (a -> b) -> a -> b
($!) f x =
  let !vx = x
   in f vx

mysum :: [Int] -> Int
mysum list0 =
  go list0 0
  where
    go [] total = total
    go (x:xs) total = go xs $! total + x

main = print $ mysum [1..1000000]
Run Code Online (Sandbox Code Playgroud)

结果是:

Linking mysum4 ...
500000500000
     209,344,064 bytes allocated in the heap
     130,602,696 bytes copied during GC
      54,339,936 bytes maximum residency (8 sample(s))
          66,624 bytes maximum slop
              80 MB total memory in use (0 MB lost due to fragmentation)
Run Code Online (Sandbox Code Playgroud)

你可以看到这与使用Prelude的$!运算符的结果相比有多糟糕:

Linking mysum4 ...
500000500000
     152,051,776 bytes allocated in the heap
          41,752 bytes copied during GC
          44,384 bytes maximum residency (2 sample(s))
          21,152 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)
Run Code Online (Sandbox Code Playgroud)

我的第二个问题是这种差异来自何处?

此外,我认为它可以像这样重写:

($!) :: (a -> b) -> a -> b
f $! !x = f x
Run Code Online (Sandbox Code Playgroud)

有什么理由不这样做吗?这是我的第三个问题.

luq*_*qui 26

啊哈! 这是一个优先问题.你忘了粘贴这行:

infixr 0  $!
Run Code Online (Sandbox Code Playgroud)

因此,当您使用自己的版本时,它会被解析为

go (x:xs) total = (go xs $! total) + x
Run Code Online (Sandbox Code Playgroud)

这显然有可怕的表现.它几乎是巧合,甚至可以给你正确的答案.

  • 你说"几乎是巧合"; 为什么"差不多"? (3认同)
  • @ wizzwizz4只是对冲,所以我没有得到好评*实际上*,因为+是可交换的和asso ... (3认同)