dawgdic-1.0.0: bench/Main.hs
{-# LANGUAGE BangPatterns #-}
module Main (main) where
import Control.Exception (evaluate)
import Criterion.Main (bench, bgroup, defaultMain, nf, nfIO)
import Data.List (sort)
import Data.Maybe (listToMaybe)
import qualified Data.DAWG.DAWG as Dawg
import qualified Data.DAWG.Dictionary as Dict
import qualified Data.DAWG.Guide as G
import qualified Data.DAWG.RankedGuide as RG
import qualified Data.DAWG.Completer as C
import qualified Data.DAWG.RankedCompleter as RC
-- ** Dataset preparation
-- | Helper function to generate @lexicon.<N>.txt@. Use 'generateAll' instead. It will take a minute to generate input for benchmarks.
generateN :: FilePath -> Int -> IO ()
generateN fullLexiconPath n = do
let outputLexiconFile = concat ["lexicon.", show n, ".txt"]
alphabet = ['a' .. 'z']
go _ws [] = return ()
go ws (char : restAlphabet) = do
let allCurrent = takeWhile ((== (Just char)) . listToMaybe) ws
current = take n allCurrent
restWords = drop (length allCurrent) ws
appendFile outputLexiconFile $ unlines current
go restWords restAlphabet
writeFile outputLexiconFile "" -- refresh
words' <- lines <$> readFile fullLexiconPath
go (sort words') alphabet
-- | Helper function to generate @lexicon.<N>.v.txt@ from @lexicon.<N>.txt@. Use 'generateAllValues' instead. It will take a couple minutes to generate input for comparison benchmarks.
generateValue :: FilePath -> Int -> IO ()
generateValue lexiconPath n = do
contents <- lines <$> readFile lexiconPath
let size = length contents
values = reverse [1 .. size]
toLine (w, v) = concat [w, "\t", show v]
lexiconValPath = (<> ".v.txt") . reverse . drop 4 . reverse $ lexiconPath
writeFile lexiconValPath $ unlines (toLine <$> zip contents values)
-- | @fullLexiconPath@ should be the relative or absolute local path to the file @words_alpha.txt@
-- from <https://github.com/dwyl/english-words/tree/master>.
-- Consider downloading and unpacking @words_alpha.zip@.
generateAll fullLexiconPath = mapM_ (generateN fullLexiconPath) [10, 100, 1000]
-- ** Utilities
-- | Benchmark inputs were produced via 'generateAll' function.
--
-- Revision: @20f5cc9@.
readLexicon :: Int -> IO [String]
readLexicon n = do
let !lexiconFile = concat ["data/lexicon.", show n, ".txt"]
lines <$> readFile lexiconFile
dawgFromAscListBench lexicon = do
!_dawg <- Dawg.fromAscList lexicon
pure ()
dictContainsBench !dict lexicon =
let go dict !w = Dict.contains w dict
in go dict <$> lexicon
dictLookupBench !dict lexicon =
let go dict !w = Dict.lookup w dict
in go dict <$> lexicon
dictFollowBench !dict lexicon =
let go dict !w = Dict.follow w Dict.root dict
in go dict <$> lexicon
completerCompleteKeysBench !guide lexicon =
let go !w = C.completeKeys w guide
in concatMap go lexicon
rankedCompleterCompleteKeysBench !guide lexicon =
let go !w = RC.completeKeys w guide
in concatMap go lexicon
utilities n = do
!lexiconN <- evaluate =<< readLexicon n
!dawg <- evaluate =<< Dawg.fromAscList lexiconN
!dict <- evaluate =<< Dict.build' dawg
!guide <- evaluate =<< G.build' dawg dict
!rguide <- evaluate =<< RG.build' dawg dict
return $ bgroup (show n)
[ bench "Dawg.fromAscList" $ nfIO (dawgFromAscListBench lexiconN)
, bench "Dict.build'" $ nfIO (Dict.build' dawg)
, bench "Dict.contains" $ nf (dictContainsBench dict) lexiconN
, bench "Dict.lookup" $ nf (dictLookupBench dict) lexiconN
, bench "Dict.follow" $ nf (dictFollowBench dict) lexiconN
, bench "Guide.build'" $ nfIO (G.build' dawg dict)
, bench "Completer.completeKeys" $ nf (completerCompleteKeysBench guide) lexiconN
, bench "RankedCompleter.completeKeys" $ nf (rankedCompleterCompleteKeysBench rguide) lexiconN
]
-- ** Main
main :: IO ()
main = do
let inputs = [10, 100, 1000]
utilitiesBench <- mapM utilities inputs
defaultMain [ bgroup "Utilities" utilitiesBench ]