mason 0.2.2 → 0.2.3
raw patch · 5 files changed
+118/−20 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Mason.Builder.Internal: StreamingEnv :: !ByteString -> IO () -> !IORef (ForeignPtr Word8) -> StreamingEnv
- Mason.Builder.Internal: data StreamingEnv
- Mason.Builder.Internal: instance Mason.Builder.Internal.Buildable Mason.Builder.Internal.StreamingEnv
+ Mason.Builder: data StreamingBackend
+ Mason.Builder: intDecPadded :: Int -> Int -> Builder
+ Mason.Builder: type BufferedIOBackend = PutEnv
+ Mason.Builder: type LazyByteStringBackend = Channel
+ Mason.Builder: type StrictByteStringBackend = GrowingBuffer
+ Mason.Builder.Internal: StreamingBackend :: !ByteString -> IO () -> !IORef (ForeignPtr Word8) -> StreamingBackend
+ Mason.Builder.Internal: data StreamingBackend
+ Mason.Builder.Internal: instance Mason.Builder.Internal.Buildable Mason.Builder.Internal.StreamingBackend
+ Mason.Builder.Internal: type BufferedIOBackend = PutEnv
+ Mason.Builder.Internal: type LazyByteStringBackend = Channel
+ Mason.Builder.Internal: type StrictByteStringBackend = GrowingBuffer
- Mason.Builder: hPutBuilderLen :: Handle -> BuilderFor PutEnv -> IO Int
+ Mason.Builder: hPutBuilderLen :: Handle -> BuilderFor BufferedIOBackend -> IO Int
- Mason.Builder: sendBuilder :: Socket -> BuilderFor PutEnv -> IO Int
+ Mason.Builder: sendBuilder :: Socket -> BuilderFor BufferedIOBackend -> IO Int
- Mason.Builder: toLazyByteString :: BuilderFor Channel -> ByteString
+ Mason.Builder: toLazyByteString :: BuilderFor LazyByteStringBackend -> ByteString
- Mason.Builder: toStreamingBody :: BuilderFor StreamingEnv -> (Builder -> IO ()) -> IO () -> IO ()
+ Mason.Builder: toStreamingBody :: BuilderFor StreamingBackend -> (Builder -> IO ()) -> IO () -> IO ()
- Mason.Builder: toStrictByteString :: BuilderFor GrowingBuffer -> ByteString
+ Mason.Builder: toStrictByteString :: BuilderFor StrictByteStringBackend -> ByteString
- Mason.Builder: withPopper :: BuilderFor Channel -> (IO ByteString -> IO a) -> IO a
+ Mason.Builder: withPopper :: BuilderFor LazyByteStringBackend -> (IO ByteString -> IO a) -> IO a
- Mason.Builder.Internal: [seBuffer] :: StreamingEnv -> !IORef (ForeignPtr Word8)
+ Mason.Builder.Internal: [seBuffer] :: StreamingBackend -> !IORef (ForeignPtr Word8)
- Mason.Builder.Internal: [sePush] :: StreamingEnv -> !ByteString -> IO ()
+ Mason.Builder.Internal: [sePush] :: StreamingBackend -> !ByteString -> IO ()
- Mason.Builder.Internal: hPutBuilderLen :: Handle -> BuilderFor PutEnv -> IO Int
+ Mason.Builder.Internal: hPutBuilderLen :: Handle -> BuilderFor BufferedIOBackend -> IO Int
- Mason.Builder.Internal: sendBuilder :: Socket -> BuilderFor PutEnv -> IO Int
+ Mason.Builder.Internal: sendBuilder :: Socket -> BuilderFor BufferedIOBackend -> IO Int
- Mason.Builder.Internal: toLazyByteString :: BuilderFor Channel -> ByteString
+ Mason.Builder.Internal: toLazyByteString :: BuilderFor LazyByteStringBackend -> ByteString
- Mason.Builder.Internal: toStreamingBody :: BuilderFor StreamingEnv -> (Builder -> IO ()) -> IO () -> IO ()
+ Mason.Builder.Internal: toStreamingBody :: BuilderFor StreamingBackend -> (Builder -> IO ()) -> IO () -> IO ()
- Mason.Builder.Internal: toStrictByteString :: BuilderFor GrowingBuffer -> ByteString
+ Mason.Builder.Internal: toStrictByteString :: BuilderFor StrictByteStringBackend -> ByteString
- Mason.Builder.Internal: withPopper :: BuilderFor Channel -> (IO ByteString -> IO a) -> IO a
+ Mason.Builder.Internal: withPopper :: BuilderFor LazyByteStringBackend -> (IO ByteString -> IO a) -> IO a
Files
- CHANGELOG.md +4/−1
- README.md +80/−2
- mason.cabal +1/−1
- src/Mason/Builder.hs +13/−5
- src/Mason/Builder/Internal.hs +20/−11
CHANGELOG.md view
@@ -1,4 +1,7 @@-# Revision history for mason+## 0.2.3++* Added `intDecPadded`+* Exported backend types: `StrictByteStringBackend`, `LazyByteStringBackend` and `BufferedIOBackend` ## 0.2.2
README.md view
@@ -15,11 +15,13 @@ * `toStrictByteString` produces a strict `ByteString` directly. * `hPutBuilderLen` writes a builder to a handle and returns the number of bytes. * `sendBuilder` sends the content of `Builder` over a socket.+* `withPopper` turns a builder into http-client's[GivesPopper](http://hackage.haskell.org/package/http-client-0.7.2.1/docs/Network-HTTP-Client.html#t:GivesPopper)+* `toStreamingBody` creates wai's [StreamingBody](http://hackage.haskell.org/package/wai-3.2.2.1/docs/Network-Wai.html#t:StreamingBody) Usage ---- -Replace `Data.ByteString.Builder` with `Mason.Builder`. Note that if you have `Builder` in the type signature, you'll need `RankNTypes` extensions because of the design explained below.+Replace `Data.ByteString.Builder` with `Mason.Builder`. Note that if you have `Builder` in the type signature, you'll need `RankNTypes` extensions because of the design explained below. Alternatively, you can also import `Mason.Builder.Compat` which has an API almost compatible with `Data.ByteString.Builder`. Performance ----@@ -63,7 +65,83 @@ bytestring/double mean 2.312 μs ( +- 118.8 ns ) ``` -TBD: more benchmarks+You can find more benchmarks below:++* [bytes-builder-shootout](https://github.com/andrewthad/bytes-builder-shootout)++<details>+<summary>Click to expand</summary>+<pre>+treeToHex-2000/small-bytearray-builder mean 44.01 μs ( +- 1.620 μs )+treeToHex-2000/fast-builder mean 34.40 μs ( +- 390.1 ns )+treeToHex-2000/bytestring mean 58.76 μs ( +- 3.843 μs )+treeToHex-2000/mason mean 41.08 μs ( +- 180.8 ns )+treeToHex-9000/small-bytearray-builder mean 191.8 μs ( +- 1.835 μs )+treeToHex-9000/bytestring mean 284.8 μs ( +- 1.156 μs )+treeToHex-9000/mason mean 181.2 μs ( +- 386.3 ns )+short-text-tree-1000/small-bytearray-builder mean 26.54 μs ( +- 34.08 ns )+short-text-tree-1000/fast-builder mean 37.51 μs ( +- 99.85 ns )+short-text-tree-1000/bytestring mean 37.95 μs ( +- 167.5 ns )+short-text-tree-1000/mason mean 26.87 μs ( +- 312.4 ns )+byte-tree-2000/small-bytearray-builder mean 30.53 μs ( +- 51.53 ns )+byte-tree-2000/fast-builder mean 26.91 μs ( +- 592.2 ns )+byte-tree-2000/bytestring mean 54.40 μs ( +- 1.743 μs )+byte-tree-2000/mason mean 34.34 μs ( +- 193.5 ns )+</pre>+</details>++* [haskell-perf/strict-bytestring-builder](https://github.com/haskell-perf/strict-bytestring-builders)++<details>+<summary>Click to expand</summary>+<pre>+averagedAppends-1/byteStringStrictBuilder mean 116.3 ns ( +- 6.479 ns )+averagedAppends-1/byteStringTreeBuilder mean 181.7 ns ( +- 20.88 ns )+averagedAppends-1/fastBuilder mean 181.5 ns ( +- 7.219 ns )+averagedAppends-1/bufferBuilder mean 728.5 ns ( +- 9.114 ns )+averagedAppends-1/byteString mean 358.7 ns ( +- 4.663 ns )+averagedAppends-1/blazeBuilder mean 356.0 ns ( +- 7.604 ns )+averagedAppends-1/binary mean 635.0 ns ( +- 7.936 ns )+averagedAppends-1/cereal mean 638.6 ns ( +- 12.40 ns )+averagedAppends-1/mason mean 155.2 ns ( +- 2.000 ns )+averagedAppends-100/byteStringStrictBuilder mean 7.290 μs ( +- 99.74 ns )+averagedAppends-100/byteStringTreeBuilder mean 13.40 μs ( +- 283.4 ns )+averagedAppends-100/fastBuilder mean 13.07 μs ( +- 418.2 ns )+averagedAppends-100/bufferBuilder mean 19.57 μs ( +- 5.644 μs )+averagedAppends-100/byteString mean 17.31 μs ( +- 1.609 μs )+averagedAppends-100/blazeBuilder mean 19.15 μs ( +- 6.533 μs )+averagedAppends-100/binary mean 48.26 μs ( +- 727.1 ns )+averagedAppends-100/cereal mean 51.57 μs ( +- 21.81 μs )+averagedAppends-100/mason mean 12.07 μs ( +- 233.8 ns )+averagedAppends-10000/byteStringStrictBuilder mean 1.038 ms ( +- 18.63 μs )+averagedAppends-10000/byteStringTreeBuilder mean 1.989 ms ( +- 70.63 μs )+averagedAppends-10000/fastBuilder mean 1.611 ms ( +- 42.24 μs )+averagedAppends-10000/bufferBuilder mean 1.895 ms ( +- 25.09 μs )+averagedAppends-10000/byteString mean 2.248 ms ( +- 40.99 μs )+averagedAppends-10000/blazeBuilder mean 2.394 ms ( +- 1.016 ms )+averagedAppends-10000/binary mean 6.503 ms ( +- 157.6 μs )+averagedAppends-10000/cereal mean 6.458 ms ( +- 221.6 μs )+averagedAppends-10000/mason mean 1.738 ms ( +- 25.89 μs )+regularConcat-100/byteStringStrictBuilder mean 1.606 μs ( +- 32.93 ns )+regularConcat-100/byteStringTreeBuilder mean 2.000 μs ( +- 43.73 ns )+regularConcat-100/fastBuilder mean 1.364 μs ( +- 37.95 ns )+regularConcat-100/bufferBuilder mean 2.204 μs ( +- 48.40 ns )+regularConcat-100/byteString mean 1.253 μs ( +- 25.68 ns )+regularConcat-100/blazeBuilder mean 1.317 μs ( +- 24.05 ns )+regularConcat-100/binary mean 2.845 μs ( +- 62.24 ns )+regularConcat-100/cereal mean 3.021 μs ( +- 48.53 ns )+regularConcat-100/mason mean 1.405 μs ( +- 35.11 ns )+regularConcat-10000/byteStringStrictBuilder mean 321.3 μs ( +- 11.13 μs )+regularConcat-10000/byteStringTreeBuilder mean 349.1 μs ( +- 4.359 μs )+regularConcat-10000/fastBuilder mean 121.0 μs ( +- 1.755 μs )+regularConcat-10000/bufferBuilder mean 156.1 μs ( +- 2.050 μs )+regularConcat-10000/byteString mean 106.6 μs ( +- 1.355 μs )+regularConcat-10000/blazeBuilder mean 110.8 μs ( +- 1.397 μs )+regularConcat-10000/binary mean 308.1 μs ( +- 5.346 μs )+regularConcat-10000/cereal mean 352.0 μs ( +- 6.142 μs )+regularConcat-10000/mason mean 130.2 μs ( +- 10.39 μs )+</pre>+</details> Architecture ----
mason.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: mason-version: 0.2.2+version: 0.2.3 synopsis: Fast and extensible bytestring builder description: This package provides efficient implementation of bytestring builders.
src/Mason/Builder.hs view
@@ -16,12 +16,16 @@ , BuilderFor , Buildable -- * Runners+ , StrictByteStringBackend , toStrictByteString+ , LazyByteStringBackend , toLazyByteString+ , BufferedIOBackend , hPutBuilderLen , hPutBuilder , sendBuilder , withPopper+ , StreamingBackend , toStreamingBody -- * Primitives , flush@@ -75,6 +79,7 @@ , int32Dec , int64Dec , intDec+ , intDecPadded , integerDec , word8Hex , word16Hex@@ -481,7 +486,10 @@ intDec :: Int -> Builder intDec = B.primBounded P.intDec -+-- | 'intDec' with 0 padding+intDecPadded :: Int -> Int -> Builder+intDecPadded n = zeroPaddedBoundedPrim n P.intDec+{-# INLINE intDecPadded #-} ------------------------------------------------------------------------------ -- Hexadecimal Encoding@@ -615,7 +623,7 @@ go n | n < maxPow10 = intDec (fromInteger n) | otherwise = case putH (splitf (maxPow10 * maxPow10) n) of- (x:xs) -> intDec x `mappend` B.primMapListBounded intDecPadded xs+ (x:xs) -> intDec x `mappend` B.primMapListBounded intDecPadded18 xs [] -> errImpossible "integerDec: go" splitf :: Integer -> Integer -> [Integer]@@ -653,9 +661,9 @@ foreign import ccall unsafe "static _hs_bytestring_long_long_int_dec_padded18" c_long_long_int_dec_padded18 :: CLLong -> Ptr Word8 -> IO () -{-# INLINE intDecPadded #-}-intDecPadded :: P.BoundedPrim Int-intDecPadded = P.liftFixedToBounded $ caseWordSize_32_64+{-# INLINE intDecPadded18 #-}+intDecPadded18 :: P.BoundedPrim Int+intDecPadded18 = P.liftFixedToBounded $ caseWordSize_32_64 (P.fixedPrim 9 $ c_int_dec_padded9 . fromIntegral) (P.fixedPrim 18 $ c_long_long_int_dec_padded18 . fromIntegral)
src/Mason/Builder/Internal.hs view
@@ -16,11 +16,13 @@ , unBuilder , byteStringCopy , shortByteString+ , StrictByteStringBackend , toStrictByteString , Channel(..)+ , LazyByteStringBackend , toLazyByteString , withPopper- , StreamingEnv(..)+ , StreamingBackend(..) , toStreamingBody , stringUtf8 , lengthPrefixedWithin@@ -31,6 +33,7 @@ , primMapByteStringFixed , primMapLazyByteStringFixed , PutEnv(..)+ , BufferedIOBackend , hPutBuilderLen , encodeUtf8BuilderEscaped , sendBuilder@@ -305,8 +308,10 @@ return $ Buffer (dst' `plusPtr` size') (dst' `plusPtr` pos) {-# INLINE allocate #-} +type StrictByteStringBackend = GrowingBuffer+ -- | Create a strict 'B.ByteString'-toStrictByteString :: BuilderFor GrowingBuffer -> B.ByteString+toStrictByteString :: BuilderFor StrictByteStringBackend -> B.ByteString toStrictByteString b = unsafePerformIO $ do fptr0 <- mallocForeignPtrBytes initialSize bufferRef <- newIORef fptr0@@ -341,8 +346,10 @@ allocate = allocateConstant chBuffer {-# INLINE allocate #-} +type LazyByteStringBackend = Channel+ -- | Create a lazy 'BL.ByteString'. Threaded runtime is required.-toLazyByteString :: BuilderFor Channel -> BL.ByteString+toLazyByteString :: BuilderFor LazyByteStringBackend -> BL.ByteString toLazyByteString body = unsafePerformIO $ withPopper body $ \pop -> do let go _ = unsafePerformIO $ do bs <- pop@@ -353,7 +360,7 @@ {-# INLINE toLazyByteString #-} -- | Use 'Builder' as a <http://hackage.haskell.org/package/http-client-0.7.1/docs/Network-HTTP-Client.html#t:GivesPopper GivesPopper'-withPopper :: BuilderFor Channel -> (IO B.ByteString -> IO a) -> IO a+withPopper :: BuilderFor LazyByteStringBackend -> (IO B.ByteString -> IO a) -> IO a withPopper body cont = do resp <- newEmptyMVar @@ -410,10 +417,12 @@ allocate = allocateConstant peBuffer {-# INLINE allocate #-} +type BufferedIOBackend = PutEnv+ -- | Write a 'Builder' into a handle and obtain the number of bytes written. -- 'flush' does not imply actual disk operations. Set 'NoBuffering' if you want -- it to write the content immediately.-hPutBuilderLen :: Handle -> BuilderFor PutEnv -> IO Int+hPutBuilderLen :: Handle -> BuilderFor BufferedIOBackend -> IO Int hPutBuilderLen h b = do let initialSize = 4096 fptr <- mallocForeignPtrBytes initialSize@@ -436,7 +445,7 @@ when (sent > 0) $ go $ p `plusPtr` sent -- | Write a 'Builder' into a handle and obtain the number of bytes written.-sendBuilder :: S.Socket -> BuilderFor PutEnv -> IO Int+sendBuilder :: S.Socket -> BuilderFor BufferedIOBackend -> IO Int sendBuilder sock b = do let initialSize = 4096 fptr <- mallocForeignPtrBytes initialSize@@ -542,17 +551,17 @@ foreign import ccall unsafe "static grisu3" c_grisu3 :: CDouble -> Ptr Word8 -> Ptr CInt -> Ptr CInt -> IO CInt -data StreamingEnv = StreamingEnv+data StreamingBackend = StreamingBackend { sePush :: !(B.ByteString -> IO ()) , seBuffer :: !(IORef (ForeignPtr Word8)) } -instance Buildable StreamingEnv where+instance Buildable StreamingBackend where byteString bs | B.length bs < 4096 = byteStringCopy bs | otherwise = flush <> Builder (\env b -> b <$ sePush env bs) {-# INLINE byteString #-}- flush = Builder $ \(StreamingEnv push ref) (Buffer end ptr) -> do+ flush = Builder $ \(StreamingBackend push ref) (Buffer end ptr) -> do ptr0 <- unsafeForeignPtrToPtr <$> readIORef ref let len = minusPtr ptr ptr0 when (len > 0) $ push $! B.unsafeCreate len $ \dst -> B.memcpy dst ptr0 len@@ -562,14 +571,14 @@ {-# INLINE allocate #-} -- | Convert a 'Builder' into a <http://hackage.haskell.org/package/wai-3.2.2.1/docs/Network-Wai.html#t:StreamingBody StreamingBody>.-toStreamingBody :: BuilderFor StreamingEnv -> (BB.Builder -> IO ()) -> IO () -> IO ()+toStreamingBody :: BuilderFor StreamingBackend -> (BB.Builder -> IO ()) -> IO () -> IO () toStreamingBody body = \write _ -> do let initialSize = 4080 fptr <- mallocForeignPtrBytes initialSize ref <- newIORef fptr let ptr = unsafeForeignPtrToPtr fptr Buffer _ ptr2 <- unBuilder body- (StreamingEnv (write . BB.byteString) ref)+ (StreamingBackend (write . BB.byteString) ref) (Buffer (ptr `plusPtr` initialSize) ptr) fptr' <- readIORef ref let ptr1 = unsafeForeignPtrToPtr fptr'