我有一个 Haskell 程序,它在 ST monad 内运行期间生成约 280M 的日志记录文本数据。这是几乎所有内存消耗的地方(在禁用日志记录的情况下,程序总共分配了 3MB 实际内存)。
问题是,我的内存用完了。程序运行时内存消耗超过1.5GB,最终在尝试将日志字符串写入文件时耗尽。
日志函数接受一个字符串并将日志数据累积到一个字符串构建器中,该字符串构建器存储在环境中的 STRef 中:
import qualified Data.ByteString.Lazy.Builder as BB
...
myLogFunction s = do
...
lift $ modifySTRef myStringBuilderRef (<> BB.stringUtf8 s)
我尝试使用 bang 模式和 modifySTRef' 来引入严格性,但这会使内存消耗更加严重。
我按照 hPutBuilder 文档的建议编写日志字符串,如下所示:
hSetBinaryMode h True
hSetBuffering h $ BlockBuffering Nothing
BB.hPutBuilder h trace
这会消耗几个额外的 GB 内存。我尝试了不同的缓冲设置并首先转换为惰性字节字符串(稍微好一点)。
问:
如何在程序运行时最小化内存消耗?我希望给定一个严格的 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
编辑:
我按照要求运行了一个带有小日志运行的内存配置文件:
个人资料 http://imageshack.us/a/img14/9778/6a5o.png
我尝试在相关位置添加爆炸模式、$!、deepseq/$!!、force 等,但似乎没有任何区别。我如何强制 Haskell 实际获取我的字符串 / 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)
这够“严格”吗?如果我在 ReaderT/ST monad 中调用这个类型类函数,我需要注意什么吗?只是为了它实际上被调用而不是以任何方式延迟。
do
trace $ printf "%i" myint
好吗?
谢谢!