我正在研究一个更复杂的程序,我希望它非常有效,但我现在已经把我的问题归结为以下简单的程序:
main :: IO ()
main = print $ foldl (+) 0 [(1::Int)..1000000]
我在这里构建并运行它。
$ uname -s -r -v -m
Linux 3.12.9-x86_64-linode37 #1 SMP Mon Feb 3 10:01:02 EST 2014 x86_64
$ ghc -V
The Glorious Glasgow Haskell Compilation System, version 7.4.1
$ ghc -O -prof --make B.hs
$ ./B +RTS -P
500000500000
$ less B.prof
Sun Feb 16 16:37 2014 Time and Allocation Profiling Report (Final)
B +RTS -P -RTS
total time = 0.04 secs (38 ticks @ 1000 us, 1 processor)
total alloc = 80,049,792 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc ticks bytes
CAF Main 100.0 99.9 38 80000528
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc ticks bytes
MAIN MAIN 44 0 0.0 0.0 100.0 100.0 0 10872
CAF Main 87 0 100.0 99.9 100.0 99.9 38 80000528
CAF GHC.IO.Handle.FD 85 0 0.0 0.0 0.0 0.0 0 34672
CAF GHC.Conc.Signal 83 0 0.0 0.0 0.0 0.0 0 672
CAF GHC.IO.Encoding 76 0 0.0 0.0 0.0 0.0 0 2800
CAF GHC.IO.Encoding.Iconv 60 0 0.0 0.0 0.0 0.0 0 248
看起来每次迭代分配80个字节。我认为期望编译器在这里生成无分配代码是非常合理的。
我的期望是否不合理?分配是否是启用分析的副作用?我怎样才能完成任务以摆脱分配?
答案 0 :(得分:3)
在这种情况下,看起来GHC足够聪明,可以将foldl
优化为更严格的形式,但GHC无法优化中间列表,因为foldl
不是good consumer ,所以大概是那些分配是针对( EDIT3 :不,看起来不是这样;请参阅注释)(:)
构造函数的。
通过使用foldr
融合,您可以摆脱中间列表:
main :: IO ()
main = print $ foldr (+) 0 [(1::Int)..1000000]
......正如您所见:
k +RTS -P -RTS
total time = 0.01 secs (10 ticks @ 1000 us, 1 processor)
total alloc = 45,144 bytes (excludes profiling overheads)
与
具有相同的内存配置文件main = print $ (1784293664 :: Int)
编辑:在这个新版本中,我们正在为堆栈上的一堆(1 + (2 + (3 +...)))
交换堆分配。为了真正得到一个好的循环,我们必须手写,如:
main = print $ add 1000000
add :: Int -> Int
add nMax = go 0 1 where
go !acc !n
| n == nMax = acc + n
| otherwise = go (acc+n) (n+1)
表示:
total time = 0.00 secs (0 ticks @ 1000 us, 1 processor)
total alloc = 45,144 bytes (excludes profiling overheads)
EDIT2 我还没有使用Gabriel Gonzalez foldl library,但也可能值得为你的应用程序使用。