我试图通过haskell读取一个大的csv文件,并按每列生成单词count.
这个文件中有超过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
Run Code Online (Sandbox Code Playgroud)
剖析结果
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)
Run Code Online (Sandbox Code Playgroud)
bit*_*app 11
首先,提出一些建议.
列表并不快.好吧,好吧,缺点是恒定时间,但总的来说,列表并不快.你正在使用列表.(对于两端的消费和消费,Data.Sequence会更快)
字符串很慢.字符串很慢,因为它们是[Char](字符列表).您当前使用的库是根据字符串列表编写的.通常,链接列表的链接列表不是您想要的文本处理.这不是bueno.在将来使用Text(for,呃,text)或ByteString(for bytes)而不是String,除非它是小而且不是性能敏感的.
您正在使用的库只是懒惰而不是流式传输.您必须处理将流式行为叠加到惰性语义上以获得常量内存使用.流式库解决了递增处理数据和限制内存使用的问题.我建议为一般类问题学习Pipes或Conduit.一些特定于问题的库还将提供可用于流式传输的iteratee API.Iteratee API可以直接使用或连接到Pipes/Conduit/etc.
我不认为你使用的图书馆是个好主意.
我建议你使用以下库之一:
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(基于管道)
这些应该为你提供良好的性能和恒定的内存使用模数,无论你可能积累什么.