Commits

tibbe committed 5b6ffba

Added RULES to merge adjacent bounds checks.

This speeds up the "builder/mappend 8 char" benchmark by 39%. The
number of merged bounds checks is currently limited to
-fmax-simplifier-iterations. Replacing 'append' (and perhaps
'writeN') with some other combinator might fix that problem.

Comments (0)

Files changed (2)

Data/Text/Lazy/Builder.hs

 --
 append :: Builder -> Builder -> Builder
 append (Builder f) (Builder g) = Builder (f . g)
-{-# INLINE append #-}
+{-# INLINE [1] append #-}
 
 -- TODO: Experiment to find the right threshold.
 copyLimit :: Int
     if n <= l
     then empty
     else flush `append` withBuffer (const (newBuffer (max n defaultChunkSize)))
-{-# INLINE ensureFree #-}
+{-# INLINE [1] ensureFree #-}
 
 -- | Ensure that @n@ many elements are available, and then use @f@ to
 -- write some elements into the memory.
 writeN :: Int -> (forall s. A.MArray s Word16 -> Int -> ST s ()) -> Builder
 writeN n f = ensureFree 1 `append` withBuffer (writeNBuffer n f)
-{-# INLINE writeN #-}
+{-# INLINE [1] writeN #-}
 
 writeNBuffer :: Int -> (A.MArray s Word16 -> Int -> ST s ()) -> (Buffer s)
              -> ST s (Buffer s)
         | otherwise = do A.unsafeWrite dest j (A.unsafeIndex src i)
                          copy_loop (i+1) (j+1) (c+1)
 {-# INLINE unsafeCopy #-}
+
+------------------------------------------------------------------------
+-- Some nice rules for Builder
+
+{-# RULES
+
+"writeN/combine" forall a b (f::forall s. A.MArray s Word16 -> Int -> ST s ())
+                            (g::forall s. A.MArray s Word16 -> Int -> ST s ()).
+        append (writeN a f) (writeN b g) =
+        (writeN (a+b) (\marr o -> f marr o >> g marr (o+a)))
+
+"ensureFree/combine" forall a b .
+        append (ensureFree a) (ensureFree b) = ensureFree (max a b)
+
+"flush/combine"
+        append flush flush = flush
+
+ #-}

tests/Benchmarks.hs

       ],
       bgroup "builder" [
         bench "mappend char" $ nf (TL.length . TB.toLazyText . mappendNChar 'a') 10000,
+        bench "mappend 8 char" $ nf (TL.length . TB.toLazyText . mappend8Char) 'a',
         bench "mappend text" $ nf (TL.length . TB.toLazyText . mappendNText short) 10000
       ]
     ]
       | i < n     = go (i+1) (acc `mappend` TB.singleton c)
       | otherwise = acc
 
+-- Gives more opportunity for inlining and elimination of unnecesary
+-- bounds checks.
+mappend8Char :: Char -> TB.Builder
+mappend8Char c = TB.singleton c `mappend` TB.singleton c `mappend`
+                 TB.singleton c `mappend` TB.singleton c `mappend`
+                 TB.singleton c `mappend` TB.singleton c `mappend`
+                 TB.singleton c `mappend` TB.singleton c
+
 mappendNText :: TS.Text -> Int -> TB.Builder
 mappendNText t n = go 0 mempty
   where