我试图通过 haskell 读取一个大的 csv 文件,并按每列生成字数。

这在文件中超过 4M 行。

所以我选择读取一个块并每次获取字数(5k 行一个块)。
然后把它加在一起。

当我用 12000 行和 120000 行测试函数时,时间几乎呈线性增加。
但是当读取180000行时,运行时间超过了四倍。

我觉得是内存不够用,换磁盘让功能慢很多。

我已经将我的代码编写为 map/reduce 样式,但是如何使 haskell 不将所有数据保存在内存中?

打击是我的代码和分析结果。

import Data.Ord
import Text.CSV.Lazy.String
import Data.List
import System.IO
import Data.Function (on)
import System.Environment

splitLength = 5000


mySplit' [] = []
mySplit' xs = [x] ++  mySplit' t
    where
    x = take splitLength xs
    t = drop splitLength xs

getBlockCount::Ord a => [[a]] -> [[(a,Int)]]
getBlockCount t =   map
    (map (\x -> ((head x),length x))) $
    map group $ map sort $ transpose t

foldData::Ord a=> [(a,Int)]->[(a,Int)]->[(a,Int)]
foldData lxs rxs = map combind wlist
    where
        wlist = groupBy ((==) `on` fst) $ sortBy (comparing fst) $ lxs ++ rxs
        combind xs
         | 1==(length xs) = head xs
         | 2 ==(length xs) = (((fst . head) xs ), ((snd . head) xs)+((snd . last) xs))


loadTestData datalen = do
    testFile <- readFile "data/test_csv"
    let cfile = fromCSVTable $ csvTable $ parseCSV testFile
    let column = head cfile
    let body = take datalen $ tail cfile
    let countData = foldl1' (zipWith  foldData)  $ map  getBlockCount  $ mySplit' body
    let output =  zip column $ map ( reverse . sortBy (comparing snd) ) countData
    appendFile "testdata" $ foldl1 (\x y -> x ++"\n"++y)$ map show $tail output

main = do
    s<-getArgs
    loadTestData $ read  $ last s

分析结果
loadData +RTS -p -RTS 12000

total time  =        1.02 secs   (1025 ticks @ 1000 us, 1 processor)
total alloc = 991,266,560 bytes  (excludes profiling overheads)

 loadData +RTS -p -RTS 120000

total time  =       17.28 secs   (17284 ticks @ 1000 us, 1 processor)
total alloc = 9,202,259,064 bytes  (excludes profiling overheads)



   loadData +RTS -p -RTS 180000

total time  =       85.06 secs   (85059 ticks @ 1000 us, 1 processor)
total alloc = 13,760,818,848 bytes  (excludes profiling overheads)

最佳答案

所以首先,提出一些建议。

  • 列表并不快。好吧,好吧,缺点是恒定的时间,但总的来说,列表并不快。您正在使用列表。 (Data.Sequence 对于两端 cons'ing 和消费会更快)
  • 字符串很慢。字符串很慢,因为它们是 [Char](字符列表)。您当前使用的库是根据字符串列表编写的。通常字符链表的链表不是您想要的文本处理。这不是布埃诺。将来使用 Text (for, uh, text) 或 ByteString (for bytes) 而不是 String ,除非它很小并且对性能不敏感。
  • 您使用的库只是懒惰,而不是流式传输。您必须处理将流行为叠加到惰性语义上才能获得持续的内存使用。流库解决了增量处理数据和限制内存使用的问题。我建议学习 Pipes 或 Conduit 来解决一般问题。一些特定于问题的库还将提供可用于流式传输的 iteratee API。 Iteratee API 可以直接使用或连接到 Pipes/Conduit/等。

  • 我认为您使用的库不是一个好主意。

    我建议您使用以下库之一:

    http://hackage.haskell.org/package/pipes-csv(基于管道)

    https://hackage.haskell.org/package/cassava-0.4.2.0/docs/Data-Csv-Streaming.html(通用 CSV 库,不基于特定的流媒体库)

    https://hackage.haskell.org/package/csv-conduit(基于导管)

    这些应该为您提供良好的性能和恒定的内存使用模数,无论您可能累积什么。

    关于haskell - 如何读取大的csv文件?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26706997/

    10-11 22:34
    查看更多