on master help sign in
Write a new version of replace that is 2.2x faster
commit3cf85641cc1dad474a624b7e735a5a3fd81f781d authorBryan O'Sullivan <bos@serpentine.com> dateTue 3 Dec 2013, 22:21 (13 years ago) parent99848f7
3 files changed+33−1
Data/Text.hs+26 benchmarks/haskell/Benchmarks/Replace.hs+1 tests/Tests/Properties.hs+6−1
Data/Text.hs+26
@@ -73,6 +73,7 @@ module Data.Text
7373 , transpose
7474 , reverse
7575 , replace
76+ , replace2
7677
7778 -- ** Case conversion
7879 -- $case
@@ -623,6 +624,31 @@ replace :: Text -- ^ Text to search for
623624replace s d = intercalate d . splitOn s
624625{-# INLINE replace #-}
625626
627+replace2 :: Text -> Text -> Text -> Text
628+replace2 needle@(Text _ _ neeLen)
629+ r@(Text repArr repOff repLen)
630+ haystack@(Text hayArr hayOff hayLen)
631+ | neeLen == 0 = emptyError "replace2"
632+ | len < 0 = overflowError "replace2"
633+ | len == 0 = empty
634+ | L.null ixs = haystack
635+ | otherwise = Text (A.run x) 0 len
636+ where
637+ ixs = indices needle haystack
638+ cnt = L.length ixs
639+ len = hayLen - (neeLen - repLen) * cnt
640+ x = do
641+ marr <- A.new len
642+ let loop is0@(i:is) o d = do
643+ let d0 = d + i - o
644+ d1 = d0 + repLen
645+ A.copyI marr d hayArr (hayOff+o) d0
646+ A.copyI marr d0 repArr repOff d1
647+ loop is (i + neeLen) d1
648+ loop [] o d = A.copyI marr d hayArr (hayOff+o) len
649+ loop ixs 0 0
650+ return marr
651+
626652-- ----------------------------------------------------------------------------
627653-- ** Case conversions (folds)
628654
benchmarks/haskell/Benchmarks/Replace.hs+1
@@ -28,6 +28,7 @@ benchmark fp pat sub = do
2828 !b = T.encodeUtf8 t
2929 return $ bgroup "Replace" [
3030 bench "Text" $ nf (T.length . T.replace tpat tsub) t
31+ , bench "Text2" $ nf (T.length . T.replace2 tpat tsub) t
3132 , bench "ByteString" $ nf (BL.length . B.replace bpat bsub) b
3233 , bench "LazyText" $ nf (TL.length . TL.replace tlpat tlsub) tl
3334 , bench "LazyByteString" $ nf (BL.length . BL.replace blpat blsub) bl
tests/Tests/Properties.hs+6−1
@@ -234,12 +234,16 @@ t_reverse_short n = L.reverse `eqP` (unpackS . S.reverse . shorten n . S.stream)
234234
235235t_replace s d = (L.intercalate d . splitOn s) `eqP`
236236 (unpackS . T.replace (T.pack s) (T.pack d))
237+t_replace2 (NotEmpty s0) (NotEmpty d0) (NotEmpty w0) =
238+ T.replace s d w =^= T.replace2 s d w
239+ where s = f s0; d = f d0; w = f w0
240+ f = T.tail . T.pack
237241tl_replace s d = (L.intercalate d . splitOn s) `eqP`
238242 (unpackS . TL.replace (TL.pack s) (TL.pack d))
239243
240244splitOn :: (Eq a) => [a] -> [a] -> [[a]]
241245splitOn pat src0
242− | l == 0 = error "empty"
246+ | l == 0 = error "splitOn: empty"
243247 | otherwise = go src0
244248 where
245249 l = length pat
@@ -896,6 +900,7 @@ tests =
896900 testProperty "tl_reverse" tl_reverse,
897901 testProperty "t_reverse_short" t_reverse_short,
898902 testProperty "t_replace" t_replace,
903+ testProperty "t_replace2" t_replace2,
899904 testProperty "tl_replace" tl_replace,
900905
901906 testGroup "case conversion" [