上下文:我在名为toXlsx :: ByteString -> Xlsx
的库中定义了一个函数(ByteString来自Data.ByteString.Lazy)
现在要做某些操作我已经定义了在同一个文件上运行的某些函数,因此我想打开,读取并将文件转换为Xlsx一次并将其保存在内存中以便与它一起操作。
现在我正在以bs <- Data.ByteString.Lazy.readfile file
的形式阅读该文件,最后正在Data.ByteString.Lazy.length bs 'seq' return value
。
有没有办法使用这个函数并将文件作为一个整体保存在内存中以重用它?
答案 0 :(得分:4)
请注意,惰性字节字符串的工作方式,文件的内容在被“使用”之前不会被读取,但一旦被读取,它们将保留在内存中以用于任何后续操作。它们被从内存中删除的唯一方法是它们是垃圾收集的,因为你的程序不再有任何方法可以访问它们。
例如,如果您在大文件上运行以下程序:
import qualified Data.ByteString.Lazy as BL
main = do
bigFile <- BL.readFile "ubuntu-14.04-desktop-amd64.iso"
print $ BL.length $ BL.filter (==0) bigFile -- takes a while
print $ BL.length $ BL.filter (==255) bigFile -- runs fast
第一次计算实际上会将整个文件读入内存,并将保留在那里进行第二次计算。
我猜这本身并不太令人信服,因为操作系统也会将文件缓存到内存中,并且最终很难分辨出Haskell从操作系统缓存中读取文件的时间差异计算并将其保存在所有计算的内存中。但是,如果您对此代码运行了一些堆分析,您会发现第一个操作将整个文件加载到“固定”字节串中,并且该分配在后续操作中保持不变。
如果您担心的是您希望在开始时读取完整的文件,即使第一个操作不需要全部读取,因此在读取文件的其他部分时不会出现后续延迟,然后基于seq
的解决方案可能没问题。或者,您可以将整个文件作为严格的字节串读取,然后使用fromStrict
进行转换 - 此操作是即时的,不会复制任何数据。 (与toStrict
相比,这是昂贵的, 复制数据。)所以这将有效:
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
main = do
-- read strict
bigFile <- BS.readFile "whatever.mov"
-- do strict and lazy operations
print $ strictOp bigFile
print $ lazyOp (BL.fromStrict bigFile)