2015-10-18 18 views
12

Niedawno po uruchomieniu testów porównawczych w moim projekcie odkryłem, że bezpośrednia konstrukcja dokładnych próbek może być o rząd wielkości większa niż ta, w której uczestniczy konstruktor.Skuteczne tworzenie surowych ByteStrings

Np implementacja kodera, który wykorzystuje producentów:

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

zachowuje się jak 10 razy gorzej niż jednego, który konstruuje bytestring bezpośrednio i ma kilka możliwości dalszej optymalizacji:

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) 

Więc moje pytanie jest dwojaki:

  1. Wh y nie ma bezpośredniej konwersji z Builder do ścisłej ByteString? To denerwujące, ponieważ często muszę importować Data.ByteString.Lazy tylko po to, aby użyć jego funkcji , ponieważ Data.ByteString.Builder ujawnia tylko toLazyByteString.

  2. Wspomniane doświadczenie sprawiło jednak, że zastanawiam się, czy nie jest to z jakiegoś powodu. Z tego powodu stosuję niepoprawny wzorzec użytkowania. Czy rzeczywiście jest to nieprawidłowe i czy istnieje lepsza alternatywa? BTW, wiem o Data.ByteString.Builder.Prim, ale wątpię, że użycie go w przypadku jak powyżej, miałoby duże znaczenie.

+0

Interesujące. Do mojej codziennej pracy zakładam, że sposób budowania -> leniwy Bajtak -> ścisłe Bytestring opłaca się tylko przy budowaniu dużego ciągu z dużej liczby krótkich. Zwykle pakuję. pokaż do konwersji z liczb do ścisłych bs ... nie wiem, czy to dobrze. Czy mógłbyś opublikować kod, który może być użyty do mierzenia wydajności? To wygląda na ciekawy problem. – dsign

+0

Istnieją dwie gałęzie projektu "postgresql-binary", które implementują enkodery za pomocą dwóch wspomnianych różnych strategii. Oba pochodzą z benchmarków wydajności kodowania. Oto [drzewo, w którym enkodery są zaimplementowane za pomocą bezpośredniej konstrukcji 'ByteString'] (https://github.com/nikita-volkov/postgresql-binary/tree/911a32110cfd618e2f7d377f4acc4c8f593f9acc), tutaj jest [ten obracający się wokół' Builder'] (https://github.com/nikita-volkov/postgresql-binary/tree/2fb6954968763621cbbdb8ba8505434ec2961b9e). –

+1

Myślę, że problem polega na tym, że "Builder" nie utrzymuje liczby bajtów wymaganych do zapisania wynikowego testu, nawet jeśli nie robisz leniwego streamingu, jest to statycznie znane (O (1)) lub O (n) - ale prawdopodobnie warto. Możesz sprawdzić "builder-bufora" i sprawdzić, czy robi to, czego potrzebujesz. Zobacz dyskusję tutaj: https://github.com/chadaustin/buffer-builder/issues/7 – jberryman

Odpowiedz

7

Builder nie jest abstrakcją o zerowym koszcie, jest zoptymalizowany pod kątem dużych leniwych ciągów. Z konstruktora docs:

Obecna implementacja jest dostrojony do średniej wielkość porcji pomiędzy 4kB i 32KB

W twoim przypadku, budowniczy przydziela cały 4k klocek tylko produkować 8 bajtów.

Porównaj z pack, który oblicza wymagany rozmiar bufora, przydziela go, a następnie wypełnia go w pętli. Jedynym źródłem nieefektywności jest lista przydzielonych z góry 8 Word8. Prawdopodobnie unfoldrN będzie jeszcze bardziej efektywny.

Używanie konstruktora do konstruowania małych, ścisłych bytów jest czasem wygodne, ale istnieją lepsze sposoby.

+0

Dzięki! Wspominasz o "lepszych drogach". Co sugerujesz? Czy są jakieś sugerowane biblioteki? –

+0

@NikitaVolkov Miałem na myśli lepszą wydajność ad-hoc, jak 'unfoldrN'. Nie znam żadnej konkretnej biblioteki, która ułatwia życie i zwykle używam testowania konstruktora, gdy wydajność nie jest krytyczna. – Yuras

+0

Co ze złączeniem małych, ścisłych próbek? Czy najskuteczniej jest używać 'append' i/lub' concat'? –

3

Spróbuj użyć toLazyByteStringWith z Data.ByteString.Builder.Extra, aby dostroić swoją konstrukcję ByteString. Zajmuje to AllocationStrategy, który pozwala dostroić rozmiar bufora i tempo wzrostu.

Powiązane problemy