有效地创建严格的ByteStrings

时间:2015-10-18 08:00:20

标签: haskell

最近,在对我的项目运行基准测试后,我发现严格字节串的直接构造比构建器的构建速度快一个数量级。

,例如,编码器实现,它使用构建器:

encoder :: Int64 -> Data.ByteString.ByteString
encoder =
  Data.ByteString.Lazy.toStrict .
  Data.ByteString.Builder.toLazyByteString .
  Data.ByteString.Builder.int64BE

执行的情况比直接构造字节串的情况差10倍,并且有进一步优化的几种可能性:

encoder :: Int64 -> Data.ByteString.ByteString
encoder =
  unpackIntBySize 8

unpackIntBySize :: (Bits a, Integral a) => Int -> a -> Data.ByteString.ByteString
unpackIntBySize n x =
  Data.ByteString.pack $ map f $ reverse [0..n - 1]
  where
    f s =
      fromIntegral $ shiftR x (8 * s)

所以我的问题是双重的:

  1. 为什么没有从Builder到严格ByteString的直接转换?这很烦人,因为我经常需要导入Data.ByteString.Lazy才能使用其toStrict功能,因为Data.ByteString.Builder只会公开toLazyByteString

  2. 然而,所提到的经历让我感到奇怪,如果它不是有原因的话。原因是我完全使用了不正确的使用模式。那么,它确实是不正确的,还有更好的选择吗?顺便说一句,我知道Data.ByteString.Builder.Prim,但我怀疑在上述案例中使用它会产生很大的不同。

2 个答案:

答案 0 :(得分:7)

Builder不是零成本抽象,它针对大型惰性字符串进行了优化。来自构建器docs

  

当前的实现调整为4kb到32kb之间的平均块大小

在您的情况下,构建器仅分配整个4k块以产生8个字节。

与计算必要缓冲区大小的pack进行比较,分配它然后在循环中填充它。低效率的唯一来源是预先分配的8 Word8列表。可能unfoldrN会更有效率。

使用构建器来构造小的严格字节串有时很方便,但有更好的方法。

答案 1 :(得分:3)

尝试使用toLazyByteStringWith中的Data.ByteString.Builder.Extra来调整您的ByteString构造。这需要AllocationStrategy,可以调整缓冲区大小和增长率。