我一直在尝试在 Haskell 中编码一个需要使用大量可变引用的算法,但与纯粹的惰性代码相比,它(也许并不奇怪)非常慢。
考虑一个非常简单的例子:
module Main where
import Data.IORef
import Control.Monad
import Control.Monad.Identity
list :: [Int]
list = [1..10^6]
main1 = mapM newIORef list >>= mapM readIORef >>= print
main2 = print $ map runIdentity $ map Identity list
在我的机器上运行 GHC 7.8.2,main1
需要 1.2 秒并使用 290MB 内存,而main2
仅需 0.4 秒,仅使用 1MB。有什么技巧可以阻止这种增长,尤其是在太空中?我经常需要IORef
s 用于非原始类型,与Int
,并假设IORef
会使用一个额外的指针,就像常规的 thunk 一样,但我的直觉似乎是错误的。
我已经尝试过一种特殊的列表类型,其中包含未打包的IORef
,但没有显着差异。
问题是你的使用mapM
,在时间和空间上的大型列表上总是表现不佳。正确的解决方案是使用以下方法融合中间列表mapM_
and (>=>)
:
import Data.IORef
import Control.Monad
list :: [Int]
list = [1..10^6]
main = mapM_ (newIORef >=> readIORef >=> print) list
它在恒定的空间中运行并提供出色的性能,在我的机器上运行只需 0.4 秒。
编辑:在回答你的问题时,你也可以这样做pipes
以避免手动熔断循环:
import Data.IORef
import Pipes
import qualified Pipes.Prelude as Pipes
list :: [Int]
list = [1..10^6]
main = runEffect $
each list >-> Pipes.mapM newIORef >-> Pipes.mapM readIORef >-> Pipes.print
在我的机器上,它在恒定空间中运行大约需要 0.7 秒。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)