fast-builder 0.0.0.6 → 0.0.1.0
raw patch · 4 files changed
+101/−22 lines, 4 filesdep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base
API changes (from Hackage documentation)
+ Data.ByteString.FastBuilder: hPutBuilderWith :: Handle -> Int -> Int -> Builder -> IO ()
+ Data.ByteString.FastBuilder.Internal: hPutBuilderWith :: Handle -> Int -> Int -> Builder -> IO ()
+ Data.ByteString.FastBuilder.Internal: unsafeCStringLen :: CStringLen -> Builder
+ Data.ByteString.FastBuilder.Unsafe: unsafeCString :: CString -> Builder
+ Data.ByteString.FastBuilder.Unsafe: unsafeCStringLen :: CStringLen -> Builder
- Data.ByteString.FastBuilder.Internal: HandleSink :: !Handle -> !(IORef Queue) -> DataSink
+ Data.ByteString.FastBuilder.Internal: HandleSink :: !Handle -> !Int -> !(IORef Queue) -> DataSink
Files
- Data/ByteString/FastBuilder.hs +61/−6
- Data/ByteString/FastBuilder/Internal.hs +25/−13
- Data/ByteString/FastBuilder/Unsafe.hs +7/−0
- fast-builder.cabal +8/−3
Data/ByteString/FastBuilder.hs view
@@ -1,14 +1,68 @@ -- | An efficient implementation of ByteString builder. -- -- In many cases, this module works as a drop-in replacement for--- Data.ByteString.Builder, and should improve speed. However, one caveat--- applies: when using 'toLazyByteString', if you consume the result+-- Data.ByteString.Builder, and should improve speed.+--+-- = Performance tips+--+-- fast-builder should be faster than the standard builder in most situations.+-- However, by following certain code patterns, you can often achive even+-- more efficient code, with almost no memory allocation aside from the+-- resulting 'ByteString' itself. The below are a list of hints for writing+-- efficient code for constructing builders.+--+-- == Return builders directly from your function+--+-- Once you construct a builder, it's usually a good idea to just return it from+-- your function. Avoid storing it in a data structure or passing it to another+-- function, unless they are going to be eliminated by the compiler.+-- Schematically, prefer this:+--+-- @+-- good :: YourDataStructure -> Builder+-- good d = serializeThis (this d) <> serializeThat (that d)+-- @+--+-- over:+--+-- @+-- bad0 :: YourDataStructure -> (Int, Builder)+-- bad0 d+-- = (compute d, serializeThis (this d) <> serializeThat (that d))+-- @+--+-- or:+--+-- @+-- bad1 :: YourDataStructure -> Builder+-- bad1 d = serializeMore d (serializeThis (this d))+-- @+--+-- An important special case of this general rule is to prefer foldr over+-- foldl' when serializing a list, and to prefer structural recursion over+-- tail recursion in general.+--+-- === Use 'rebuild'+--+-- When your function returns a different builder depending on the input,+-- it's usually a good idea to use 'rebuild' to wrap the whole body of your+-- function. See the documentation for 'rebuild' for details.+--+-- === Background+--+-- Why is it good to return builders directly? It is because they are+-- implemented as functions. When storing a function in a data structure or+-- passing it around, you need to first allocate a closure for it. However,+-- if you are just returning it, the returned function can be merged with your+-- function, creating a function with a larger arity. For example, GHC can+-- compile the @good@ function above into a 5-ary function, which requires+-- no runtime allocation (the exact arity depends on the library version).+--+-- == Watch out for lazy ByteString generation+--+-- When using 'toLazyByteString', if you consume the result -- in a bound thread, performance degrades significantly. See the -- documentation for 'toLazyByteString' for details.------ Sometimes performance can be greatly improved by inserting calls to--- 'rebuild' to your program. See the documentation for 'rebuild' for--- details. module Data.ByteString.FastBuilder ( -- * The type@@ -20,6 +74,7 @@ , toLazyByteStringWith , toStrictByteString , hPutBuilder+ , hPutBuilderWith -- * Performance tuning , rebuild
Data/ByteString/FastBuilder/Internal.hs view
@@ -39,6 +39,7 @@ , toLazyByteStringWith , toStrictByteString , hPutBuilder+ , hPutBuilderWith -- * Basic builders , primBounded@@ -51,6 +52,7 @@ , byteStringCopyNoCheck , byteStringInsert , unsafeCString+ , unsafeCStringLen , ensureBytes , getBytes @@ -130,7 +132,7 @@ -- ^ The destination of data changes while the builder is running. | GrowingBuffer !(IORef (ForeignPtr Word8)) -- ^ Bytes are accumulated in a contiguous buffer.- | HandleSink !IO.Handle !(IORef Queue)+ | HandleSink !IO.Handle !Int{-next buffer size-} !(IORef Queue) -- ^ Bytes are first accumulated in the 'Queue', then flushed to the -- 'IO.Handle'. @@ -467,12 +469,18 @@ -- | Output a 'Builder' to a 'IO.Handle'. hPutBuilder :: IO.Handle -> Builder -> IO ()-hPutBuilder !h builder = do- let cap = 100- fptr <- mallocForeignPtrBytes cap+hPutBuilder !h builder = hPutBuilderWith h 100 4096 builder++-- | Like 'hPutBuffer', but allows the user to specify the initial+-- and the subsequent desired buffer sizes. This function may be useful for+-- setting large buffer when high throughput I/O is needed.+hPutBuilderWith :: IO.Handle -> Int -> Int -> Builder -> IO ()+hPutBuilderWith !h !initialCap !nextCap builder = do+ fptr <- mallocForeignPtrBytes initialCap qRef <- newIORef $ Queue fptr 0 let !base = unsafeForeignPtrToPtr fptr- cur <- runBuilder builder (HandleSink h qRef) base (base `plusPtr` cap)+ cur <- runBuilder builder (HandleSink h nextCap qRef)+ base (base `plusPtr` initialCap) flushQueue h qRef cur ----------------------------------------------------------------@@ -591,7 +599,7 @@ when (r < S.length bstr) $ growBuffer bufRef (S.length bstr) useBuilder $ byteStringCopyNoCheck bstr- HandleSink h queueRef -> do+ HandleSink h _nextCap queueRef -> do cur <- getCur io $ flushQueue h queueRef cur io $ S.hPut h bstr@@ -602,14 +610,18 @@ unsafeCString :: CString -> Builder unsafeCString cstr = rebuild $ let !len = fromIntegral $ c_pure_strlen cstr- in- mappend (ensureBytes len) $ mkBuilder $ do- cur <- getCur- io $ copyBytes cur (castPtr cstr) len- setCur $ cur `plusPtr` len+ in unsafeCStringLen (cstr, len) foreign import ccall unsafe "strlen" c_pure_strlen :: CString -> CSize +-- | Turn a 'CStringLen' into a 'Builder'. The behavior is undefined if the+-- given 'CStringLen' does not point to a constant memory block.+unsafeCStringLen :: CStringLen -> Builder+unsafeCStringLen (ptr, len) = mappend (ensureBytes len) $ mkBuilder $ do+ cur <- getCur+ io $ copyBytes cur (castPtr ptr) len+ setCur $ cur `plusPtr` len+ -- | @'ensureBytes' n@ ensures that at least @n@ bytes of free space is -- available in the current buffer, by allocating a new buffer when -- necessary.@@ -638,10 +650,10 @@ BoundedGrowingBuffer fptr bound -> growBufferBounded dRef fptr bound (I# n) GrowingBuffer bufRef -> growBuffer bufRef (I# n)- HandleSink h queueRef -> do+ HandleSink h nextCap queueRef -> do cur <- getCur io $ flushQueue h queueRef cur- switchQueue queueRef $ max 4096 (I# n)+ switchQueue queueRef $ max nextCap (I# n) {-# NOINLINE getBytes_ #-} -- | Return the remaining size of the current buffer, in bytes.
+ Data/ByteString/FastBuilder/Unsafe.hs view
@@ -0,0 +1,7 @@+-- | Unsafe functions.+module Data.ByteString.FastBuilder.Unsafe+ ( unsafeCString+ , unsafeCStringLen+ ) where++import Data.ByteString.FastBuilder.Internal
fast-builder.cabal view
@@ -1,5 +1,5 @@ name: fast-builder-version: 0.0.0.6+version: 0.0.1.0 synopsis: Fast ByteString Builder description: An efficient implementation of ByteString builder. It should be faster than the standard implementation in most cases.@@ -20,11 +20,12 @@ library exposed-modules: Data.ByteString.FastBuilder,- Data.ByteString.FastBuilder.Internal+ Data.ByteString.FastBuilder.Internal,+ Data.ByteString.FastBuilder.Unsafe other-modules: Data.ByteString.FastBuilder.Internal.Prim -- other-extensions:- build-depends: base >= 4.8 && < 4.10, bytestring >= 0.10.6.0, ghc-prim+ build-depends: base >= 4.8 && < 4.11, bytestring >= 0.10.6.0, ghc-prim -- hs-source-dirs: default-language: Haskell2010 ghc-options: -Wall -g@@ -40,6 +41,7 @@ template-haskell, true-name >= 0.1.0.0 ghc-options: -fsimpl-tick-factor=120 ghc-options: -Wall -threaded -g -rtsopts+ default-language: Haskell2010 benchmark vector type: exitcode-stdio-1.0@@ -47,6 +49,7 @@ hs-source-dirs: benchmarks build-depends: base, fast-builder, criterion, bytestring, vector, deepseq ghc-options: -Wall -threaded -g -rtsopts -eventlog+ default-language: Haskell2010 benchmark map type: exitcode-stdio-1.0@@ -54,6 +57,7 @@ hs-source-dirs: benchmarks build-depends: base, fast-builder, criterion, bytestring, containers, deepseq ghc-options: -Wall -threaded -g -rtsopts -eventlog+ default-language: Haskell2010 test-suite prop type: exitcode-stdio-1.0@@ -61,6 +65,7 @@ hs-source-dirs: tests build-depends: base, fast-builder, bytestring, QuickCheck, stm, process ghc-options: -Wall -g -rtsopts+ default-language: Haskell2010 source-repository head type: git