Эффективное создание строгих байтов
Недавно после запуска тестов в моем проекте я обнаружил, что прямое построение строгих байтов может быть на порядок быстрее, чем у строителя.
Например, реализация кодировщика, которая использует построитель:
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
, но я сомневаюсь, что использование этого в случае, как указано выше, будет иметь большое значение.
Ответы
Ответ 1
Builder - это не абстракция нулевой стоимости, она оптимизирована для больших ленивых строк. От строителя docs:
Текущая реализация настроена для среднего размера блока между 4kb и 32kb
В вашем случае строитель выделяет целую часть 4k только для создания 8 байтов.
Сравните с pack
, который вычисляет необходимый размер буфера, выделяет его и затем заполняет его в цикле. Единственный источник неэффективности - это список из 8 Word8
, выделенный upfront. Вероятно, unfoldrN
будет еще более эффективным.
Использование builder для построения небольших строгих байтов иногда бывает удобным, но есть лучшие способы.
Ответ 2
Попробуйте использовать toLazyByteStringWith
из Data.ByteString.Builder.Extra
, чтобы настроить конструкцию ByteString
. Для этого требуется AllocationStrategy
, который позволяет настраивать размер буфера и скорость роста.