在Haskell中读取大型二进制文件的最快方法?

时间:2019-04-23 05:19:44

标签: haskell

我想处理一个太大而无法读入内存的二进制文件。目前,我使用ByteString.Lazy.readFile来传输字节。我认为使用streaming软件包使我的程序更快是一个好主意。但是,readFile的{​​{3}}表示:

  

readFile :: FilePath -> (Stream (Of String) IO () -> IO a) -> IO a

     

使用类型为'Stream(Of String)IO()-> IO a'的函数读取文件的行,以将流转换为'IO a'类型的值。

因此,streaming包仅读取ASCII文本文件?我可以使用该程序包以字节为单位读取二进制文件吗?

2 个答案:

答案 0 :(得分:3)

为了详细说明@Cubic的评论,尽管人们普遍认为应该在生产代码中避免使用惰性I / O,而应使用流方法代替它,但这与性能没有直接关系。如果您正在编写程序来对大文件进行一次性处理,只要您现在可以运行良好的惰性I / O版本,那么可能就没有很好的 performance 理由来转换它了转到流媒体包。

实际上,流传输更有可能增加一些开销,因此,我怀疑在大多数情况下,优化的惰性I / O解决方案的性能将不及优化的流传输解决方案。

避免使用惰性I / O的主要原因是previously discussed on SO。简而言之,懒惰的I / O使得难以一致地管理资源(例如,文件句柄和网络套接字),难以推断出空间使用情况(例如,小的程序更改可能会导致内存使用量爆炸),并且如果有问题的I / O的时间和顺序很重要,则有时是“不安全的”(如果您只是读入一组文件和/或写出另一组文件,通常就不成问题了。)

用于读取和/或写入大文件的短时间运行实用程序可能是以惰性I / O风格编写的不错的候选人。只要它们在运行时没有任何明显的空间泄漏,就可以了。

答案 1 :(得分:2)

仅使用 streaming bytestring ,就可以编写如下内容:

import           Data.ByteString
import           Streaming
import qualified Streaming.Prelude as S
import           System.IO

fromHandle :: Int -> Handle -> Stream (Of ByteString) IO ()
fromHandle chunkSize h = 
    S.untilRight $ do bytes <- Data.ByteString.hGet h chunkSize
                      pure $ if Data.ByteString.null bytes then Right ()
                                                           else Left bytes

使用 bytestring 中的hGetnull streaming 中的untilRight。您将需要使用withFile来获取Handle,并在回调中使用Stream

dump :: FilePath -> IO ()
dump file = withFile file ReadMode go
 where
   go :: Handle -> IO ()
   go = S.mapM_ (Data.ByteString.hPut stdout) . fromHandle 4096