День добрый. Написал небольшую программку. Читает из файла список чисел, фильтрует, записывает результат. Так как с Хаскелем начинающий, спрашиваю общественность, можно ли улучшить/ускорить/уменьшить поток памяти?
module Main where
import qualified Data.ByteString.Lazy.Char8 as B
import Maybe
import Data.List
import IO
import System.Environment
-- input type
type IT = [Int]
-- output type
type OT = [Int]
main = do
(inputFilePath : outputFilePath : []) <- getArgs
loadData inputFilePath >>= return.treatData >>= saveResult outputFilePath
-- function to load data
loadData :: String -> IO IT
loadData fileName = do
content <- B.readFile fileName
loadIntList content
-- load int list
loadIntList :: B.ByteString -> IO IT
loadIntList str = return ( map (fst.fromJust.B.readInt) . filter (not.B.null) . B.splitWith isBreakSymbol $ str )
where
isBreakSymbol '\n' = True
isBreakSymbol ' ' = True
isBreakSymbol _ = False
-- function to save data
saveResult :: String -> OT -> IO ()
saveResult fileName res = writeFile fileName ( concat $ map ((" "++).show) res )
-- scale and convert
toDouble :: Double -> [Int] -> [Double]
toDouble scale = map ((scale*).fromIntegral)
-- scale and convert
toInt :: Double -> [Double] -> [Int]
toInt scale = map (truncate.(scale*))
-- dot product of two lists
convolution :: [Double] -> [Double] -> Double
convolution filter dat = zipEx filter dat 0
where
zipEx [] _ acc = acc
zipEx _ [] acc = acc
zipEx (a:as) (b:bs) acc = zipEx as bs $! (acc+a*b)
-- Example:
-- for m = 4, p = 2, [1,2,3,4,5,6,7,8,9,10,...] => [ [1,2,3,4], [3,4,5,6], [5,6,7,8], ... ]
partition1 :: Int -> Int -> [Double] -> [[Double]]
partition1 m p lst = map (take m) $ iterate (drop p) lst
-- function to treat data
treatData :: IT -> OT
treatData = toInt 1000000 . map (convolution filter) . takeWhile (not.null) . partition1 m p . toDouble 0.01
where
filter = [0.1,0.3,-0.2,-0.1,3.2,0.7,4.5,7.8,-1.3,6,0.1,0.3,-0.2,-0.1,3.2,0.7,4.5,7.8,-1.3,6]
m = length filter
p = 2
компилирую : ghc.exe -O2
Привожу профайлинг для 10 млн чисел на входе.
COST CENTRE MODULE %time %alloc
convolution Main 22.4 18.8
loadIntList Main 21.6 17.6
saveResult Main 20.8 21.3
partition1 Main 15.4 23.1
toInt Main 12.0 11.7
treatData Main 4.3 2.7
toDouble Main 3.5 4.2
1. Почему toInt намного затратнее toDouble ?
2. Можно ли оптимизировать partition1 по памяти?
3. Можно ли сделать более эффективно loadIntList и saveResult?
Также через запуск с RTS видно здоровый поток по памяти (9 гиг) что настораживает.
Это нормально или можно поправить?
9,621,450,136 bytes allocated in the heap
13,334,116 bytes copied during GC
36,912 bytes maximum residency (78 sample(s))
12,460 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)
Generation 0: 18275 collections, 0 parallel, 0.08s, 0.08s elapsed
Generation 1: 78 collections, 0 parallel, 0.03s, 0.03s elapsed
INIT time 0.02s ( 0.00s elapsed)
MUT time 12.19s ( 14.67s elapsed)
GC time 0.11s ( 0.11s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 12.31s ( 14.78s elapsed)
%GC time 0.9% (0.7% elapsed)
Alloc rate 788,441,496 bytes per MUT second
Productivity 99.0% of total user, 82.5% of total elapsed
Добавлена разметка — Кодт