繁体   English   中英

GHC编译二进制堆栈溢出?

[英]GHC compiled binary stack overflow?

我正在阅读《真实世界haskell》第8章,想了解SumFile.hs程序如何处理一百万个数字:

main :: IO ()
main = do
  contents <- getContents
  print (sumFile contents)
    where sumFile = sum . map read . words

当我使用以下命令向程序提供一百万个整数时:

Runhaskell SumFile.hs <data.txt,程序给出正确的结果。

但是,当我使用GHC编译它时:

ghc SumFile.hs

二进制文件给出“堆栈空间溢出”错误:

./SumFile < data.txt 
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.

我有两个问题:

  1. 是什么导致堆栈空间使用?
  2. 为什么编译版本与解释版本不同,我该怎么办?

谢谢!

编辑:

好的原因是map,但这是使用惰性字节串的修改版本:

import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as LCHAR
import Data.Monoid
import Data.List

main :: IO ()
main = do
  contents <- L.getContents
  case sumFile contents of
    Nothing -> print "Invalid input"
    Just s -> print $ getSum s
   where sumFile = foldl' mappend (Just (Sum 0)) . map ((fmap Sum) . (fmap fst) . LCHAR.readInt) . (LCHAR.words)

结果是一样的:即使我不使用sum,二进制版本也会占用堆栈空间。

我在#haskell上与其他人进行了讨论,ByteString版本产生堆栈溢出错误的原因是由于嵌套的Just(总和)而未对内部部分进行严格评估。

本质上,当我们映射两个Maybe(Just Num)(例如Just(总和2)和Just(总和3))时,foldl'使用seq生成Just((Sum 2)mappend(总和3)),即。 seq严格评估了最外面的构造函数(将两个Just(Monoid)附加到一个Just(Monoid)上)。 在这种情况下,内部Monoid不会得到严格评估,因此它们将保留为mappend连接(Sum Num)。 这样一来,Just中封装了100万个mappend连接(Sum Num)。

因此,#haskell上的Saizan提供了此版本,该版本严格评估Maybe(Sum Num)的内部

import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as LCHAR
import Data.Monoid
import Data.List

forceMaybe Nothing = Nothing
forceMaybe (Just x) = x `seq` (Just x)

main :: IO ()
main = do
  contents <- L.getContents
  case sumFile contents of
    Nothing -> print "Invalid input"
    Just s -> print $ getSum s
   where sumFile = foldl' (\ x y -> forceMaybe (x `mappend` y)) (Just (Sum 0)) . map ((fmap Sum) . (fmap fst) . LCHAR.readInt) . (LCHAR.words)

我检查了该书的在线版本,在该程序下进行了一些讨论,并且它使用堆栈空间的原因是因为map,用foldl代替map可以解决问题。

首先,简单说明一下:ghc运行时中的堆栈与堆栈段无关,它是运行时的内部结构,它不是缓冲区溢出类型攻击的来源。

第二。 Haskell很懒。 惰性io(getContents)产生惰性列表。 总和产生懒惰的结果。 但是,一旦请求求和的结果,就必须递归地挖掘列表,从而迅速耗尽堆栈空间(如果需要,您可以查看源代码)

为了避免这种情况,您必须使用严格的sum版本,它应该消除问题。 标准库在这种情况下具有特殊功能,即foldl'-foldl的严格版本。 使用foldl' (+) 0代替总和可以消除问题

第三。 当使用惰性IO时,堆栈空间泄漏是非常普遍的问题。 如果切换到基于Iteratee的IO,可能会解决。 否则,应该学会在需要的地方添加严格性注释。

啊。 顺便说一下。 GHC正在优化编译器。 这并不常见,但仍然有可能在编译程序中出现内存泄漏问题,而ghci却没有问题,反之亦然。

暂无
暂无

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

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