Haskell优化函数寻找字节串终止符

时间:2010-03-20 11:38:43

标签: performance optimization haskell bytestring

一些代码的分析表明,大约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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

如果问题是“反向”,您可以使用“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