Skip to content

Commit

Permalink
Adds benchmarks for {elem,find}Ind{ex,ices}
Browse files Browse the repository at this point in the history
Adds separate benchmark suite for comparing the relative performance
of findIndex, findIndices, elemIndex, and elemIndices over first
occurence and second occurence in sparse strict bytestring

Also includes benchmark for performance of uninlined versus inlined
equality test as predicate for findIndex and findIndices
  • Loading branch information
archaephyrryx committed Aug 27, 2020
1 parent 155bf8a commit 6957f6d
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
136 changes: 136 additions & 0 deletions bench/BenchIndices.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
{-# LANGUAGE BangPatterns #-}
-- |
-- Copyright : (c) 2011 Simon Meier
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Simon Meier <[email protected]>
-- Stability : experimental
-- Portability : tested on GHC only
--
-- Benchmark all 'Builder' functions.
module Main (main) where

import Data.Foldable (foldMap)
import Data.Monoid
import Data.String
import Gauge
import Prelude hiding (words)
import GHC.Word (Word8, eqWord8)

import qualified Data.ByteString as S
import qualified Data.ByteString.Unsafe as S


------------------------------------------------------------------------------
-- Benchmark
------------------------------------------------------------------------------



-- lines of 200 letters from a to e, followed by repeated letter f
absurdlong :: S.ByteString
absurdlong = S.replicate 200 0x61 <> S.singleton 0xa
<> S.replicate 200 0x62 <> S.singleton 0xa
<> S.replicate 200 0x63 <> S.singleton 0xa
<> S.replicate 200 0x64 <> S.singleton 0xa
<> S.replicate 200 0x65 <> S.singleton 0xa
<> S.replicate 999999 0x66
{-# NOINLINE absurdlong #-}

main :: IO ()
main = do
Gauge.defaultMain
[ bgroup "ByteString strict first index" $
[ bench "FindIndices" $ nf bench_find_indices_first absurdlong
, bench "ElemIndices" $ nf bench_elem_indices_first absurdlong
, bench "FindIndex" $ nf bench_find_index_first absurdlong
, bench "ElemIndex" $ nf bench_elem_index_first absurdlong
]
, bgroup "ByteString strict second index" $
[ bench "FindIndices" $ nf bench_find_indices_second absurdlong
, bench "ElemIndices" $ nf bench_elem_indices_second absurdlong
, bench "FindIndex" $ nf bench_find_index_second absurdlong
, bench "ElemIndex" $ nf bench_elem_index_second absurdlong
]
, bgroup "ByteString index equality inlining" $
[ bench "FindIndices/inlined" $ nf bench_find_indices_inline absurdlong
, bench "FindIndices/non-inlined" $ nf bench_find_indices_noinline absurdlong
, bench "FindIndex/inlined" $ nf bench_find_index_inline absurdlong
, bench "FindIndex/non-inlined" $ nf bench_find_index_noinline absurdlong
]
]

safeHead :: [Int] -> Maybe Int
safeHead (!x:_) = Just x
safeHead _ = Nothing
{-# INLINE safeHead #-}

bench_find_indices :: S.ByteString -> [Int]
bench_find_indices = S.findIndices (== 0xa)
{-# INLINE bench_find_indices #-}

bench_elem_indices :: S.ByteString -> [Int]
bench_elem_indices = S.elemIndices 0xa
{-# INLINE bench_elem_indices #-}

bench_find_index_first :: S.ByteString -> Maybe Int
bench_find_index_first = S.findIndex (== 0xa)
{-# INLINE bench_find_index_first #-}

bench_elem_index_first :: S.ByteString -> Maybe Int
bench_elem_index_first = S.elemIndex 0xa
{-# INLINE bench_elem_index_first #-}

bench_find_indices_first :: S.ByteString -> Maybe Int
bench_find_indices_first = safeHead . bench_find_indices
{-# INLINE bench_find_indices_first #-}

bench_elem_indices_first :: S.ByteString -> Maybe Int
bench_elem_indices_first = safeHead . bench_elem_indices
{-# INLINE bench_elem_indices_first #-}



bench_find_index_second :: S.ByteString -> Maybe Int
bench_find_index_second bs =
let isNl = (== 0xa)
in case S.findIndex isNl bs of
Just !i -> S.findIndex isNl (S.unsafeDrop (i+1) bs)
Nothing -> Nothing
{-# INLINE bench_find_index_second #-}

bench_elem_index_second :: S.ByteString -> Maybe Int
bench_elem_index_second bs =
let nl = 0xa
in case S.elemIndex nl bs of
Just !i -> S.elemIndex nl (S.unsafeDrop (i+1) bs)
Nothing -> Nothing
{-# INLINE bench_elem_index_second #-}

bench_find_indices_second :: S.ByteString -> Maybe Int
bench_find_indices_second = safeHead . tail . bench_find_indices
{-# INLINE bench_find_indices_second #-}

bench_elem_indices_second :: S.ByteString -> Maybe Int
bench_elem_indices_second = safeHead . tail . bench_elem_indices
{-# INLINE bench_elem_indices_second #-}

nilEq :: Word8 -> Word8 -> Bool
{-# NOINLINE nilEq #-}
nilEq = eqWord8

bench_find_indices_inline :: S.ByteString -> [Int]
bench_find_indices_inline = S.findIndices (== 0xa)
{-# INLINE bench_find_indices_inline #-}

bench_find_index_inline :: S.ByteString -> Maybe Int
bench_find_index_inline = S.findIndex (== 0xa)
{-# INLINE bench_find_index_inline #-}

bench_find_indices_noinline :: S.ByteString -> [Int]
bench_find_indices_noinline = S.findIndices (nilEq 0xa)
{-# INLINE bench_find_indices_noinline #-}

bench_find_index_noinline :: S.ByteString -> Maybe Int
bench_find_index_noinline = S.findIndex (nilEq 0xa)
{-# INLINE bench_find_index_noinline #-}
19 changes: 19 additions & 0 deletions bench/bench-bytestring.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,22 @@ benchmark bench-builder-csv
-fdicts-cheap
-fspec-constr-count=6
default-language: Haskell2010

benchmark bench-strict-indices
hs-source-dirs: .. .
main-is: BenchIndices.hs
other-modules: Data.ByteString
Data.ByteString.Internal
Data.ByteString.Unsafe
type: exitcode-stdio-1.0
build-depends: base >= 4.4 && < 5
, ghc-prim
, deepseq >= 1.2
, gauge >= 0.2.5
-- we require bytestring due to benchmarking against
-- blaze-textual, which uses blaze-builder
ghc-options: -O2
-fmax-simplifier-iterations=10
-fdicts-cheap
-fspec-constr-count=6
default-language: Haskell2010

0 comments on commit 6957f6d

Please sign in to comment.