Skip to content

Commit

Permalink
Speed up Data.ByteString.Short.unpack
Browse files Browse the repository at this point in the history
  • Loading branch information
hasufell committed Jul 3, 2022
1 parent 2b9416d commit 486be40
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 46 deletions.
50 changes: 4 additions & 46 deletions Data/ByteString/Short/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -573,55 +573,13 @@ packLenBytes len ws0 =
-- unpackAppendChars do the chunks strictly.

unpackChars :: ShortByteString -> [Char]
unpackChars sbs = unpackAppendCharsLazy sbs []
unpackChars sbs = let ix = length sbs - 1
in List.map (indexCharArray (asBA sbs)) [0..ix]

unpackBytes :: ShortByteString -> [Word8]
unpackBytes sbs = unpackAppendBytesLazy sbs []
unpackBytes sbs = let ix = length sbs - 1
in List.map (unsafeIndex sbs) [0..ix]

-- Why 100 bytes you ask? Because on a 64bit machine the list we allocate
-- takes just shy of 4k which seems like a reasonable amount.
-- (5 words per list element, 8 bytes per word, 100 elements = 4000 bytes)

unpackAppendCharsLazy :: ShortByteString -> [Char] -> [Char]
unpackAppendCharsLazy sbs = go 0 (length sbs)
where
sz = 100

go off len cs
| len <= sz = unpackAppendCharsStrict sbs off len cs
| otherwise = unpackAppendCharsStrict sbs off sz remainder
where remainder = go (off+sz) (len-sz) cs

unpackAppendBytesLazy :: ShortByteString -> [Word8] -> [Word8]
unpackAppendBytesLazy sbs = go 0 (length sbs)
where
sz = 100

go off len ws
| len <= sz = unpackAppendBytesStrict sbs off len ws
| otherwise = unpackAppendBytesStrict sbs off sz remainder
where remainder = go (off+sz) (len-sz) ws

-- For these unpack functions, since we're unpacking the whole list strictly we
-- build up the result list in an accumulator. This means we have to build up
-- the list starting at the end. So our traversal starts at the end of the
-- buffer and loops down until we hit the sentinal:

unpackAppendCharsStrict :: ShortByteString -> Int -> Int -> [Char] -> [Char]
unpackAppendCharsStrict !sbs off len = go (off-1) (off-1 + len)
where
go !sentinal !i !acc
| i == sentinal = acc
| otherwise = let !c = indexCharArray (asBA sbs) i
in go sentinal (i-1) (c:acc)

unpackAppendBytesStrict :: ShortByteString -> Int -> Int -> [Word8] -> [Word8]
unpackAppendBytesStrict !sbs off len = go (off-1) (off-1 + len)
where
go !sentinal !i !acc
| i == sentinal = acc
| otherwise = let !w = indexWord8Array (asBA sbs) i
in go sentinal (i-1) (w:acc)


------------------------------------------------------------------------
Expand Down
12 changes: 12 additions & 0 deletions bench/BenchShort.hs
Original file line number Diff line number Diff line change
Expand Up @@ -231,5 +231,17 @@ benchShort = bgroup "ShortByteString"
, bench "FindIndex/inlined" $ nf (S.findIndex (== nl)) absurdlong
, bench "FindIndex/non-inlined" $ nf (S.findIndex (nilEq nl)) absurdlong
]
, bgroup "ShortByteString unpack" $
[ bench "unpack and look at first 100 elements" $ nf (unpackX) absurdlong
, bench "unpackLast" $ nf (unpackLast) absurdlong
]
]


unpackX :: ShortByteString -> Bool
unpackX sbs = case S.unpack sbs of
(_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_:_) -> True
_ -> False

unpackLast :: ShortByteString -> Word8
unpackLast sbs = Prelude.last $ S.unpack sbs

0 comments on commit 486be40

Please sign in to comment.