We/wy z String
jest mniej niż szybki w Haskell. Bajty odczytane z uchwytu generalnie muszą zostać przekonwertowane na punkty kodowe Unicode, a następnie na nich zbudowana jest lista połączona. To dużo pracy, która powoduje dużo alokacji. W takim przypadku konwersja do punktów kodowych jest nieco prostsza, ponieważ ustawiasz tryb stdin na binarny, ale budowa listy połączonych znaków wymaga jeszcze wiele czasu.
Innym małym czynnikiem jest to, że liczba linii używa Integer
, ale jest to niewielka i odgrywa znaczącą rolę tylko wtedy, gdy we/wy działa.
Jeśli potrzebujesz szybkiego wejścia/wyjścia, musisz użyć typu lepiej do tego przystosowanego. Jedną z możliwości jest użycie ByteString
np
import Data.List
import qualified Data.ByteString.Lazy.Char8 as C
main = do
txt <- C.getContents
putStrLn $ (\(w,l)->"line:"++(show l)++"\nwords:"++(show w)++"\n"). foldl' (\(w,l) r-> w `seq` l `seq` (w+C.length r ,succ l)) (0,0) . C.lines $ txt
spełnia swoje zadanie na plik 94MB w 0.12s na moim oknie (wc -l -c zajmuje 0.06s), podczas gdy oryginalna użyciu String
wziął 4.4s. To może być zoptymalizowane dalej,
{-# LANGUAGE BangPatterns #-}
import Data.List
import qualified Data.ByteString.Lazy.Char8 as C
main = do
txt <- C.getContents
putStrLn $ (\(w,l)->"line:"++(show l)++"\nwords:"++(show w)++"\n"). loop 0 0 . C.lines $ txt
loop :: Int -> Int -> [C.ByteString] -> (Int,Int)
loop !w !l (ln:lns) = loop (w + fromIntegral (C.length ln)) (l+1) lns
loop w l _ = (w,l)
trwa tylko 0.08s, który jest na tyle przyzwoity, żebym przestał optymalizacji tam (podobną zmianę dla wersji String
przynosi czas w dół do 3.6s do tego).
czy kompilowałeś z opcją -O2? –
tak, -O2 faktycznie po prostu przyspieszyć około 0.xx sekund – vzex
Spróbuj użyć ByteString: http://stackoverflow.com/questions/9746352/parsing-large-log-files-in-haskell –