【问题标题】:Processing a very large text file with lazy Texts and ByteStrings使用惰性文本和字节字符串处理非常大的文本文件
【发布时间】:2013-11-15 18:29:19
【问题描述】:

我正在尝试处理一个非常大的 unicode 文本文件 (6GB+)。我想要的是计算每个唯一单词的频率。在遍历文件时,我使用严格的Data.Map 来跟踪每个单词的计数。 该过程需要太多时间和太多内存(20GB+)。我怀疑地图很大,但我不确定它应该达到文件大小的 5 倍! 代码如下所示。请注意,我尝试了以下方法:

  • 使用Data.HashMap.Strict 代替Data.Map.StrictData.Map 似乎在较慢的内存消耗增加率方面表现更好。

  • 使用惰性ByteString 而不是惰性Text 读取文件。然后我将其编码为 Text 进行一些处理,然后将其编码回 ByteString for IO

    import Data.Text.Lazy (Text(..), cons, pack, append)
    import qualified Data.Text.Lazy as T
    import qualified Data.Text.Lazy.IO as TI
    import Data.Map.Strict hiding (foldr, map, foldl')
    import System.Environment
    import System.IO
    import Data.Word
    
    dictionate :: [Text] -> Map Text Word16
    dictionate = fromListWith (+) . (`zip` [1,1..])
    
    main = do
        [file,out] <- getArgs
        h <- openFile file ReadMode
        hO <- openFile out WriteMode
        mapM_ (flip hSetEncoding utf8) [h,hO]
        txt <- TI.hGetContents h
        TI.hPutStr hO . T.unlines . 
          map (uncurry ((. cons '\t' . pack . show) . append)) . 
          toList . dictionate . T.words $ txt
        hFlush hO
        mapM_ hClose [h,hO]
        print "success"
    

我的方法有什么问题?就时间和内存性能而言,完成我想做的事情的最佳方法是什么?

【问题讨论】:

  • @leftaroundabout 让我们假设最坏的情况,文件中的所有单词都是唯一的。 Map 大小应该达到 30GB 吗?
  • 我想是的,但对于Map 来说可能相当困难。如果您不希望单词中有太多重复,也许您应该完全切换到其他单词。 External merge sort(将重复计数和结块作为单独的步骤)相对简单。当然 anything external 必然会涉及到大量的脏 IO;我敢打赌,用 C 或 C++ 编写代码实际上更容易,这也让您可以更好地控制数据结构的开销。
  • 一点也不奇怪,@duplode 只是使用了一个文本文件,其中的唯一词少了很多(自然语言不可避免,并且使用许多 same的副本也无济于事> text) 比你在文件中明显有的要多。
  • 只是问一些愚蠢的问题:您正在使用-O2 进行编译,对吧?
  • 你可能会从使用bytestring-trie之类的东西中获利

标签: haskell text hashmap bigdata file-processing


【解决方案1】:

这是预期的内存使用量。 Data.Map.Map 消耗大约 6N 字的内存 + 键和值的大小(数据取自 this excellent post by Johan Tibell)。一个 lazy Texttakes up 7 words + 2*N bytes(四舍五入到机器字大小的倍数)和一个 Word16 takes up two words(标头 + 有效负载)。我们将假设一台 64 位机器,因此字长为 8 个字节。我们还将假设输入中的平均字符串长度为 8 个字符。

考虑到这一切,内存使用的最终公式是6*N + 7*N + 2*N + 2*N words。

在最坏的情况下,所有单词都会不同,大约有(6 * 1024^3)/8 ~= 800 * 10^6 个。将其插入上面的公式中,我们得到最坏情况下的地图大小约为。 102 GiB,这似乎与实验结果一致。反向求解这个方程告诉我们您的文件包含大约200*10^6 不同的单词。

至于解决此问题的替代方法,请考虑使用 trie(如 J.Abrahamson 在 cmets 中建议的那样)或近似方法,例如 count-min sketch

【讨论】:

    【解决方案2】:

    在传统数据处理的世界中,这个问题可以通过排序来解决(如果需要,可以在外部磁盘或磁带上),然后扫描排序的文件以计算组合在一起的单词运行次数。当然,您可以在排序的早期阶段进行一些部分缩减,以节省一些空间和时间。

    【讨论】:

      猜你喜欢
      • 2019-03-22
      • 1970-01-01
      • 2020-10-01
      • 2013-05-16
      • 2012-04-06
      • 1970-01-01
      • 2013-08-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多