Skip to content

Commit

Permalink
Use GHC.Exts.oneShot in parseFoldr
Browse files Browse the repository at this point in the history
It can improve performance depending on the foldr implementation.
The `foldrTree` benchmark improves from 8.2ms to 3.5ms (-56%).
  • Loading branch information
meooow25 committed Nov 7, 2024
1 parent 1479a84 commit 591244a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
57 changes: 57 additions & 0 deletions bench/Bench.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
module Bench (benches) where

import Control.Applicative
import Control.DeepSeq (NFData(..))
import Control.Monad
import Control.Monad.Random.Strict
import qualified Data.Foldable as F
Expand All @@ -18,6 +19,7 @@ import qualified Regex.Base as RB
benches :: Benchmark
benches = bgroup "parser-regex"
[ textBenches
, intTreeBenches
]

---------
Expand Down Expand Up @@ -176,6 +178,61 @@ bigExpr :: Text
bigExpr = T.intercalate "+" $ map (T.pack . show) . evalR $
replicateM 100000 (getRandom :: R Word)

------------
-- IntTree
------------

-- These benchmarks are primarily to test that parsing works well with
-- structures that are not [] or Text, such as trees. The performance depends on
-- the foldr implementation, so we test two possible implementations here.

intTreeBenches :: Benchmark
intTreeBenches = bgroup "IntTree"
[ env (pure bigTree) $ \data_ ->
bgroup "many anySingle"
[ bench "foldrTree" $ whnf (treeParse manyAnyP) data_
, bench "foldrTreeStack" $ whnf (treeParseStack manyAnyP) data_
]
]
where
!manyAnyP = RB.compile $ RB.foldlMany' (\_ _ -> ()) () RB.anySingle

data IntTree = Bin !IntTree !Int !IntTree | Tip

instance NFData IntTree where
rnf !_ = ()

foldrTree :: (Int -> b -> b) -> b -> IntTree -> b
foldrTree f z0 t = go t z0
where
go Tip z = z
go (Bin l x r) z = go l (f x (go r z))

treeParse :: RB.Parser Int a -> IntTree -> Maybe a
treeParse = RB.parseFoldr foldrTree

data Stack = Push !Int !IntTree !Stack | Nada

foldrTreeStack :: (Int -> b -> b) -> b -> IntTree -> b
foldrTreeStack f z0 t = go (down t Nada)
where
go Nada = z0
go (Push x r stk) = f x (go (down r stk))
down Tip stk = stk
down (Bin l x r) stk = down l (Push x r stk)

treeParseStack :: RB.Parser Int a -> IntTree -> Maybe a
treeParseStack = RB.parseFoldr foldrTreeStack

bigTree :: IntTree
bigTree = go 100000 0
where
go 0 _ = Tip
go n i = Bin (go ln i) (i+ln) (go rn (i+ln+1))
where
ln = (n-1) `div` 2
rn = n-1-ln

-----------
-- Random
-----------
Expand Down
3 changes: 2 additions & 1 deletion src/Regex/Internal/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Control.Monad.Fix
import Data.Maybe (isJust)
import Data.Primitive.SmallArray
import qualified Data.Foldable as F
import qualified GHC.Exts as X

import Regex.Internal.Regex (RE(..), Strictness(..), Greediness(..))
import Regex.Internal.Unique (Unique(..), UniqueSet)
Expand Down Expand Up @@ -368,7 +369,7 @@ type Foldr f a = forall b. (a -> b -> b) -> b -> f -> b
parseFoldr :: Foldr f c -> Parser c a -> f -> Maybe a
parseFoldr fr = \p xs -> fr f finishParser xs (prepareParser p)
where
f c k = \ps -> stepParser ps c >>= k
f c k = X.oneShot (\ps -> stepParser ps c >>= k)
{-# INLINE parseFoldr #-}

---------
Expand Down

0 comments on commit 591244a

Please sign in to comment.