如何提高BitGet性能

时间:2014-03-23 06:34:41

标签: haskell

我现在正在使用Haskell开发二进制解析程序。 我目前发现严格/懒惰的BitGet似乎都非常慢 令人惊讶地分配了大量的内存。

我测试了下面的代码(使用-O2构建),例如解析输入文件中的整个位,以及 找出分析结果。 在本例中,我使用了1,819,173字节的二进制文件。

严格版本:

import Prelude                   as P
import System.Environment        (getArgs)
import Data.ByteString           as B
import Data.Binary.Strict.BitGet

coreFunc :: Int -> BitGet Int
coreFunc len = f len 0
    where
    f 0 r = return r
    f l _ = do
        b <- getBit
        f (l - 1) $ if b then 1 else 0

mainFunc :: B.ByteString -> IO ()
mainFunc bs =
    case runBitGet bs (coreFunc ((B.length bs) * 8)) of
        Left emsg -> error emsg
        Right r  -> print $ show r

main :: IO ()
main = do
    args <- getArgs
    case args of
        []    -> return ()
        (x:_) -> (do
            bs <- B.readFile x
            mainFunc bs
            return ()
            )

-- profiling result --
total time  =        1.74 secs   (1741 ticks @ 1000 us, 1 processor)
total alloc = 7,948,043,192 bytes  (excludes profiling overheads)

懒人版:

import Prelude                   as P
import System.Environment        (getArgs)
import Data.ByteString.Lazy      as B
import Data.Binary.Bits.Get
import Data.Binary.Get
import Data.Int                  (Int64)

coreFunc :: Int64 -> BitGet Int
coreFunc len = f len 0
    where
    f 0 r = return r
    f l _ = do
        b <- getBool
        f (l - 1) $ if b then 1 else 0

mainFunc :: B.ByteString -> IO ()
mainFunc bs = do
    let r = runGet (runBitGet (coreFunc ((B.length bs) * 8))) bs
    print $ show r

main :: IO ()
main = do
    args <- getArgs
    case args of
        []    -> return ()
        (x:_) -> (do
            bs <- B.readFile x
            mainFunc bs
            return ()
            )

-- profiling result --
total time  =        2.21 secs   (2207 ticks @ 1000 us, 1 processor)
total alloc = 6,405,531,680 bytes  (excludes profiling overheads)

我想问一下:

  • 如何改善这种表现?
  • 我可以在BitGet库内部进行配置吗?
  • 是否有另一种解析二进制位的方法?

1 个答案:

答案 0 :(得分:2)

似乎您的coreFunc应该跳过一些(len - 1)位数,然后将一位读取为01并将其返回BitGet monad。如果这是意图,那么这样的事情会更有效率。

我正在使用binary-bits包:

import Control.Applicative
import Data.Binary.Get

coreFunc :: Int -> Get Int
coreFunc len =
    fromEnum <$> runBitGet (block (skip (len - 1) *> bool)

skip :: Int -> BitGet ()
skip n = byteString bytes *> word64be bits *> pure ()
    where (bytes, bits) = quotRem n 8 -- sizeOf Word8             

不幸的是,程序包没有skip函数让我们跳过n位,它所基于的binary包包含,所以我必须编写自己的。可以通过访问Block内部来编写更高效的版本,但是库可能已经很好地优化了它,没有任何好处。

我想对您的版本进行基准测试以获得准确的比较,您能提供用于测试的二进制文件吗?