packages feed

lsm-tree-1.0.0.0: bench/micro/Bench/Database/LSMTree/Internal/Index.hs

{-# LANGUAGE CPP #-}

module Bench.Database.LSMTree.Internal.Index (benchmarks) where

import           Control.Category ((>>>))
import           Control.DeepSeq (rnf)
import           Control.Monad.ST.Strict (runST)
import           Criterion.Main (Benchmark, Benchmarkable, bench, bgroup, env,
                     whnf)
#if !MIN_VERSION_base(4,20,0)
import           Data.List (foldl')
                 -- foldl' is included in the Prelude from base 4.20 onwards
#endif
import           Database.LSMTree.Extras.Generators (mkPages, toAppends)
                     -- also for @Arbitrary@ instantiation of @SerialisedKey@
import           Database.LSMTree.Extras.Index (Append, append)
import           Database.LSMTree.Internal.Index (Index,
                     IndexType (Compact, Ordinary), newWithDefaults, search,
                     unsafeEnd)
import           Database.LSMTree.Internal.Serialise (SerialisedKey)
import           Test.QuickCheck (choose, vector)
import           Test.QuickCheck.Gen (Gen (MkGen))
import           Test.QuickCheck.Random (mkQCGen)

-- * Benchmarks

benchmarks :: Benchmark
benchmarks = bgroup "Bench.Database.LSMTree.Internal.Index" $
             map (uncurry benchmarksForSingleType)          $
             [("Compact", Compact), ("Ordinary", Ordinary)]
    where

    benchmarksForSingleType :: String -> IndexType -> Benchmark
    benchmarksForSingleType indexTypeName indexType
        = bgroup (indexTypeName ++ " index") $
          [
              -- Search
              env (pure $ searchIndex indexType 10000) $ \ index ->
              env (pure $ searchKeys 1000)             $ \ keys  ->
              bench "Search" $
              searchBenchmarkable index keys,

              -- Incremental construction
              env (pure $ incrementalConstructionAppends 10000) $ \ appends ->
              bench "Incremental construction" $
              incrementalConstructionBenchmarkable indexType appends
          ]

-- * Utilities

-- | Deterministically constructs a value using a QuickCheck generator.
generated :: Gen a -> a
generated (MkGen exec) = exec (mkQCGen 411) 30

{-|
    Constructs serialised keys that conform to the key size constraint of
    compact indexes.
-}
keysForIndexCompact :: Int             -- ^ Number of keys
                    -> [SerialisedKey] -- ^ Constructed keys
keysForIndexCompact = vector                                        >>>
                      generated

{-|
    Constructs append operations whose serialised keys conform to the key size
    constraint of compact indexes.
-}
appendsForIndexCompact :: Int      -- ^ Number of keys used in the construction
                       -> [Append] -- ^ Constructed append operations
appendsForIndexCompact = keysForIndexCompact                >>>
                         mkPages 0.03 (choose (0, 16)) 0.01 >>>
                         generated                          >>>
                         toAppends
{-
    The arguments used for 'mkPages' are the same as the ones used for
    'genPages' in the instantiation of 'Arbitrary' for 'LogicalPageSummaries' at
    the time of writing.
-}

{-|
    Constructs an index by applying append operations to an initially empty
    index.
-}
indexFromAppends :: IndexType -> [Append] -> Index
indexFromAppends indexType appends = runST $ do
    indexAcc <- newWithDefaults indexType
    mapM_ (flip append indexAcc) appends
    snd <$> unsafeEnd indexAcc

-- * Benchmark ingredients

-- ** Search

-- | Constructs an index to be searched.
searchIndex :: IndexType -- ^ Type of index to construct
            -> Int       -- ^ Number of keys used in the construction
            -> Index     -- ^ Constructed index
searchIndex indexType keyCount
    = indexFromAppends indexType (appendsForIndexCompact keyCount)

-- | Constructs a list of keys to search for.
searchKeys :: Int             -- ^ Number of searches
           -> [SerialisedKey] -- ^ Constructed search keys
searchKeys = keysForIndexCompact

-- | The action to be performed by a search benchmark.
searchBenchmarkable :: Index -> [SerialisedKey] -> Benchmarkable
searchBenchmarkable index = whnf $ foldl' (\ _ key -> rnf (search key index)) ()

-- ** Incremental construction

-- | Constructs append operations to be used in index construction.
incrementalConstructionAppends ::
       Int      -- ^ Number of keys used in the construction
    -> [Append] -- ^ Constructed append operations
incrementalConstructionAppends = appendsForIndexCompact

-- | The action to be performed by an incremental-construction benchmark.
incrementalConstructionBenchmarkable :: IndexType -> [Append] -> Benchmarkable
incrementalConstructionBenchmarkable indexType appends
    = whnf (indexFromAppends indexType) appends