Here is a modification of your program which is twice faster and twice less hungry for memory:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BS
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Char8 as BS (lines, readInt)
import Data.List (sortOn)
import qualified Data.Map.Strict as Map
main :: IO ()
main = do
mapFrequencies <- Map.fromList . parseFrequencies <$> BS.readFile fileFrequencies
ls <- BS.lines <$> BS.readFile fileData
let sorted = sortOn (\k -> Map.findWithDefault 0 k mapFrequencies) ls
BSB.writeFile fileSorted $ foldMap ((<> "\n") . BSB.byteString) sorted
fileFrequencies :: FilePath
fileFrequencies = "deu_news_2020_freq.txt"
fileData :: FilePath
fileData = "german.utf8.dic"
fileSorted :: FilePath
fileSorted = "german.utf8.sorted.dic"
parseFrequencies :: BS.ByteString -> [(BS.ByteString, Int)]
parseFrequencies bs = case BS.uncons bs of
Nothing -> []
-- this is admittedly brittle, just to demonstrate single-pass parsing with readInt
Just (35, _) -> parseFrequencies (BS.unsafeTail (BS.dropWhile (/= 10) bs))
_ -> let (w, f) = BS.break (== 9) bs in
case BS.readInt (BS.unsafeTail f) of
Just (i, bs') -> (w, i) : parseFrequencies (BS.unsafeTail bs')
Nothing -> []
Key insights:
- Your data is UTF8, but all parsing-sensitive characters (which are
#,\tand\n) are ASCII. This suggests to useByteStringinsteadText, immediately halving memory requirements. - Both files must be loaded in full before sorting. So no point in lazy
ByteString: in fact it will degrade performance by increasing heap fragmentation. - Since keys are short strings, there is unlikely to be much benefit from
HashMap, we can useMapand do not waste space for hashes. - No need to involve
Vectoronly for the purpose of sorting, plain oldData.List.sortOnis good enough. - Use
findWithDefaultinstead offromMaybe/lookup, because it allocates less. - Use
readIntto parse integers fromByteString, do not resort toread . unpack. - Use
Builderfor output, avoiding constructing it in memory in full.