【发布时间】:2016-03-25 13:44:01
【问题描述】:
我正在尝试将 CSV 作为带有 Cassava 的 Vector 加载到内存中。我的程序确实可以工作,但是对于 50MB 的 csv 文件使用了大量内存,我不明白为什么。
我知道使用 Data.Csv.Streaming 应该更适合大文件,但我认为 50MB 仍然可以。我尝试了 Data.Csv 和 Data.Csv.Streaming 以及来自 github 项目页面的或多或少的规范示例,我还尝试实现自己的解析器,输出 Vector of Vector(我的代码基于 attoparsec-csv https://hackage.haskell.org/package/attoparsec-csv) ,所有这些解决方案都使用大约 2000MB 的内存!我确信我正在做的事情有问题。这样做的正确方法是什么?
我的最终目标是将数据完全加载到内存中以供以后进一步处理。例如,我可以将我的数据拆分为有趣的矩阵,并与使用 Hmatrix 的矩阵一起工作。
这是我用 Cassava 尝试过的 2 个程序:
1/ 使用 Data.Csv
import qualified Data.ByteString.Lazy as BL
import qualified Data.Vector as V
import Data.Csv
import Data.Foldable
main = do
csv <- BL.readFile "train.csv"
let Right res = decode HasHeader csv :: Either String (V.Vector(V.Vector(BL.ByteString)))
print $ res V.! 0
2/ 使用 Data.Csv.Streaming
{-# LANGUAGE BangPatterns #-}
import qualified Data.ByteString.Lazy as BL
import qualified Data.Vector as V
import Data.Csv.Streaming
import Data.Foldable
main = do
csv <- BL.readFile "train.csv"
let !a = decode HasHeader csv :: Records(V.Vector(BL.ByteString))
let !res = V.fromList $ Data.Foldable.toList a
print $ res V.! 0
请注意,我没有给你我基于 attoparsec-csv 制作的程序,因为它与 Vector 而不是 List 几乎完全相同。该解决方案的内存使用率仍然很差。
有趣的是,在 Data.Csv.Streaming 解决方案中,如果我简单地使用 Data.Foldable.for_ 打印我的数据,一切都非常快,内存使用量为 2MB。这让我觉得我的问题与我构建 Vector 的方式有关。可能是累积 thunk 而不是将原始数据堆叠成紧凑的数据结构。
感谢您的帮助,
安东尼
【问题讨论】:
-
问题与懒惰的
ByteString有关。我通过定义strictRead path = evaluate . force <$> BL.read path解决了这个问题,但我认为这不是最好的答案。你也可以试试ByteString.toLazy <$> ByteString.Strict.readFile path,但我从来没有测试过。 -
我试图对 ByteString 进行严格化,但解码函数需要一个 Lazy ByteString,所以我对严格化的 ByteString 进行了惰性化。同样,内存使用量很大,所以我不确定这是否有效。
-
尝试第一种方法。
force来自Control.DeepSeq和evaluate来自Control.Exception。evaluate会将其放入 WHNM 中,force将对其进行全面评估。 -
不幸的是也不起作用。我这样做了:
csv' <- BL.readFile "train.csv" csv <- evaluate $ force csv',我仍然得到大约 2600MB 的内存使用量。 -
其实我写的是
(evaluate . force) =<< BL.read path,但是脱糖后应该和你的一样。是的,所以我认为解决方案是使用cassava的其他模块,很抱歉无法提供帮助,我也对答案感兴趣。如果您在这里没有答案,请尝试 subreddit,它非常活跃。