packages feed

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 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