Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Haskell';圣莫纳德酒店_Haskell_Logging_Out Of Memory_Lazy Evaluation_Bytestring - Fatal编程技术网

在Haskell';圣莫纳德酒店

在Haskell';圣莫纳德酒店,haskell,logging,out-of-memory,lazy-evaluation,bytestring,Haskell,Logging,Out Of Memory,Lazy Evaluation,Bytestring,我有一个Haskell程序,在ST monad内运行时生成约280M的日志文本数据。这就是几乎所有内存消耗的地方(在禁用日志记录的情况下,程序总共分配3MB的实际内存) 问题是,我的内存不足。当程序运行时,内存消耗超过1.5GB,当它试图将日志字符串写入文件时,内存最终耗尽 log函数获取一个字符串,并将日志数据累积到存储在环境中STRef中的字符串生成器中: import qualified Data.ByteString.Lazy.Builder as BB ... myLogFunctio

我有一个Haskell程序,在ST monad内运行时生成约280M的日志文本数据。这就是几乎所有内存消耗的地方(在禁用日志记录的情况下,程序总共分配3MB的实际内存)

问题是,我的内存不足。当程序运行时,内存消耗超过1.5GB,当它试图将日志字符串写入文件时,内存最终耗尽

log函数获取一个字符串,并将日志数据累积到存储在环境中STRef中的字符串生成器中:

import qualified Data.ByteString.Lazy.Builder as BB
...
myLogFunction s = do
    ...
    lift $ modifySTRef myStringBuilderRef (<> BB.stringUtf8 s)
这会额外消耗几GB内存。我尝试了不同的缓冲设置,并首先转换为lazy ByteString(稍微好一点)

Qs:

  • 在程序运行时,如何最大限度地减少内存消耗?我希望给出一个严格的ByteString表示和适当的严格程度,我需要的内存比我存储的约280M实际日志数据多一点

  • 如何在不分配内存的情况下将结果写入文件?我不明白为什么Haskell需要GB的内存才能将一些常驻数据流式传输到文件中

编辑:

这是一个小规模运行的内存配置文件(约42MB的日志数据)。在禁用日志记录的情况下,总内存使用量为3MB

    15,632,058,700 bytes allocated in the heap
     4,168,127,708 bytes copied during GC
       343,530,916 bytes maximum residency (42 sample(s))
         7,149,352 bytes maximum slop
               931 MB total memory in use (0 MB lost due to fragmentation)

                                      Tot time (elapsed)  Avg pause  Max pause
    Gen  0     29975 colls,     0 par    5.96s    6.15s     0.0002s    0.0104s
    Gen  1        42 colls,     0 par    6.01s    7.16s     0.1705s    1.5604s

    TASKS: 3 (1 bound, 2 peak workers (2 total), using -N1)

    SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

    INIT    time    0.00s  (  0.00s elapsed)
    MUT     time   32.38s  ( 33.87s elapsed)
    GC      time   11.97s  ( 13.31s elapsed)
    RP      time    0.00s  (  0.00s elapsed)
    PROF    time    0.00s  (  0.00s elapsed)
    EXIT    time    0.00s  (  0.00s elapsed)
    Total   time   44.35s  ( 47.18s elapsed)

    Alloc rate    482,749,347 bytes per MUT second

    Productivity  73.0% of total user, 68.6% of total elapsed
编辑:

我运行了一个内存配置文件,并按要求运行了一个小日志:

我试着添加爆炸模式,$!,deepseq/$!!,在相关的地方使用武力等等,但这似乎没有任何区别。我如何强迫Haskell实际使用我的string/printf表达式等,并将其放入一个严格的ByteString中,而不是保留所有那些[Char]列表和未计算的Thunk

编辑:

下面是实际的完整跟踪函数

trace s = do
     enable <- asks envTraceEnable
     when (enable) $ do
        envtrace <- asks envTrace
        let b = B8.pack s
        lift $ b `seq` modifySTRef' envtrace (<> BB.byteString b)
好吗


谢谢

由于日志消息占用了那么多内存,因此在生成日志消息后立即将其写入文件将更有效。这似乎是不可能的,因为我们在圣莫纳德里面,而你不能在圣莫纳德里面执行IO

但有一个解决办法:使用一些类似“管道”包的协程monad转换器。下面是一个示例,使用:

它的工作原理如下:您使用
respond
在producer中发出日志消息,同时仍然驻留在ST monad中。通过这种方式,您可以记录并确保您的计算不会执行一些奇怪的IO操作。不过,这会迫使您在代码中添加提升

构建ST计算后,使用
将生成器的基本monad从ST转换为IO。是一个有用的功能,可以让你在盘子还在桌上的时候更换桌布


现在我们在爱娥大陆!剩下要做的唯一一件事就是将生产者与实际写入消息的消费者连接起来(在这里,消息被打印到stdout,但您也可以轻松地连接到写入文件的消费者)

日志记录与状态无关,因此我建议您在ITE中使用Writer monad,即使从使用带STRef的reader monad转换为Writer,我也有同样的情况。最后它是一个类型生成器的幺半群。我不想在transformer堆栈中添加writer,因为没有充分的理由。我们需要更多的数据。你能给我们看一个堆配置文件吗?您的日志是如何生成的?例如,如果您使用
stringUtf8
,那么我的怀疑是生成的
Builder
包含大量对
String
的引用,这就是内存所在。顺便说一句,问题几乎肯定是过度懒惰和不理解
Builder
表示的细节。强制使用
Builder
不会产生任何效果。如果
ByteString
的结构中有一个对象图,则强制将
ByteString
添加到其中是至关重要的。@正如我所怀疑的那样,您的堆配置文件显示了普通
字符串
消耗的大量内存。我同意@Carl的说法,这看起来像是过度懒惰。顺便说一句,您可以使用
hp2ps-c
生成颜色输出。我不得不承认这有点让我头疼,但我一直想查看pipes包!但需要明确的是,日志消息的大小绝对没有问题。280MB完全可以!问题是Haskell在“Haskell的东西”上浪费GBs。如果我使用一个500 MB的Word8s的可变未绑定向量实现日志记录,并将其写入一个文件,那么一切都会很好。我只是想问,是否有人能告诉我为什么ByteString库需要比预期多5倍的存储空间,然后分配GB的临时内存来将数据写入文件。
trace s = do
     enable <- asks envTraceEnable
     when (enable) $ do
        envtrace <- asks envTrace
        let b = B8.pack s
        lift $ b `seq` modifySTRef' envtrace (<> BB.byteString b)
do
    trace $ printf "%i" myint
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE LiberalTypeSynonyms #-}

import Control.Monad
import Control.Monad.ST
import Control.Monad.ST (stToIO) -- Transforms ST computations into IO computations
import Control.Monad.Trans
import Control.Monad.Morph (hoist) -- Changes the base monad of a monad transformer
import Control.Proxy.Prelude (stdoutD) -- Consumer that prints to stdout
import Control.Proxy.Core
import Control.Proxy.Core.Correct

import Data.STRef

simpleST :: ST s Bool
simpleST= do
    ref <- newSTRef True
    writeSTRef ref False
    readSTRef ref

-- Like simpleST, but emits log messages during the computation
loggingST :: Producer ProxyCorrect String (ST s) Bool
loggingST = do
    ref <- lift $ newSTRef True
    respond "Before writing"
    lift $ writeSTRef ref False
    respond "After writing"
    lift $ readSTRef ref

adapt :: (forall s . Producer ProxyCorrect String (ST s) a) ->
         Producer ProxyCorrect String IO a
adapt x = hoist stToIO x

main :: IO ()
main = do
    result <- runProxy $ (\_ -> adapt loggingST) >-> stdoutD
    putStrLn . show $ result
Before writing
After writing
False