on master help sign in
Optimize unstream loop

Separate the infrequent code from bulk processing loop so that the common case loop is smaller and more likely to use processor pipelining more efficiently. With this change the ddump-simpl output of the unsream loop was reduced by around 10 times and the performance of just stream followed by unstream got improved by 3 times. The relevant macro benchmark results on my machine are as follows: Benchmark Before After Change ------------------------------------------------------- Pure/intersperse/Text+ascii 1.552 1.025 (ms) 34% Pure/cons/Text+ascii 830.0 564.7 (us) 32% Pure/map/Text+ascii 824.2 604.5 (us) 27% Pure/filter/Text+ascii 342.5 320.2 (us) 6.5% Pure/toLower/Text+ascii 3.447 3.253 (ms) 6% Pure/zipWith/Text+ascii 1.252 1.207 (ms) 3.6% Pure/replicate char/Text+ascii 697.7 682.4 (us) 2%

commit09843692c764a3628c7161e586321ed60adfe3c7 authorHarendra Kumar <harendra.kumar@gmail.com> dateFri 6 May 2016, 19:05 (10 years ago) parentc9c3079
2 files changed+30−20
Data/Text/Internal/Fusion.hs+27−17 Data/Text/Internal/Fusion/Common.hs+3−3
Data/Text/Internal/Fusion.hs+27−17
@@ -98,25 +98,35 @@ reverseStream (Text arr off len) = Stream next (off+len-1) (betweenSize (len `sh
9898-- | /O(n)/ Convert a 'Stream Char' into a 'Text'.
9999unstream :: Stream Char -> Text
100100unstream (Stream next0 s0 len) = runText $ \done -> do
101− let mlen = upperBound 4 len
101+ -- Before encoding each char we perform a buffer realloc check assuming
102+ -- worst case encoding size of two 16-bit units for the char. Just add an
103+ -- extra space to the buffer so that we do not end up reallocating even when
104+ -- all the chars are encoded as single unit.
105+ let mlen = upperBound 4 len + 1
102106 arr0 <- A.new mlen
103− let outer arr top = loop
107+ let outer !arr !maxi = encode
104108 where
105− loop !s !i =
106− case next0 s of
107− Done -> done arr i
108− Skip s' -> loop s' i
109− Yield x s'
110− | j >= top -> {-# SCC "unstream/resize" #-} do
111− let top' = (top + 1) `shiftL` 1
112− arr' <- A.new top'
113− A.copyM arr' 0 arr 0 top
114− outer arr' top' s i
115− | otherwise -> do d <- unsafeWrite arr i x
116− loop s' (i+d)
117− where j | ord x < 0x10000 = i
118− | otherwise = i + 1
119− outer arr0 mlen s0 0
109+ -- keep the common case loop as small as possible
110+ encode !si !di =
111+ case next0 si of
112+ Done -> done arr di
113+ Skip si' -> encode si' di
114+ Yield c si'
115+ -- simply check for the worst case
116+ | maxi < di + 1 -> realloc si di
117+ | otherwise -> do
118+ n <- unsafeWrite arr di c
119+ encode si' (di + n)
120+
121+ -- keep uncommon case separate from the common case code
122+ {-# NOINLINE realloc #-}
123+ realloc !si !di = do
124+ let newlen = (maxi + 1) * 2
125+ arr' <- A.new newlen
126+ A.copyM arr' 0 arr 0 di
127+ outer arr' (newlen - 1) si di
128+
129+ outer arr0 (mlen - 1) s0 0
120130{-# INLINE [0] unstream #-}
121131{-# RULES "STREAM stream/unstream fusion" forall s. stream (unstream s) = s #-}
122132
Data/Text/Internal/Fusion/Common.hs+3−3
@@ -669,12 +669,12 @@ scanl f z0 (Stream next0 s0 len) = Stream next (Scan1 z0 s0) (len+1) -- HINT may
669669-- ** Generating and unfolding streams
670670
671671replicateCharI :: Integral a => a -> Char -> Stream Char
672−replicateCharI n c
672+replicateCharI !n !c
673673 | n < 0 = empty
674674 | otherwise = Stream next 0 (fromIntegral n) -- HINT maybe too low
675675 where
676− next i | i >= n = Done
677− | otherwise = Yield c (i + 1)
676+ next !i | i >= n = Done
677+ | otherwise = Yield c (i + 1)
678678{-# INLINE [0] replicateCharI #-}
679679
680680data RI s = RI !s {-# UNPACK #-} !Int64