{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.DeepSeq
import Criterion.Main
import Data.ByteString (ByteString)
import Data.Foldable
import Data.Hashable
import Data.Sequence (Seq)
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Data.Trie.Pattern
import qualified Data.Sequence as Seq
#if __GLASGOW_HASKELL__ < 804
import qualified Data.Trie as BST
#endif
main :: IO ()
main = defaultMain
[ bgroup "pattern-trie (ByteString)"
[ env (genStaticPTrie staticBKeys) $
bench "match (static)" . ptrieBMatch
, env (genPTrie captureBKeys) $
bench "match (capture)" . ptrieBMatch
]
, bgroup "pattern-trie (Text)"
[ env (genStaticPTrie staticTKeys) $
bench "match (static)" . ptrieTMatch
, env (genPTrie captureTKeys) $
bench "match (capture)" . ptrieTMatch
]
#if __GLASGOW_HASKELL__ < 804
, bgroup "bytestring-trie"
[ env (genBSTrie staticBKeys) $
bench "lookup" . bstrieLookup
]
#endif
]
------------------------------------------------------------------------------
-- pattern-trie
-- Generators
genStaticPTrie :: (Eq s, Hashable s) => [Str s] -> IO (Trie s Int)
genStaticPTrie keys = genPTrie (map mkPat keys)
where
mkPat = Seq.fromList . map EqStr
genPTrie :: (Eq s, Hashable s) => [Pattern s] -> IO (Trie s Int)
genPTrie pats = return $! build pats
where
build = fromAssocList . (`zip` [1..])
-- Benchmarks
ptrieBMatch :: Trie ByteString Int -> Benchmarkable
ptrieBMatch t = nf run bchunks
where
run :: Str ByteString -> Maybe (Int, Seq (Capture ByteString))
run s = match s t
ptrieTMatch :: Trie Text Int -> Benchmarkable
ptrieTMatch t = nf run tchunks
where
run :: Str Text -> Maybe (Int, Seq (Capture Text))
run s = match s t
------------------------------------------------------------------------------
-- bytestring-trie
#if __GLASGOW_HASKELL__ < 804
-- Generators
newtype BSTrie a = BSTrie (BST.Trie a)
instance NFData (BSTrie a) where
rnf t = t `seq` ()
genBSTrie :: [[ByteString]] -> IO (BSTrie Int)
genBSTrie keys = return $! build keys
where
build = BSTrie . BST.fromList . (`zip` [1..]) . map mconcat
-- Benchmarks
bstrieLookup :: BSTrie Int -> Benchmarkable
bstrieLookup (BSTrie t) = nf run (mconcat bchunks)
where
run :: ByteString -> Maybe Int
run s = BST.lookup s t
#endif
------------------------------------------------------------------------------
-- Test keys
staticBKeys :: [[ByteString]]
staticBKeys = genKeys bchunks
staticTKeys :: [[Text]]
staticTKeys = genKeys tchunks
captureBKeys :: [Pattern ByteString]
captureBKeys = captureKeys bchunks
captureTKeys :: [Pattern Text]
captureTKeys = captureKeys tchunks
captureKeys :: [s] -> [Pattern s]
captureKeys chunks = map Seq.fromList (genKeys matchers)
where
-- Replace the last chunk with a "capture", so it can appear at every
-- position of a generated key.
matchers = map EqStr (init chunks) ++ [AnyStr]
-- Given a list of length n, generate n! lists (i.e. keys), each of length n,
-- whereby the i'th item in the input list appears at positions 1..i. E.g. the
-- 1st item only appears as the 1st chunk of a path, the 2nd item can appear as
-- the 1st and 2nd chunk of a path, ..., the nth item can appear at any position
-- of a path (and in particular, it is the last chunk of every path).
--
-- For example:
-- @
-- genKeys [1..3]
-- [[1,2,3],[1,3,3],[2,2,3],[2,3,3],[3,2,3],[3,3,3]]
-- @
genKeys :: [a] -> [[a]]
genKeys l = mapM next [0..(length l - 1)]
where
next i = drop i l
-- (Static) ByteString chunks from which the keys of the tries are built up.
bchunks :: [ByteString]
bchunks =
[ "abcde"
, "bcdef"
, "cdefg"
, "defgh"
, "efghi"
, "fghij"
]
tchunks :: [Text]
tchunks = map decodeUtf8 bchunks