【问题标题】:Haskell optimization of a function looking for a bytestring terminator寻找字节串终止符的函数的 Haskell 优化
【发布时间】:2011-01-29 18:49:18
【问题描述】:

对某些代码的分析表明,大约 65% 的时间我都在以下代码中。

它的作用是使用 Data.Binary.Get monad 遍历一个字节串来寻找终止符。如果检测到 0xff,则检查下一个字节是否为 0x00。如果是,它会丢弃 0x00 并继续。如果它不是 0x00,那么它会丢弃两个字节,并将生成的字节列表转换为字节串并返回。

有什么明显的优化方法吗?我看不到。

parseECS = f [] False
    where
    f acc ff = do
        b <- getWord8
        if ff
            then if b == 0x00
                then f (0xff:acc) False
                else return $ L.pack (reverse acc)
            else if b == 0xff
                then f acc True
                else f (b:acc) False

【问题讨论】:

    标签: performance optimization haskell bytestring


    【解决方案1】:

    错误修复

    这里似乎有一个错误。如果您在找到 0xff 而不是 0x00 序列之前到达字节流的末尾,则会引发异常。这是您的函数的修改版本:

    parseECS :: Get L.ByteString
    parseECS = f [] False
      where
        f acc ff = do
          noMore <- isEmpty
          if noMore
             then return $ L.pack (reverse acc)
             else do
               b <- getWord8
               if ff
                  then
                    if b == 0x00
                       then f (0xff:acc) False
                       else return $ L.pack (reverse acc)
                  else
                    if b == 0xff
                       then f acc True
                       else f (b:acc) False
    

    优化

    我没有进行任何分析,但这个功能可能会更快。反转长列表是昂贵的。我不确定getRemainingLazyByteString 有多懒惰。如果太严格,这可能不适合你。

    parseECS2 :: Get L.ByteString
    parseECS2 = do
        wx <- liftM L.unpack $ getRemainingLazyByteString
        return . L.pack . go $ wx
      where
        go []             = []
        go (0xff:0x00:wx) = 0xff : go wx
        go (0xff:_)      = []
        go (w:wx)         = w : go wx
    

    【讨论】:

    • 这是有道理的,我同意这是一种妥协。我有了突破 Get monad 并直接搜索 ByteString 的想法,它给了我 4 倍的速度。您的代码基本上做同样的事情,但转换为列表并使用模式。回复你的表现。感谢您的帮助。
    【解决方案2】:

    如果问题出在“反向”中,您可以使用“lookAhead”扫描位置,然后返回并重建新字符串

    parseECS2 :: Get L.ByteString
    parseECS2 = do
        let nextWord8 = do
                noMore <- isEmpty
                if noMore then return Nothing
                          else liftM Just getWord8
    
        let scanChunk !n = do
                b <- nextWord8
                case b of
                    Just 0xff -> return (Right (n+1))
                    Just _ -> scanChunk (n+1)
                    Nothing -> return (Left n)
    
        let readChunks = do
                c <- lookAhead (scanChunk 0)
                case c of
                    Left n -> getLazyByteString n >>= \blk -> return [blk]
                    Right n -> do
                        blk <- getLazyByteString n
                        b <- lookAhead nextWord8
                        case b of
                            Just 0x00 -> skip 1 >> liftM (blk:) readChunks
                            _ -> return [L.init blk]
    
        liftM (foldr L.append L.empty) readChunks
    

    【讨论】:

    • 嗨,奥尼。谢谢。不过,问题并不是真正的前瞻部分。就是通过长二进制字符串getWord8很慢。
    猜你喜欢
    • 1970-01-01
    • 2011-02-08
    • 2020-06-13
    • 2023-03-11
    • 2021-10-15
    • 2012-10-15
    • 2014-02-16
    • 1970-01-01
    • 2014-04-07
    相关资源
    最近更新 更多