Подтвердить что ты не робот

Эффективное создание строгих байтов

Недавно после запуска тестов в моем проекте я обнаружил, что прямое построение строгих байтов может быть на порядок быстрее, чем у строителя.

Например, реализация кодировщика, которая использует построитель:

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)

Итак, мой вопрос в два раза:

  • Почему нет прямого преобразования из Builder в строгий ByteString? Это раздражает, потому что мне часто приходится импортировать Data.ByteString.Lazy только для использования его функции toStrict, потому что Data.ByteString.Builder предоставляет только toLazyByteString.

  • Указанный опыт, однако, заставило меня задуматься, если он не существует по какой-то причине. С той лишь разницей, что я применяю неверную схему использования в целом. Итак, действительно ли это неверно и есть лучшая альтернатива? Кстати, я знаю о Data.ByteString.Builder.Prim, но я сомневаюсь, что использование этого в случае, как указано выше, будет иметь большое значение.

4b9b3361

Ответ 1

Builder - это не абстракция нулевой стоимости, она оптимизирована для больших ленивых строк. От строителя docs:

Текущая реализация настроена для среднего размера блока между 4kb и 32kb

В вашем случае строитель выделяет целую часть 4k только для создания 8 байтов.

Сравните с pack, который вычисляет необходимый размер буфера, выделяет его и затем заполняет его в цикле. Единственный источник неэффективности - это список из 8 Word8, выделенный upfront. Вероятно, unfoldrN будет еще более эффективным.

Использование builder для построения небольших строгих байтов иногда бывает удобным, но есть лучшие способы.

Ответ 2

Попробуйте использовать toLazyByteStringWith из Data.ByteString.Builder.Extra, чтобы настроить конструкцию ByteString. Для этого требуется AllocationStrategy, который позволяет настраивать размер буфера и скорость роста.