【发布时间】:2018-12-09 13:09:06
【问题描述】:
我有一个动态编程算法,我发现我的 Haskell 实现非常令人满意,因为它允许我递归地定义主数组,如下所示:
fill :: Int -> Int -> Entry
fill 0 0 = Entry Origin 0.0
fill 0 j = ...
fill i 0 = ...
fill i j = f m i j
m :: Array (Int, Int) Entry
m = listArray dims [fill i j | (i, j) <- range dims]
其中f 是一个中等复杂的函数,它引用主数组m。 Entry 类型只是一个带有小注解的Double。
数据本身非常大,m 最终有大约 1 亿个条目。该算法仍然非常快,但在此过程中使用了约 25GB 的 RAM。从阅读中我了解到这是因为它保留了数组条目的完整计算而不是最终值,但是如果我切换到未装箱的数组,我无法像上面的示例那样递归地定义它们。
有没有办法吃蛋糕(低内存占用)并吃掉它(递归定义)?
【问题讨论】:
-
根据你的数据,大概消耗250B/entry。也许您可以尝试通过使您的
Entry类型更严格来改进它,例如通过添加严格性注释和/或解包。您无法避免Entry本身的装箱并使用未装箱的数组,但您可能可以从Entry的组件中删除装箱。该类型是如何定义的? -
您确定要使用整个矩阵进行计算,还是会有一些未评估的条目?很多?
-
暂时使用 25GB 可以吗?在程序启动时的预计算阶段,甚至可能之前——只要算法本身在运行期间使用较少?
-
感谢大家的回复。 @chi
data Entry = Entry Source Double,data Source = Origin | Left | Right | Top | Bottom。超级简单。我相信拆箱Double部分可能会有所帮助,因为这是递归定义的部分。 @luqui 我估计实际上可能使用了一半的条目,并且内存分析支持这一点。 @DanielWagner 我很好奇你的想法。原则上,任何阶段的 25 GB 都有点重,因为我希望最终在较小的机器上运行它,但我肯定在听。 -
我肯定会尝试
data Entry = Entry {-# UNPACK #-} !Source {-# UNPACK #-} !Double这样的基本类型。不要忘记使用-O开启优化(并避免使用 GHCi 来衡量性能)
标签: haskell memory dynamic-programming