Эффективная ленивая генерация ByteString на примере случайной ByteString

Кажется, есть некоторые препятствия для эффективной ленивых ByteString поколение рекурсией. Чтобы продемонстрировать это, выбранная задача состоит в том, чтобы сделать ленивую случайную ByteString, (Генерация случайных чисел - просто разумная операция, т. Е. Заполнитель для любой другой рекурсии, которая может представлять интерес.)

Вот две попытки создать случайный ленивый ByteString фиксированной длины n, Они выделяют огромное количество кучи. Сначала немного импорта:

import qualified Data.ByteString.Lazy as BSL
import Data.Word8
import System.Random

Теперь функция, которая использует cons:

lazyRandomByteString1 :: Int -> StdGen -> BSL.ByteString
lazyRandomByteString1 n g = fst3 $ iter (BSL.empty, n, g) where
    fst3 (a, _, _) = a
    iter (bs', n', g') =
        if n' == 0 then (bs', 0, g')
        else iter (w `BSL.cons` bs', n'-1, g'') where
            (w, g'') = random g' :: (Word8, StdGen)

То же самое, просто используя unfoldr короче, но почти так же плохо, как и выше:

lazyRandomByteString2 :: Int -> StdGen -> BSL.ByteString
lazyRandomByteString2 n g = BSL.unfoldr f (n, g) where
    f :: (Int, StdGen) -> (Int, StdGen)
    f (n', g') =
        if n' == 0 then Nothing
        else Just (w, (n'-1, g'')) where
            (w, g'') = random g' :: (Word8, StdGen)

В пределах того, что предусмотрено Data.ByteString.Lazy это все доступные варианты создания ByteStrings рекурсией.

Затем перейдите к Data.ByteString.Lazy.Builder был построен, чтобы строить ленивый ByteStrings Конечно, это должно быть более эффективным:

import Data.ByteString.Lazy.Builder (Builder, toLazyByteString, word8)

lazyRandomByteString3 :: Int -> StdGen -> BSL.ByteString
lazyRandomByteString3 n g = toLazyByteString builder where
    builder :: Builder
    builder = fst3 $ iter (mempty, n, g) where
        fst3 (a, _, _) = a
        iter :: (Builder, Int, StdGen) -> (Builder, Int, StdGen)
        iter (b, n', g') =
            if n' == 0 then (b, 0, g')
            else iter (b <> (word8 w), n'-1, g'') where
                (w, g'') = random g' :: (Word8, StdGen)

Но это не так.

Builder действительно должно быть в состоянии сделать это эффективно, не так ли? Что не так с lazyRandomByteString3?

Исходный код находится на github.

0 ответов

Другие вопросы по тегам