字节串到 [Word16] 的高效转换
Efficient Conversion of Bytestring to [Word16]
我正在尝试进行从字节串到 Word16 列表的简单转换。下面使用 Data.Binary.Get 的实现有效,尽管它是代码中的性能瓶颈。这是可以理解的,因为 IO 总是很慢,但我想知道是否有更有效的方法来做到这一点。
getImageData' = do
e <- isEmpty
if e then return []
else do
w <- getWord16be
ws <- getImageData'
return $ w : ws
我怀疑您在使用 Data.Binary.Get
时遇到的最大问题是解码器本身对您的目的来说太严格了。 serialise
似乎也是这种情况,其他序列化库也可能如此。我认为根本的问题在于,虽然 you 知道只要 ByteString
的字节数为偶数,操作就会成功,但库不知道这一点。因此,它必须先阅读整个 ByteString
,然后才能得出 "Ah yes, all is well" 的结论并构建您所请求的列表。事实上,你构建结果的方式,在实际做任何有用的事情之前,它会构建一系列闭包(数量与长度成正比)。
如何解决这个问题?直接使用 bytestring
库即可。最简单的方法是使用 unpack
,但我认为您可能会像这样获得更好的性能:
{-# language BangPatterns #-}
module Wordy where
import qualified Data.ByteString as BS
import Data.ByteString (ByteString)
import Data.Word (Word16)
import Data.Maybe (fromMaybe)
import Data.Bits (unsafeShiftL)
toDBs :: ByteString -> Maybe [Word16]
toDBs bs0
| odd (BS.length bs0) = Nothing
| otherwise = Just (go bs0)
where
go bs = fromMaybe [] $ do
(b1, bs') <- BS.uncons bs
(b2, bs'') <- BS.uncons bs'
let !res = (fromIntegral b1 `unsafeShiftL` 8) + fromIntegral b2
Just (res : go bs'')
我正在尝试进行从字节串到 Word16 列表的简单转换。下面使用 Data.Binary.Get 的实现有效,尽管它是代码中的性能瓶颈。这是可以理解的,因为 IO 总是很慢,但我想知道是否有更有效的方法来做到这一点。
getImageData' = do
e <- isEmpty
if e then return []
else do
w <- getWord16be
ws <- getImageData'
return $ w : ws
我怀疑您在使用 Data.Binary.Get
时遇到的最大问题是解码器本身对您的目的来说太严格了。 serialise
似乎也是这种情况,其他序列化库也可能如此。我认为根本的问题在于,虽然 you 知道只要 ByteString
的字节数为偶数,操作就会成功,但库不知道这一点。因此,它必须先阅读整个 ByteString
,然后才能得出 "Ah yes, all is well" 的结论并构建您所请求的列表。事实上,你构建结果的方式,在实际做任何有用的事情之前,它会构建一系列闭包(数量与长度成正比)。
如何解决这个问题?直接使用 bytestring
库即可。最简单的方法是使用 unpack
,但我认为您可能会像这样获得更好的性能:
{-# language BangPatterns #-}
module Wordy where
import qualified Data.ByteString as BS
import Data.ByteString (ByteString)
import Data.Word (Word16)
import Data.Maybe (fromMaybe)
import Data.Bits (unsafeShiftL)
toDBs :: ByteString -> Maybe [Word16]
toDBs bs0
| odd (BS.length bs0) = Nothing
| otherwise = Just (go bs0)
where
go bs = fromMaybe [] $ do
(b1, bs') <- BS.uncons bs
(b2, bs'') <- BS.uncons bs'
let !res = (fromIntegral b1 `unsafeShiftL` 8) + fromIntegral b2
Just (res : go bs'')