List と ByteString を比較してみました。
以下のようなテキストファイルを読み込み、ファイル内に書かれた数字の文字列を数字に変換し、合計を算出するものです。
$ cat num.txt 127 69 76 70 1 1 1 0 0 0 0 0 0 0 0 0 2 0 3 0 1 0 0 0 64 158 4 8 52 0 0 0 212 0 6 0 0 0 0 0 52 0 32 0 9 0 40 0 31 0 28 0 6 0 0 0 52 0 0 0 52 128 4 8 52 128 4 8 32 1 0 0 32 1 0 0 5 0 0 0 4 0 0 0 3 0 0 0 84 1 0 0 84 129 4 8 84 129 4 8 19 0 0 0 19 0 0 0 4 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 128 4 8 0 128 4 8 40 178 5 0 40 178 5 0 5 0 0 0 0 16 0 0 1 0 0 0 224 (snip)
- List を使って集計。どちらもメモリをいっぱい使っている。
import Numeric main = readFile "./num.txt" >>= print . sumFile where sumFile = sum .(map readDigit) . words readDigit s = (read s)::Integer
数字の要素は 625283 あります。
ghci> f<-readFile "./num.txt" ghci> take 3 $ words f -- => ["127","69","76"] ghci> length $ words f -- => 625283
$ time ./sum +RTS -K200M 52607898 real 0m46.554s user 0m46.143s sys 0m0.296s
- ByteString を使ったもの
import Numeric import qualified Data.ByteString.Lazy.Char8 as L main = L.readFile "./num.txt" >>= print . sumFile where sumFile = sum . (map (toNumber.(L.readInteger))).(L.words) toNumber (Just (x,_)) = x toNumber Nothing = 0
ghci> f <- L.readFile "./num.txt" ghci> take 3 $ L.words f -- => [Chunk "127" Empty,Chunk "69" Empty,Chunk "76" Empty] ghci> length $ L.words f -- => 625283
$ time ./sum +RTS -K200M 52607898 real 0m3.406s user 0m3.276s sys 0m0.108s
この例では圧倒的に ByteString が高速です。
正格版の Data.ByteString.Char8 にすると微妙ですが、さらに高速でした。
$ time ./sum +RTS -K200M 52607898 real 0m3.169s user 0m3.036s sys 0m0.132s
正格版の Data.ByteString.Char8 で L.readInteger を L.readIntにすると微妙に高速。
$ time ./sum +RTS -K200M 52607898 real 0m2.994s user 0m2.924s sys 0m0.072s
最後に遅延評価版の Data.ByteString.Lazy.Char8 で L.readInt の場合、信じられない遅さ。何これ?
import Numeric import qualified Data.ByteString.Lazy.Char8 as L main = L.readFile "./num.txt" >>= print . sumFile where sumFile = sum . (map (toNumber.(L.readInt))).(L.words) toNumber (Just (x,_)) = x toNumber Nothing = 0 $ time ./sum +RTS -K200M 52607898 real 0m22.200s user 0m22.089s sys 0m0.112s
おまけ:Vectorも追加してみました。List に読み込んで fromList で変換していますので一手間多いのですが、結果は List と ByteString の中間くらい。
import Numeric import qualified Data.Vector as V main = readFile "./num.txt" >>= print . sumFile where sumFile = V.sum . V.map (\s->(read s)::Integer) . V.fromList . words {- $ time ./vector +RTS -K200M 52607898 real 0m21.279s user 0m21.077s sys 0m0.184s -}
+---------------------------+-----------+----------------+ | Type | Integer | Int | +---------------------------+-----------+----------------+ |Data.List | 0m46.554s | 0m46.157s | |Data.Vector | 0m21.279s | 0m21.581s | |Data.ByteString.Lazy.Char8 | 0m3.406s | 0m22.200s | |Data.ByteString.Char8 | 0m3.169s | 0m2.994s | +---------------------------+-----------+----------------+