blaze-builder 0.1 → 0.2.0.0
raw patch · 35 files changed
+7037/−385 lines, 35 filesdep ~basedep ~bytestringdep ~textnew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, bytestring, text
API changes (from Hackage documentation)
- Text.Blaze.Builder.Core: Write :: {-# UNPACK #-} !Int -> (Ptr Word8 -> IO ()) -> Write
- Text.Blaze.Builder.Core: data Builder
- Text.Blaze.Builder.Core: data Write
- Text.Blaze.Builder.Core: fromByteString :: ByteString -> Builder
- Text.Blaze.Builder.Core: instance Monoid Builder
- Text.Blaze.Builder.Core: instance Monoid Write
- Text.Blaze.Builder.Core: singleton :: Word8 -> Builder
- Text.Blaze.Builder.Core: toLazyByteString :: Builder -> ByteString
- Text.Blaze.Builder.Core: writeByte :: Word8 -> Write
- Text.Blaze.Builder.Core: writeByteString :: ByteString -> Write
- Text.Blaze.Builder.Core: writeList :: (a -> Write) -> [a] -> Builder
- Text.Blaze.Builder.Core: writeSingleton :: (a -> Write) -> a -> Builder
- Text.Blaze.Builder.Html: fromHtmlEscapedChar :: Char -> Builder
- Text.Blaze.Builder.Html: fromHtmlEscapedString :: String -> Builder
- Text.Blaze.Builder.Html: fromHtmlEscapedText :: Text -> Builder
- Text.Blaze.Builder.Html: writeHtmlEscapedChar :: Char -> Write
- Text.Blaze.Builder.Utf8: fromChar :: Char -> Builder
- Text.Blaze.Builder.Utf8: fromString :: String -> Builder
- Text.Blaze.Builder.Utf8: fromText :: Text -> Builder
- Text.Blaze.Builder.Utf8: writeChar :: Char -> Write
+ Blaze.ByteString.Builder: append :: Builder -> Builder -> Builder
+ Blaze.ByteString.Builder: data Builder
+ Blaze.ByteString.Builder: empty :: Builder
+ Blaze.ByteString.Builder: flush :: Builder
+ Blaze.ByteString.Builder: putWord16be :: Word16 -> Builder
+ Blaze.ByteString.Builder: putWord16host :: Word16 -> Builder
+ Blaze.ByteString.Builder: putWord16le :: Word16 -> Builder
+ Blaze.ByteString.Builder: putWord32be :: Word32 -> Builder
+ Blaze.ByteString.Builder: putWord32host :: Word32 -> Builder
+ Blaze.ByteString.Builder: putWord32le :: Word32 -> Builder
+ Blaze.ByteString.Builder: putWord64be :: Word64 -> Builder
+ Blaze.ByteString.Builder: putWord64host :: Word64 -> Builder
+ Blaze.ByteString.Builder: putWord64le :: Word64 -> Builder
+ Blaze.ByteString.Builder: putWordhost :: Word -> Builder
+ Blaze.ByteString.Builder: singleton :: Word8 -> Builder
+ Blaze.ByteString.Builder: toByteString :: Builder -> ByteString
+ Blaze.ByteString.Builder: toByteStringIO :: (ByteString -> IO ()) -> Builder -> IO ()
+ Blaze.ByteString.Builder: toByteStringIOWith :: Int -> (ByteString -> IO ()) -> Builder -> IO ()
+ Blaze.ByteString.Builder: toLazyByteString :: Builder -> ByteString
+ Blaze.ByteString.Builder: toLazyByteStringWith :: Int -> Int -> Int -> Builder -> ByteString -> ByteString
+ Blaze.ByteString.Builder.ByteString: copyByteString :: ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: copyLazyByteString :: ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: fromByteString :: ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: fromByteStringWith :: Int -> ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: fromLazyByteString :: ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: fromLazyByteStringWith :: Int -> ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: insertByteString :: ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: insertLazyByteString :: ByteString -> Builder
+ Blaze.ByteString.Builder.ByteString: writeByteString :: ByteString -> Write
+ Blaze.ByteString.Builder.Char.Utf8: fromChar :: Char -> Builder
+ Blaze.ByteString.Builder.Char.Utf8: fromLazyText :: Text -> Builder
+ Blaze.ByteString.Builder.Char.Utf8: fromShow :: (Show a) => a -> Builder
+ Blaze.ByteString.Builder.Char.Utf8: fromString :: String -> Builder
+ Blaze.ByteString.Builder.Char.Utf8: fromText :: Text -> Builder
+ Blaze.ByteString.Builder.Char.Utf8: writeChar :: Char -> Write
+ Blaze.ByteString.Builder.Html.Utf8: fromHtmlEscapedChar :: Char -> Builder
+ Blaze.ByteString.Builder.Html.Utf8: fromHtmlEscapedLazyText :: Text -> Builder
+ Blaze.ByteString.Builder.Html.Utf8: fromHtmlEscapedShow :: (Show a) => a -> Builder
+ Blaze.ByteString.Builder.Html.Utf8: fromHtmlEscapedString :: String -> Builder
+ Blaze.ByteString.Builder.Html.Utf8: fromHtmlEscapedText :: Text -> Builder
+ Blaze.ByteString.Builder.Html.Utf8: writeHtmlEscapedChar :: Char -> Write
+ Blaze.ByteString.Builder.Int: fromInt16be :: Int16 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt16host :: Int16 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt16le :: Int16 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt16sbe :: [Int16] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt16shost :: [Int16] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt16sle :: [Int16] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt32be :: Int32 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt32host :: Int32 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt32le :: Int32 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt32sbe :: [Int32] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt32shost :: [Int32] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt32sle :: [Int32] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt64be :: Int64 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt64host :: Int64 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt64le :: Int64 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt64sbe :: [Int64] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt64shost :: [Int64] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt64sle :: [Int64] -> Builder
+ Blaze.ByteString.Builder.Int: fromInt8 :: Int8 -> Builder
+ Blaze.ByteString.Builder.Int: fromInt8s :: [Int8] -> Builder
+ Blaze.ByteString.Builder.Int: fromInthost :: Int -> Builder
+ Blaze.ByteString.Builder.Int: fromIntshost :: [Int] -> Builder
+ Blaze.ByteString.Builder.Int: writeInt16be :: Int16 -> Write
+ Blaze.ByteString.Builder.Int: writeInt16host :: Int16 -> Write
+ Blaze.ByteString.Builder.Int: writeInt16le :: Int16 -> Write
+ Blaze.ByteString.Builder.Int: writeInt32be :: Int32 -> Write
+ Blaze.ByteString.Builder.Int: writeInt32host :: Int32 -> Write
+ Blaze.ByteString.Builder.Int: writeInt32le :: Int32 -> Write
+ Blaze.ByteString.Builder.Int: writeInt64be :: Int64 -> Write
+ Blaze.ByteString.Builder.Int: writeInt64host :: Int64 -> Write
+ Blaze.ByteString.Builder.Int: writeInt64le :: Int64 -> Write
+ Blaze.ByteString.Builder.Int: writeInt8 :: Int8 -> Write
+ Blaze.ByteString.Builder.Int: writeInthost :: Int -> Write
+ Blaze.ByteString.Builder.Internal: BufferFull :: !!Int -> !!Ptr Word8 -> !BuildStep -> BuildSignal
+ Blaze.ByteString.Builder.Internal: Builder :: (BuildStep -> BuildStep) -> Builder
+ Blaze.ByteString.Builder.Internal: Done :: !!Ptr Word8 -> BuildSignal
+ Blaze.ByteString.Builder.Internal: ModifyChunks :: !!Ptr Word8 -> !ByteString -> ByteString -> !BuildStep -> BuildSignal
+ Blaze.ByteString.Builder.Internal: data BuildSignal
+ Blaze.ByteString.Builder.Internal: defaultBufferSize :: Int
+ Blaze.ByteString.Builder.Internal: defaultMaximalCopySize :: Int
+ Blaze.ByteString.Builder.Internal: defaultMinimalBufferSize :: Int
+ Blaze.ByteString.Builder.Internal: flush :: Builder
+ Blaze.ByteString.Builder.Internal: instance Monoid Builder
+ Blaze.ByteString.Builder.Internal: newtype Builder
+ Blaze.ByteString.Builder.Internal: toByteString :: Builder -> ByteString
+ Blaze.ByteString.Builder.Internal: toByteStringIO :: (ByteString -> IO ()) -> Builder -> IO ()
+ Blaze.ByteString.Builder.Internal: toByteStringIOWith :: Int -> (ByteString -> IO ()) -> Builder -> IO ()
+ Blaze.ByteString.Builder.Internal: toLazyByteString :: Builder -> ByteString
+ Blaze.ByteString.Builder.Internal: toLazyByteStringWith :: Int -> Int -> Int -> Builder -> ByteString -> ByteString
+ Blaze.ByteString.Builder.Internal: type BuildStep = Ptr Word8 -> Ptr Word8 -> IO BuildSignal
+ Blaze.ByteString.Builder.Word: fromWord16be :: Word16 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord16host :: Word16 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord16le :: Word16 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord16sbe :: [Word16] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord16shost :: [Word16] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord16sle :: [Word16] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord32be :: Word32 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord32host :: Word32 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord32le :: Word32 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord32sbe :: [Word32] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord32shost :: [Word32] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord32sle :: [Word32] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord64be :: Word64 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord64host :: Word64 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord64le :: Word64 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord64sbe :: [Word64] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord64shost :: [Word64] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord64sle :: [Word64] -> Builder
+ Blaze.ByteString.Builder.Word: fromWord8 :: Word8 -> Builder
+ Blaze.ByteString.Builder.Word: fromWord8s :: [Word8] -> Builder
+ Blaze.ByteString.Builder.Word: fromWordhost :: Word -> Builder
+ Blaze.ByteString.Builder.Word: fromWordshost :: [Word] -> Builder
+ Blaze.ByteString.Builder.Word: writeWord16be :: Word16 -> Write
+ Blaze.ByteString.Builder.Word: writeWord16host :: Word16 -> Write
+ Blaze.ByteString.Builder.Word: writeWord16le :: Word16 -> Write
+ Blaze.ByteString.Builder.Word: writeWord32be :: Word32 -> Write
+ Blaze.ByteString.Builder.Word: writeWord32host :: Word32 -> Write
+ Blaze.ByteString.Builder.Word: writeWord32le :: Word32 -> Write
+ Blaze.ByteString.Builder.Word: writeWord64be :: Word64 -> Write
+ Blaze.ByteString.Builder.Word: writeWord64host :: Word64 -> Write
+ Blaze.ByteString.Builder.Word: writeWord64le :: Word64 -> Write
+ Blaze.ByteString.Builder.Word: writeWord8 :: Word8 -> Write
+ Blaze.ByteString.Builder.Word: writeWordhost :: Word -> Write
+ Blaze.ByteString.Builder.Write: Write :: !!Int -> (Ptr Word8 -> IO ()) -> Write
+ Blaze.ByteString.Builder.Write: data Write
+ Blaze.ByteString.Builder.Write: fromWrite :: Write -> Builder
+ Blaze.ByteString.Builder.Write: fromWrite16List :: (a -> Write) -> [a] -> Builder
+ Blaze.ByteString.Builder.Write: fromWrite1List :: (a -> Write) -> [a] -> Builder
+ Blaze.ByteString.Builder.Write: fromWrite2List :: (a -> Write) -> [a] -> Builder
+ Blaze.ByteString.Builder.Write: fromWrite4List :: (a -> Write) -> [a] -> Builder
+ Blaze.ByteString.Builder.Write: fromWrite8List :: (a -> Write) -> [a] -> Builder
+ Blaze.ByteString.Builder.Write: fromWriteSingleton :: (a -> Write) -> a -> Builder
+ Blaze.ByteString.Builder.Write: instance Monoid Write
Files
- Blaze/ByteString/Builder.hs +205/−0
- Blaze/ByteString/Builder/ByteString.hs +229/−0
- Blaze/ByteString/Builder/Char/Utf8.hs +149/−0
- Blaze/ByteString/Builder/Html/Utf8.hs +81/−0
- Blaze/ByteString/Builder/Int.hs +301/−0
- Blaze/ByteString/Builder/Internal.hs +441/−0
- Blaze/ByteString/Builder/Word.hs +436/−0
- Blaze/ByteString/Builder/Write.hs +384/−0
- CHANGES +34/−0
- Makefile +101/−0
- README.markdown +30/−0
- TODO +63/−0
- Text/Blaze/Builder/Core.hs +0/−219
- Text/Blaze/Builder/Html.hs +0/−55
- Text/Blaze/Builder/Utf8.hs +0/−91
- benchmarks/BenchThroughput.hs +239/−0
- benchmarks/BlazeVsBinary.hs +83/−0
- benchmarks/ChunkedWrite.hs +158/−0
- benchmarks/Compression.hs +55/−0
- benchmarks/PlotTest.hs +218/−0
- benchmarks/StringAndText.hs +125/−0
- benchmarks/Throughput/BinaryBuilder.hs +697/−0
- benchmarks/Throughput/BinaryBuilderDeclarative.hs +118/−0
- benchmarks/Throughput/BinaryPut.hs +696/−0
- benchmarks/Throughput/BlazeBuilder.hs +702/−0
- benchmarks/Throughput/BlazeBuilderDeclarative.hs +197/−0
- benchmarks/Throughput/BlazePut.hs +742/−0
- benchmarks/Throughput/BlazePutMonad.hs +222/−0
- benchmarks/Throughput/CBenchmark.c +39/−0
- benchmarks/Throughput/CBenchmark.h +4/−0
- benchmarks/Throughput/Memory.hs +98/−0
- benchmarks/Throughput/Utils.hs +12/−0
- blaze-builder.cabal +47/−20
- tests/LlvmSegfault.hs +35/−0
- tests/Tests.hs +96/−0
+ Blaze/ByteString/Builder.hs view
@@ -0,0 +1,205 @@+-----------------------------------------------------------------------------+-- |+-- Module : Blaze.ByteString.Builder+-- Copyright : (c) 2010 Jasper Van der Jeugt & Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- "Blaze.ByteString.Builder" is the main module, which you should import as a user+-- of the @blaze-builder@ library.+--+-- > import Blaze.ByteString.Builder+-- +-- It provides you with a type 'Builder' that allows to efficiently construct+-- lazy bytestrings with a large average chunk size.+--+-- Intuitively, a 'Builder' denotes the construction of a part of a lazy+-- bytestring. Builders can either be created using one of the primitive+-- combinators in "Blaze.ByteString.Builder.Write" or by using one of the predefined+-- combinators for standard Haskell values (see the exposed modules of this+-- package). Concatenation of builders is done using 'mappend' from the+-- 'Monoid' typeclass.+--+-- Here is a small example that serializes a list of strings using the UTF-8+-- encoding.+--+-- @ import "Blaze.ByteString.Builder.Char.Utf8"@+--+-- > strings :: [String]+-- > strings = replicate 10000 "Hello there!"+--+-- The function @'fromString'@ creates a 'Builder' denoting the UTF-8 encoded+-- argument. Hence, UTF-8 encoding and concatenating all @strings@ can be done+-- follows.+--+-- > concatenation :: Builder+-- > concatenation = mconcat $ map fromString strings+--+-- The function 'toLazyByteString' can be used to execute a 'Builder' and+-- obtain the resulting lazy bytestring.+--+-- > result :: L.ByteString+-- > result = toLazyByteString concatenation+--+-- The @result@ is a lazy bytestring containing 10000 repetitions of the string+-- @\"Hello there!\"@ encoded using UTF-8. The corresponding 120000 bytes are+-- distributed among three chunks of 32kb and a last chunk of 6kb.+--+-- /A note on history./ This serialization library was inspired by the+-- @Data.Binary.Builder@ module provided by the @binary@ package. It was+-- originally developed with the specific needs of the @blaze-html@ package in+-- mind. Since then it has been restructured to serve as a drop-in replacement+-- for @Data.Binary.Builder@, which it improves upon both in speed as well as+-- expressivity.+-----------------------------------------------------------------------------++module Blaze.ByteString.Builder+ ( + -- * The @Builder@ type+ Builder++ -- * Creating builders+ , module Blaze.ByteString.Builder.Write + , module Blaze.ByteString.Builder.Int+ , module Blaze.ByteString.Builder.Word+ , module Blaze.ByteString.Builder.ByteString+ , flush++ -- * Executing builders+ , toLazyByteString+ , toLazyByteStringWith+ , toByteString+ , toByteStringIO+ , toByteStringIOWith+ ++ -- * Compatibility to Data.Binary.Builder from the binary package+ --+ -- | The following functions ensure that @"Blaze.ByteString.Builder"@ is a+ -- drop-in replacement for @Data.Binary.Builder@ from the @binary@+ -- package. Note that these functions are deprecated and may be removed+ -- in future versions of the @blaze-builder@ package.+ --+ , empty -- DEPRECATED: use 'mempty' instead+ , singleton -- DEPRECATED: use 'fromByte' instead+ , append -- DEPRECATED: use 'mappend' instead+ + , putWord16be -- DEPRECATED: use 'fromWord<n><endian>' instead+ , putWord32be --+ , putWord64be --+ , putWord16le --+ , putWord32le --+ , putWord64le -- for all these functions+ , putWordhost --+ , putWord16host --+ , putWord32host --+ , putWord64host --+ ) where++import Blaze.ByteString.Builder.Internal+import Blaze.ByteString.Builder.Write+import Blaze.ByteString.Builder.Int+import Blaze.ByteString.Builder.Word+import Blaze.ByteString.Builder.ByteString++import Data.Monoid+import Data.Word++------------------------------------------------------------------------------+-- API Compatibility to Data.Binary.Builder from 'binary'+------------------------------------------------------------------------------++-- | /O(1)/. An empty builder. +--+-- /Deprecated:/ use 'mempty' instead.+empty :: Builder+empty = mempty+{-# DEPRECATED empty "Use 'mempty' instead." #-}++-- | /O(1)/. Append two builders. +--+-- /Deprecated:/ use 'mappend' instead.+append :: Builder -> Builder -> Builder+append = mappend+{-# DEPRECATED append "Use 'mappend' instead." #-}++-- | /O(1)/. Serialize a single byte.+--+-- /Deprecated:/ use 'fromWord8' instead.+singleton :: Word8 -> Builder+singleton = fromWriteSingleton writeWord8+{-# DEPRECATED singleton "Use 'fromWord8' instead." #-}++-- | /O(1)/. Serialize a 'Word16' in big endian format.+--+-- /Deprecated:/ use 'fromWord16be' instead.+putWord16be :: Word16 -> Builder+putWord16be = fromWord16be +{-# DEPRECATED putWord16be "Use 'fromWord16be' instead." #-}++-- | /O(1)/. Serialize a 'Word32' in big endian format.+--+-- /Deprecated:/ use 'fromWord32be' instead.+putWord32be :: Word32 -> Builder+putWord32be = fromWord32be+{-# DEPRECATED putWord32be "Use 'fromWord32be' instead." #-}++-- | /O(1)/. Serialize a 'Word64' in big endian format.+--+-- /Deprecated:/ use 'fromWord64be' instead.+putWord64be :: Word64 -> Builder+putWord64be = fromWord64be+{-# DEPRECATED putWord64be "Use 'fromWord64be' instead." #-}++-- | /O(1)/. Serialize a 'Word16' in little endian format.+--+-- /Deprecated:/ use 'fromWord16le' instead.+putWord16le :: Word16 -> Builder+putWord16le = fromWord16le+{-# DEPRECATED putWord16le "Use 'fromWord16le' instead." #-}++-- | /O(1)/. Serialize a 'Word32' in little endian format.+--+-- /Deprecated:/ use 'fromWord32le' instead.+putWord32le :: Word32 -> Builder+putWord32le = fromWord32le+{-# DEPRECATED putWord32le "Use 'fromWord32le' instead." #-}++-- | /O(1)/. Serialize a 'Word64' in little endian format.+--+-- /Deprecated:/ use 'fromWord64le' instead.+putWord64le :: Word64 -> Builder+putWord64le = fromWord64le+{-# DEPRECATED putWord64le "Use 'fromWord64le' instead." #-}++-- | /O(1)/. Serialize a 'Word' in host endian format.+--+-- /Deprecated:/ use 'fromWordhost' instead.+putWordhost :: Word -> Builder+putWordhost = fromWordhost+{-# DEPRECATED putWordhost "Use 'fromWordhost' instead." #-}++-- | /O(1)/. Serialize a 'Word16' in host endian format.+--+-- /Deprecated:/ use 'fromWord16host' instead.+putWord16host :: Word16 -> Builder+putWord16host = fromWord16host+{-# DEPRECATED putWord16host "Use 'fromWord16host' instead." #-}++-- | /O(1)/. Serialize a 'Word32' in host endian format.+--+-- /Deprecated:/ use 'fromWord32host' instead.+putWord32host :: Word32 -> Builder+putWord32host = fromWord32host+{-# DEPRECATED putWord32host "Use 'fromWord32host' instead." #-}++-- | /O(1)/. Serialize a 'Word64' in host endian format.+--+-- /Deprecated:/ use 'fromWord64host' instead.+putWord64host :: Word64 -> Builder+putWord64host = fromWord64host+{-# DEPRECATED putWord64host "Use 'fromWord64host' instead." #-}+
+ Blaze/ByteString/Builder/ByteString.hs view
@@ -0,0 +1,229 @@+{-# LANGUAGE CPP, BangPatterns, OverloadedStrings #-}++-- |+-- Module : Blaze.ByteString.Builder.ByteString+-- Copyright : (c) 2010 Jasper Van der Jeugt & Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- 'Write's and 'Builder's for strict and lazy bytestrings.+--+-- We assume the following qualified imports in order to differentiate between+-- strict and lazy bytestrings in the code examples.+--+-- > import qualified Data.ByteString as S+-- > import qualified Data.ByteString.Lazy as L+--+module Blaze.ByteString.Builder.ByteString+ ( + -- * Strict bytestrings+ writeByteString+ , fromByteString+ , fromByteStringWith+ , copyByteString+ , insertByteString++ -- * Lazy bytestrings+ , fromLazyByteString+ , fromLazyByteStringWith+ , copyLazyByteString+ , insertLazyByteString++ ) where++import Blaze.ByteString.Builder.Write+import Blaze.ByteString.Builder.Internal++import Foreign+import Data.Monoid++import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L++#ifdef BYTESTRING_IN_BASE+import qualified Data.ByteString.Base as S+import qualified Data.ByteString.Lazy.Base as L -- FIXME: check if this is the right module+#else+import qualified Data.ByteString.Internal as S+import qualified Data.ByteString.Lazy.Internal as L+#endif+++------------------------------------------------------------------------------+-- Strict ByteStrings+------------------------------------------------------------------------------++-- | Write a strict 'S.ByteString' to a buffer.+--+writeByteString :: S.ByteString -> Write+writeByteString bs = Write l io+ where+ (fptr, o, l) = S.toForeignPtr bs+ io pf = withForeignPtr fptr $ \p -> copyBytes pf (p `plusPtr` o) l+{-# INLINE writeByteString #-}++-- | Smart serialization of a strict bytestring.+--+-- @'fromByteString' = 'fromByteStringWith' 'defaultMaximalCopySize'@+--+-- Use this function to serialize strict bytestrings. It guarantees an+-- average chunk size of 4kb, which has been shown to be a reasonable size in+-- benchmarks. Note that the check whether to copy or to insert is (almost)+-- free as the builder performance is mostly memory-bound.+--+-- If you statically know that copying or inserting the strict bytestring is+-- always the best choice, then you can use the 'copyByteString' or+-- 'insertByteString' functions. +--+fromByteString :: S.ByteString -> Builder+fromByteString = fromByteStringWith defaultMaximalCopySize+{-# INLINE fromByteString #-}+++-- | @fromByteStringWith maximalCopySize bs@ serializes the strict bytestring+-- @bs@ according to the following rules.+--+-- [@S.length bs <= maximalCopySize@:] @bs@ is copied to the output buffer.+--+-- [@S.length bs > maximalCopySize@:] @bs@ the output buffer is flushed and+-- @bs@ is inserted directly as separate chunk in the output stream.+--+-- These rules guarantee that average chunk size in the output stream is at+-- least half the @maximalCopySize@.+--+fromByteStringWith :: Int -- ^ Maximal number of bytes to copy.+ -> S.ByteString -- ^ Strict 'S.ByteString' to serialize.+ -> Builder -- ^ Resulting 'Builder'.+fromByteStringWith maximalCopySize bs = Builder step+ where+ step k pf pe+ | maximalCopySize < size = + return $ ModifyChunks pf (L.Chunk bs) k+ | pf `plusPtr` size <= pe = do+ withForeignPtr fpbuf $ \pbuf -> + copyBytes pf (pbuf `plusPtr` offset) size+ let pf' = pf `plusPtr` size+ pf' `seq` k pf' pe+ | otherwise = return $ BufferFull size pf (step k)+ where+ (fpbuf, offset, size) = S.toForeignPtr bs+{-# INLINE fromByteStringWith #-}+++-- | @copyByteString bs@ serialize the strict bytestring @bs@ by copying it to+-- the output buffer. +--+-- Use this function to serialize strict bytestrings that are statically known+-- to be smallish (@<= 4kb@).+--+copyByteString :: S.ByteString -> Builder+copyByteString = fromWriteSingleton writeByteString+{-# INLINE copyByteString #-}++-- | @insertByteString bs@ serializes the strict bytestring @bs@ by inserting+-- it directly as a chunk of the output stream. +--+-- Note that this implies flushing the output buffer; even if it contains just+-- a single byte. Hence, you should use this operation only for large (@> 8kb@)+-- bytestrings, as otherwise the resulting output stream may be too fragmented+-- to be processed efficiently.+--+insertByteString :: S.ByteString -> Builder+insertByteString bs = Builder $ \ k pf _ ->+ return $ ModifyChunks pf (L.Chunk bs) k+{-# INLINE insertByteString #-}+++-- Lazy bytestrings+------------------------------------------------------------------------------++-- | /O(n)/. Smart serialization of a lazy bytestring.+--+-- @'fromLazyByteString' = 'fromLazyByteStringWith' 'defaultMaximalCopySize'@+--+-- Use this function to serialize lazy bytestrings. It guarantees an average+-- chunk size of 4kb, which has been shown to be a reasonable size in+-- benchmarks. Note that the check whether to copy or to insert is (almost)+-- free as the builder performance is mostly memory-bound.+--+-- If you statically know that copying or inserting /all/ chunks of the lazy+-- bytestring is always the best choice, then you can use the+-- 'copyLazyByteString' or 'insertLazyByteString' functions. +--+fromLazyByteString :: L.ByteString -> Builder+fromLazyByteString = fromLazyByteStringWith defaultMaximalCopySize+{-# INLINE fromLazyByteString #-}++-- | /O(n)/. Serialize a lazy bytestring chunk-wise according to the same rules+-- as in 'fromByteStringWith'.+--+-- Semantically, it holds that+--+-- > fromLazyByteStringWith maxCopySize+-- > = mconcat . map (fromByteStringWith maxCopySize) . L.toChunks+--+-- However, the left-hand-side is much more efficient, as it moves the+-- end-of-buffer pointer out of the inner loop and provides the compiler with+-- more strictness information.+--+fromLazyByteStringWith :: Int -- ^ Maximal number of bytes to copy.+ -> L.ByteString -- ^ Lazy 'L.ByteString' to serialize.+ -> Builder -- ^ Resulting 'Builder'.+fromLazyByteStringWith maximalCopySize = + makeBuilder+ where+ -- FIXME: Justify this first case split. Can we justify it at all? I seem+ -- to remember that its idea was to enable a partial inlining; i.e. sharing+ -- of 'step' between different calls to 'fromLazyByteStringWith'.+ makeBuilder L.Empty = mempty+ makeBuilder lbs0 = Builder $ step lbs0+ where+ step lbs1 k pf0 pe0 = go lbs1 pf0+ where+ go L.Empty !pf = k pf pe0+ go lbs@(L.Chunk bs' lbs') !pf+ | maximalCopySize < size = + return $ ModifyChunks pf (L.Chunk bs') (step lbs' k)++ | pf' <= pe0 = do+ withForeignPtr fpbuf $ \pbuf -> + copyBytes pf (pbuf `plusPtr` offset) size+ go lbs' pf'++ | otherwise = return $ BufferFull size pf (step lbs k)+ where+ pf' = pf `plusPtr` size+ (fpbuf, offset, size) = S.toForeignPtr bs'+{-# INLINE fromLazyByteStringWith #-}+++-- | /O(n)/. Serialize a lazy bytestring by copying /all/ chunks sequentially+-- to the output buffer.+--+-- See 'copyByteString' for usage considerations.+--++-- FIXME: Implement fused L.toChunks and fromWrite1List+copyLazyByteString :: L.ByteString -> Builder+copyLazyByteString = fromWrite1List writeByteString . L.toChunks+{-# INLINE copyLazyByteString #-}++-- | /O(n)/. Serialize a lazy bytestring by inserting /all/ its chunks directly+-- into the output stream.+--+-- See 'insertByteString' for usage considerations.+--+-- For library developers, see the 'ModifyChunks' build signal, if you+-- need an /O(1)/ lazy bytestring insert based on difference lists.+--+insertLazyByteString :: L.ByteString -> Builder+insertLazyByteString lbs = Builder step+ where+ step k pf _ = + return $ ModifyChunks pf (\lbs' -> L.foldrChunks L.Chunk lbs' lbs) k++{-# INLINE insertLazyByteString #-}+
+ Blaze/ByteString/Builder/Char/Utf8.hs view
@@ -0,0 +1,149 @@+{-# OPTIONS_GHC -fno-warn-unused-imports #-} +-- ignore warning from 'import Data.Text.Encoding'++-- |+-- Module : Blaze.ByteString.Builder.Char.Utf8+-- Copyright : (c) 2010 Jasper Van der Jeugt & Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- 'Write's and 'Builder's for serializing Unicode characters using the UTF-8+-- encoding. +--+module Blaze.ByteString.Builder.Char.Utf8+ ( + -- * Writing UTF-8 encoded characters to a buffer+ writeChar++ -- * Creating Builders from UTF-8 encoded characters+ , fromChar+ , fromString+ , fromShow+ , fromText+ , fromLazyText+ ) where++import Foreign+import Data.Char (ord)++import qualified Data.Text as TS+import qualified Data.Text.Encoding as TS -- imported for documentation links+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Encoding as TS -- imported for documentation links++import Blaze.ByteString.Builder.Internal+import Blaze.ByteString.Builder.Write++-- | Write a UTF-8 encoded Unicode character to a buffer.+--+-- Note that the control flow of 'writeChar' is more complicated than the one+-- of 'writeWord8', as the size of the write depends on the 'Char' written.+-- Therefore,+--+-- > fromWrite $ writeChar a `mappend` writeChar b+--+-- must not always be faster than+--+-- > fromChar a `mappend` fromChar b+--+-- Use benchmarking to make informed decisions.+--++-- FIXME: Use a Write that always checks if 4 bytes are available and only take+-- care of the precise pointer advance once the data has been written. Either+-- formulate it using continuation passing or returning the increment using the+-- IO action. The latter is probably simpler and better understandable.+--+writeChar :: Char -> Write+writeChar = encodeCharUtf8 f1 f2 f3 f4+ where+ f1 x = Write 1 $ \ptr -> poke ptr x++ f2 x1 x2 = Write 2 $ \ptr -> do poke ptr x1+ poke (ptr `plusPtr` 1) x2++ f3 x1 x2 x3 = Write 3 $ \ptr -> do poke ptr x1+ poke (ptr `plusPtr` 1) x2+ poke (ptr `plusPtr` 2) x3++ f4 x1 x2 x3 x4 = Write 4 $ \ptr -> do poke ptr x1+ poke (ptr `plusPtr` 1) x2+ poke (ptr `plusPtr` 2) x3+ poke (ptr `plusPtr` 3) x4+{-# INLINE writeChar #-}++-- | Encode a Unicode character to another datatype, using UTF-8. This function+-- acts as an abstract way of encoding characters, as it is unaware of what+-- needs to happen with the resulting bytes: you have to specify functions to+-- deal with those.+--+encodeCharUtf8 :: (Word8 -> a) -- ^ 1-byte UTF-8+ -> (Word8 -> Word8 -> a) -- ^ 2-byte UTF-8+ -> (Word8 -> Word8 -> Word8 -> a) -- ^ 3-byte UTF-8+ -> (Word8 -> Word8 -> Word8 -> Word8 -> a) -- ^ 4-byte UTF-8+ -> Char -- ^ Input 'Char'+ -> a -- ^ Result+encodeCharUtf8 f1 f2 f3 f4 c = case ord c of+ x | x <= 0x7F -> f1 $ fromIntegral x+ | x <= 0x07FF ->+ let x1 = fromIntegral $ (x `shiftR` 6) + 0xC0+ x2 = fromIntegral $ (x .&. 0x3F) + 0x80+ in f2 x1 x2+ | x <= 0xFFFF ->+ let x1 = fromIntegral $ (x `shiftR` 12) + 0xE0+ x2 = fromIntegral $ ((x `shiftR` 6) .&. 0x3F) + 0x80+ x3 = fromIntegral $ (x .&. 0x3F) + 0x80+ in f3 x1 x2 x3+ | otherwise ->+ let x1 = fromIntegral $ (x `shiftR` 18) + 0xF0+ x2 = fromIntegral $ ((x `shiftR` 12) .&. 0x3F) + 0x80+ x3 = fromIntegral $ ((x `shiftR` 6) .&. 0x3F) + 0x80+ x4 = fromIntegral $ (x .&. 0x3F) + 0x80+ in f4 x1 x2 x3 x4+{-# INLINE encodeCharUtf8 #-}++-- | /O(1)/. Serialize a Unicode character using the UTF-8 encoding.+--+fromChar :: Char -> Builder+fromChar = fromWriteSingleton writeChar++-- | /O(n)/. Serialize a Unicode 'String' using the UTF-8 encoding.+--+fromString :: String -> Builder+fromString = fromWrite1List writeChar+-- Performance note: ^^^+--+-- fromWrite2List made things slightly worse for the blaze-html benchmarks+-- despite being better when serializing only a list. Probably, the cache is+-- already occupied enough with dealing with the data from Html rendering.+--+++-- | /O(n)/. Serialize a value by 'Show'ing it and UTF-8 encoding the resulting+-- 'String'.+--+fromShow :: Show a => a -> Builder+fromShow = fromString . show++-- | /O(n)/. Serialize a strict Unicode 'TS.Text' value using the UTF-8 encoding.+--+-- Note that this function is currently faster than 'TS.encodeUtf8' provided by+-- "Data.Text.Encoding". Moreover, 'fromText' is also lazy, while 'TL.encodeUtf8'+-- is strict.+--+fromText :: TS.Text -> Builder+fromText = fromString . TS.unpack+{-# INLINE fromText #-}+++-- | /O(n)/. Serialize a lazy Unicode 'TL.Text' value using the UTF-8 encoding.+--+-- Note that this function is currently faster than 'TL.encodeUtf8' provided by+-- "Data.Text.Lazy.Encoding".+--+fromLazyText :: TL.Text -> Builder+fromLazyText = fromString . TL.unpack+{-# INLINE fromLazyText #-}
+ Blaze/ByteString/Builder/Html/Utf8.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Module : Blaze.ByteString.Builder.Html.Utf8+-- Copyright : (c) 2010 Jasper Van der Jeugt & Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- 'Write's and 'Builder's for serializing HTML escaped and UTF-8 encoded+-- characters.+--+-- This module is used by both the 'blaze-html' and the \'hamlet\' HTML+-- templating libraries. If the 'Builder' from 'blaze-builder' replaces the+-- 'Data.Binary.Builder' implementation, this module will most likely keep its+-- place, as it provides a set of very specialized functions.+module Blaze.ByteString.Builder.Html.Utf8+ ( + module Blaze.ByteString.Builder.Char.Utf8++ -- * Writing HTML escaped and UTF-8 encoded characters to a buffer+ , writeHtmlEscapedChar++ -- * Creating Builders from HTML escaped and UTF-8 encoded characters+ , fromHtmlEscapedChar+ , fromHtmlEscapedString+ , fromHtmlEscapedShow+ , fromHtmlEscapedText+ , fromHtmlEscapedLazyText+ ) where++import Data.ByteString.Char8 () -- for the 'IsString' instance of bytesrings++import qualified Data.Text as TS+import qualified Data.Text.Lazy as TL++import Blaze.ByteString.Builder+import Blaze.ByteString.Builder.Char.Utf8++-- | Write a HTML escaped and UTF-8 encoded Unicode character to a bufffer.+--+writeHtmlEscapedChar :: Char -> Write+writeHtmlEscapedChar '<' = writeByteString "<"+writeHtmlEscapedChar '>' = writeByteString ">"+writeHtmlEscapedChar '&' = writeByteString "&"+writeHtmlEscapedChar '"' = writeByteString """+writeHtmlEscapedChar '\'' = writeByteString "'"+writeHtmlEscapedChar c = writeChar c+{-# INLINE writeHtmlEscapedChar #-}++-- | /O(1)./ Serialize a HTML escaped Unicode character using the UTF-8+-- encoding.+--+fromHtmlEscapedChar :: Char -> Builder+fromHtmlEscapedChar = fromWriteSingleton writeHtmlEscapedChar++-- | /O(n)/. Serialize a HTML escaped Unicode 'String' using the UTF-8+-- encoding.+--+fromHtmlEscapedString :: String -> Builder+fromHtmlEscapedString = fromWrite1List writeHtmlEscapedChar++-- | /O(n)/. Serialize a value by 'Show'ing it and then, HTML escaping and+-- UTF-8 encoding the resulting 'String'.+--+fromHtmlEscapedShow :: Show a => a -> Builder+fromHtmlEscapedShow = fromHtmlEscapedString . show+++-- | /O(n)/. Serialize a HTML escaped strict Unicode 'TS.Text' value using the+-- UTF-8 encoding.+--+fromHtmlEscapedText :: TS.Text -> Builder+fromHtmlEscapedText = fromHtmlEscapedString . TS.unpack++-- | /O(n)/. Serialize a HTML escaped Unicode 'TL.Text' using the UTF-8 encoding.+--+fromHtmlEscapedLazyText :: TL.Text -> Builder+fromHtmlEscapedLazyText = fromHtmlEscapedString . TL.unpack+
+ Blaze/ByteString/Builder/Int.hs view
@@ -0,0 +1,301 @@+{-# LANGUAGE CPP #-}+-- |+-- Module : Blaze.ByteString.Builder.Int+-- Copyright : (c) 2010 Simon Meier+--+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- 'Write's and 'Builder's for serializing integers.+--+-- See "Blaze.ByteString.Builder.Word" for information about how to best write several+-- integers at once.+--+#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+#include "MachDeps.h"+#endif++module Blaze.ByteString.Builder.Int+ ( + -- * Writing integers to a buffer++ writeInt8++ -- ** Big-endian writes+ , writeInt16be -- :: Int16 -> Write+ , writeInt32be -- :: Int32 -> Write+ , writeInt64be -- :: Int64 -> Write++ -- ** Little-endian writes+ , writeInt16le -- :: Int16 -> Write+ , writeInt32le -- :: Int32 -> Write+ , writeInt64le -- :: Int64 -> Write++ -- ** Host-endian writes+ , writeInthost -- :: Int -> Write+ , writeInt16host -- :: Int16 -> Write+ , writeInt32host -- :: Int32 -> Write+ , writeInt64host -- :: Int64 -> Write++ -- * Creating builders from integers+ + -- | We provide serialization functions both for singleton integers as well as+ -- for lists of integers. Using these list serialization functions is /much/ faster+ -- than using @mconcat . map fromInt/<n/>@, as the list serialization+ -- functions use a tighter inner loop and a chunked write like it is+ -- provided by functions such as 'fromWrite2List'.++ , fromInt8+ , fromInt8s++ -- ** Big-endian serialization+ , fromInt16be -- :: Int16 -> Builder+ , fromInt32be -- :: Int32 -> Builder+ , fromInt64be -- :: Int64 -> Builder+ , fromInt32sbe -- :: [Int32] -> Builder+ , fromInt16sbe -- :: [Int16] -> Builder+ , fromInt64sbe -- :: [Int64] -> Builder++ -- ** Little-endian serialization+ , fromInt16le -- :: Int16 -> Builder+ , fromInt32le -- :: Int32 -> Builder+ , fromInt64le -- :: Int64 -> Builder+ , fromInt16sle -- :: [Int16] -> Builder+ , fromInt32sle -- :: [Int32] -> Builder+ , fromInt64sle -- :: [Int64] -> Builder++ -- ** Host-endian serialization+ , fromInthost -- :: Int -> Builder+ , fromInt16host -- :: Int16 -> Builder+ , fromInt32host -- :: Int32 -> Builder+ , fromInt64host -- :: Int64 -> Builder+ , fromIntshost -- :: [Int] -> Builder+ , fromInt16shost -- :: [Int16] -> Builder+ , fromInt32shost -- :: [Int32] -> Builder+ , fromInt64shost -- :: [Int64] -> Builder++ ) where++import Blaze.ByteString.Builder.Internal+import Blaze.ByteString.Builder.Write+import Blaze.ByteString.Builder.Word++import Foreign++------------------------------------------------------------------------------+-- Int writes+--------------+--+-- we rely on 'fromIntegral' to do a loss-less conversion to the corresponding+-- 'Word' type+-- +------------------------------------------------------------------------------+++-- | Write a single signed byte.+--+writeInt8 :: Int8 -> Write+writeInt8 = writeWord8 . fromIntegral+{-# INLINE writeInt8 #-}++-- | Write an 'Int16' in big endian format.+writeInt16be :: Int16 -> Write+writeInt16be = writeWord16be . fromIntegral+{-# INLINE writeInt16be #-}++-- | Write an 'Int16' in little endian format.+writeInt16le :: Int16 -> Write+writeInt16le = writeWord16le . fromIntegral+{-# INLINE writeInt16le #-}++-- | Write an 'Int32' in big endian format.+writeInt32be :: Int32 -> Write+writeInt32be = writeWord32be . fromIntegral+{-# INLINE writeInt32be #-}++-- | Write an 'Int32' in little endian format.+writeInt32le :: Int32 -> Write+writeInt32le = writeWord32le . fromIntegral+{-# INLINE writeInt32le #-}++-- | Write an 'Int64' in big endian format.+writeInt64be :: Int64 -> Write+writeInt64be = writeWord64be . fromIntegral+{-# INLINE writeInt64be #-}++-- | Write an 'Int64' in little endian format.+writeInt64le :: Int64 -> Write+writeInt64le = writeWord64le . fromIntegral+{-# INLINE writeInt64le #-}+++------------------------------------------------------------------------+-- Unaligned, integer size ops++-- | Write a single native machine 'Int'. The 'Int' is written in host order,+-- host endian form, for the machine you're on. On a 64 bit machine the 'Int'+-- is an 8 byte value, on a 32 bit machine, 4 bytes. Values written this way+-- are not portable to different endian or integer sized machines, without+-- conversion.+--+writeInthost :: Int -> Write+writeInthost w = + Write (sizeOf (undefined :: Int)) (\p -> poke (castPtr p) w)+{-# INLINE writeInthost #-}++-- | Write an 'Int16' in native host order and host endianness.+writeInt16host :: Int16 -> Write+writeInt16host w16 = + Write (sizeOf (undefined :: Int16)) (\p -> poke (castPtr p) w16)+{-# INLINE writeInt16host #-}++-- | Write an 'Int32' in native host order and host endianness.+writeInt32host :: Int32 -> Write+writeInt32host w32 = + Write (sizeOf (undefined :: Int32)) (\p -> poke (castPtr p) w32)+{-# INLINE writeInt32host #-}++-- | Write an 'Int64' in native host order and host endianness.+writeInt64host :: Int64 -> Write+writeInt64host w = + Write (sizeOf (undefined :: Int64)) (\p -> poke (castPtr p) w)+{-# INLINE writeInt64host #-}+++------------------------------------------------------------------------------+-- Builders corresponding to the integer writes+------------------------------------------------------------------------------++-- Single bytes+------------------------------------------------------------------------------++-- | Serialize a single byte.+--+fromInt8 :: Int8 -> Builder+fromInt8 = fromWriteSingleton writeInt8++-- | Serialize a list of bytes.+--+fromInt8s :: [Int8] -> Builder+fromInt8s = fromWrite8List writeInt8+++-- Int16+------------------------------------------------------------------------------++-- | Serialize an 'Int16' in big endian format.+fromInt16be :: Int16 -> Builder+fromInt16be = fromWriteSingleton writeInt16be +{-# INLINE fromInt16be #-}++-- | Serialize a list of 'Int16's in big endian format.+fromInt16sbe :: [Int16] -> Builder+fromInt16sbe = fromWrite4List writeInt16be +{-# INLINE fromInt16sbe #-}++-- | Serialize an 'Int16' in little endian format.+fromInt16le :: Int16 -> Builder+fromInt16le = fromWriteSingleton writeInt16le +{-# INLINE fromInt16le #-}++-- | Serialize a list of 'Int16's in little endian format.+fromInt16sle :: [Int16] -> Builder+fromInt16sle = fromWrite4List writeInt16le +{-# INLINE fromInt16sle #-}+++-- Int32+-----------------------------------------------------------------------------++-- | Serialize an 'Int32' in big endian format.+fromInt32be :: Int32 -> Builder+fromInt32be = fromWriteSingleton writeInt32be +{-# INLINE fromInt32be #-}++-- | Serialize a list of 'Int32's in big endian format.+fromInt32sbe :: [Int32] -> Builder+fromInt32sbe = fromWrite2List writeInt32be +{-# INLINE fromInt32sbe #-}++-- | Serialize an 'Int32' in little endian format.+fromInt32le :: Int32 -> Builder+fromInt32le = fromWriteSingleton writeInt32le +{-# INLINE fromInt32le #-}++-- | Serialize a list of 'Int32's in little endian format.+fromInt32sle :: [Int32] -> Builder+fromInt32sle = fromWrite2List writeInt32le +{-# INLINE fromInt32sle #-}++-- | Serialize an 'Int64' in big endian format.+fromInt64be :: Int64 -> Builder+fromInt64be = fromWriteSingleton writeInt64be +{-# INLINE fromInt64be #-}++-- | Serialize a list of 'Int64's in big endian format.+fromInt64sbe :: [Int64] -> Builder+fromInt64sbe = fromWrite1List writeInt64be +{-# INLINE fromInt64sbe #-}++-- | Serialize an 'Int64' in little endian format.+fromInt64le :: Int64 -> Builder+fromInt64le = fromWriteSingleton writeInt64le +{-# INLINE fromInt64le #-}++-- | Serialize a list of 'Int64's in little endian format.+fromInt64sle :: [Int64] -> Builder+fromInt64sle = fromWrite1List writeInt64le +{-# INLINE fromInt64sle #-}+++------------------------------------------------------------------------+-- Unaligned, integer size ops++-- | Serialize a single native machine 'Int'. The 'Int' is serialized in host+-- order, host endian form, for the machine you're on. On a 64 bit machine the+-- 'Int' is an 8 byte value, on a 32 bit machine, 4 bytes. Values written this+-- way are not portable to different endian or integer sized machines, without+-- conversion.+--+fromInthost :: Int -> Builder+fromInthost = fromWriteSingleton writeInthost +{-# INLINE fromInthost #-}++-- | Serialize a list of 'Int's.+-- See 'fromInthost' for usage considerations.+fromIntshost :: [Int] -> Builder+fromIntshost = fromWrite2List writeInthost +{-# INLINE fromIntshost #-}++-- | Write an 'Int16' in native host order and host endianness.+fromInt16host :: Int16 -> Builder+fromInt16host = fromWriteSingleton writeInt16host +{-# INLINE fromInt16host #-}++-- | Write a list of 'Int16's in native host order and host endianness.+fromInt16shost :: [Int16] -> Builder+fromInt16shost = fromWrite4List writeInt16host +{-# INLINE fromInt16shost #-}++-- | Write an 'Int32' in native host order and host endianness.+fromInt32host :: Int32 -> Builder+fromInt32host = fromWriteSingleton writeInt32host +{-# INLINE fromInt32host #-}++-- | Write a list of 'Int32's in native host order and host endianness.+fromInt32shost :: [Int32] -> Builder+fromInt32shost = fromWrite2List writeInt32host +{-# INLINE fromInt32shost #-}++-- | Write an 'Int64' in native host order and host endianness.+fromInt64host :: Int64 -> Builder+fromInt64host = fromWriteSingleton writeInt64host+{-# INLINE fromInt64host #-}++-- | Write a list of 'Int64's in native host order and host endianness.+fromInt64shost :: [Int64] -> Builder+fromInt64shost = fromWrite1List writeInt64host+{-# INLINE fromInt64shost #-}
+ Blaze/ByteString/Builder/Internal.hs view
@@ -0,0 +1,441 @@+{-# LANGUAGE CPP, BangPatterns #-}+-- |+-- Module : Blaze.ByteString.Builder.Internal+-- Copyright : (c) 2010 Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- Implementation of the 'Builder' monoid.+--+-- A standard library user must never import this module directly. Instead, he+-- should import "Blaze.ByteString.Builder", which re-exports the 'Builder' type and+-- its associated public functions defined in this module.+--+-- Developers of other libraries may import this module to gain access to the+-- internal representation of builders. For example, in some cases, creating a+-- 'Builder' with a custom low-level 'BuildStep' may improve performance+-- considerably compared to the creating it using the public 'Builder'+-- combinators (e.g., @'fromWrite1List'@ in "Blaze.ByteString.Builder.Write").+-- Another example, is the use of 'ModifyChunks' to efficiently wire the+-- 'Builder' type with another library that generates lazy bytestrings.+--+-- In any case, whenever you import this module you /must/ reference the full+-- version of the 'blaze-builder' package in your cabal file, as the+-- implementation and the guarantees given in this file may change in any+-- version! The release notes will tell, if this was the case.+--+module Blaze.ByteString.Builder.Internal+ ( + -- * The @Builder@ type+ Builder(..)+ , BuildStep+ , BuildSignal(..)++ -- * Flushing the buffer+ , flush++ -- * Executing builders+ , toLazyByteStringWith+ , toLazyByteString+ , toByteString+ , toByteStringIOWith+ , toByteStringIO++ -- * Default sizes+ , defaultBufferSize+ , defaultMinimalBufferSize+ , defaultMaximalCopySize+ ) where+++import Foreign+import Data.Monoid+import Control.Monad (unless)+import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L++#ifdef BYTESTRING_IN_BASE+import Data.ByteString.Base (inlinePerformIO)+import qualified Data.ByteString.Base as S+import qualified Data.ByteString.Lazy.Base as L -- FIXME: is this the right module for access to 'Chunks'?+#else+import Data.ByteString.Internal (inlinePerformIO)+import qualified Data.ByteString.Internal as S+import qualified Data.ByteString.Lazy.Internal as L+#endif+++------------------------------------------------------------------------------+-- The Builder type+------------------------------------------------------------------------------++-- | Intuitively, a builder denotes the construction of a lazy bytestring. +--+-- Builders can be created from primitive buffer manipulations using the+-- @'Write'@ abstraction provided by in "Blaze.ByteString.Builder.Write". However for+-- many Haskell values, there exist predefined functions doing that already. +-- For example, UTF-8 encoding 'Char' and 'String' values is provided by the+-- functions in "Blaze.ByteString.Builder.Char.Utf8". Concatenating builders is done+-- using their 'Monoid' instance.+--+-- Semantically, builders are nothing special. They just denote a sequence of+-- bytes. However, their representation is chosen such that this sequence of+-- bytes can be efficiently (in terms of CPU cycles) computed in an+-- incremental, chunk-wise fashion such that the average chunk-size is large.+-- Note that the large average chunk size allows to make good use of cache+-- prefetching in later processing steps (e.g. compression) or to reduce the+-- sytem call overhead when writing the resulting lazy bytestring to a file or+-- sending it over the network.+--+-- For precisely understanding the performance of a specific 'Builder',+-- benchmarking is unavoidable. Moreover, it also helps to understand the+-- implementation of builders and the predefined combinators. This should be+-- amenable to the average Haskell programmer by reading the source code of+-- "Blaze.ByteString.Builder.Internal" and the other modules of this library. +--+-- The guiding implementation principle was to reduce the abstraction cost per+-- output byte. We use continuation passing to achieve a constant time append.+-- The output buffer is filled by the individual builders as long as possible.+-- They call each other directly when they are done and control is returned to+-- the driver (e.g., 'toLazyByteString') only when the buffer is full, a+-- bytestring needs to be inserted directly, or no more bytes can be written.+-- We also try to take the pressure off the cache by moving variables as far+-- out of loops as possible. This leads to some duplication of code, but+-- results in sometimes dramatic increases in performance. For example, see the+-- @'fromWord8s'@ function in "Blaze.ByteString.Builder.Word".+--+newtype Builder = Builder (BuildStep -> BuildStep)++-- | A 'BuildSignal' signals to the driver of the 'Builder' execution the next+-- step to be taken.+--+data BuildSignal =+ -- | @Done pf@ signals that the 'BuildStep' is finished and data has been+ -- written up to the next free byte @pf@.+ Done {-# UNPACK #-} !(Ptr Word8) + -- | @BufferFull newSize pf nextStep@ signals that the buffer is full and+ -- data has been written up to the next free byte @pf@. Moreover, the next+ -- build step to be executed @nextStep@ requires a buffer of at least size+ -- @newSize@ to execute successfully.+ --+ -- A driver /must/ guarantee that the buffer used to call @nextStep@ is at+ -- least of size @newSize@.+ | BufferFull+ {-# UNPACK #-} !Int+ {-# UNPACK #-} !(Ptr Word8)+ !BuildStep+ -- | @ModifyChunks pf fbs nextStep@ signals that the data written up to the+ -- next free byte @pf@ must be output and the remaining lazy bytestring that+ -- is produced by executing @nextStep@ must be modified using the function+ -- @fbs@.+ --+ -- This signal is used to insert bytestrings directly into the output stream.+ -- It can also be used to efficiently hand over control to another library+ -- for generating streams of strict bytestrings.+ | ModifyChunks+ {-# UNPACK #-} !(Ptr Word8) + !(L.ByteString -> L.ByteString) + !BuildStep++-- | A 'BuildStep' fills a buffer from the given start pointer as long as+-- possible and returns control to the caller using a 'BuildSignal', once it is+-- required.+--+type BuildStep = Ptr Word8 -- ^ Pointer to the next free byte in the+ -- buffer. A 'BuildStep' must start writing+ -- its data from this address.+ -> Ptr Word8 -- ^ Pointer to the first byte /after/ the+ -- buffer. A 'BuildStep' must never write+ -- data at or after this address.+ -> IO BuildSignal -- ^ Signal to the driver about the next step+ -- to be taken.++instance Monoid Builder where+ mempty = Builder id+ {-# INLINE mempty #-}+ mappend (Builder f) (Builder g) = Builder $ f . g+ {-# INLINE mappend #-}+ mconcat = foldr mappend mempty+ {-# INLINE mconcat #-}++------------------------------------------------------------------------------+-- Internal global constants.+------------------------------------------------------------------------------++-- | Default size (~32kb) for the buffer that becomes a chunk of the output+-- stream once it is filled.+--+defaultBufferSize :: Int+defaultBufferSize = 32 * 1024 - overhead -- Copied from Data.ByteString.Lazy.+ where overhead = 2 * sizeOf (undefined :: Int)++-- | The minimal length (~4kb) a buffer must have before filling it and+-- outputting it as a chunk of the output stream. +--+-- This size determines when a buffer is spilled after a 'flush' or a direct+-- bytestring insertion. It is also the size of the first chunk generated by+-- 'toLazyByteString'.+defaultMinimalBufferSize :: Int+defaultMinimalBufferSize = 4 * 1024 - overhead+ where overhead = 2 * sizeOf (undefined :: Int)++-- | The default length (64) for the first buffer to be allocated when+-- converting a 'Builder' to a lazy bytestring. +--+-- See 'toLazyByteStringWith' for further explanation.+defaultFirstBufferSize :: Int+defaultFirstBufferSize = 64++-- | The maximal number of bytes for that copying is cheaper than direct+-- insertion into the output stream. This takes into account the fragmentation+-- that may occur in the output buffer due to the early 'flush' implied by the+-- direct bytestring insertion.+--+-- @'defaultMaximalCopySize' = 2 * 'defaultMinimalBufferSize'@+--+defaultMaximalCopySize :: Int+defaultMaximalCopySize = 2 * defaultMinimalBufferSize++------------------------------------------------------------------------------+-- Flushing and running a Builder+------------------------------------------------------------------------------+++-- | Output all data written in the current buffer and start a new chunk.+--+-- The use uf this function depends on how the resulting bytestrings are+-- consumed. 'flush' is possibly not very useful in non-interactive scenarios.+-- However, it is kept for compatibility with the builder provided by+-- Data.Binary.Builder.+--+-- When using 'toLazyByteString' to extract a lazy 'L.ByteString' from a+-- 'Builder', this means that a new chunk will be started in the resulting lazy+-- 'L.ByteString'. The remaining part of the buffer is spilled, if the+-- reamining free space is smaller than the minimal desired buffer size.+--+flush :: Builder+flush = Builder $ \k pf _ -> return $ ModifyChunks pf id k++-- | Run a 'Builder' with the given buffer sizes.+--+-- Use this function for integrating the 'Builder' type with other libraries+-- that generate lazy bytestrings.+--+-- Note that the builders should guarantee that on average the desired chunk+-- size is attained. Builders may decide to start a new buffer and not+-- completely fill the existing buffer, if this is faster. However, they should+-- not spill too much of the buffer, if they cannot compensate for it.+--+-- A call @toLazyByteStringWith bufSize minBufSize firstBufSize@ will generate+-- a lazy bytestring according to the following strategy. First, we allocate+-- a buffer of size @firstBufSize@ and start filling it. If it overflows, we+-- allocate a buffer of size @minBufSize@ and copy the first buffer to it in+-- order to avoid generating a too small chunk. Finally, every next buffer will+-- be of size @bufSize@. This, slow startup strategy is required to achieve+-- good speed for short (<200 bytes) resulting bytestrings, as for them the+-- allocation cost is of a large buffer cannot be compensated. Moreover, this+-- strategy also allows us to avoid spilling too much memory for short+-- resulting bytestrings.+--+-- Note that setting @firstBufSize >= minBufSize@ implies that the first buffer+-- is no longer copied but allocated and filled directly. Hence, setting+-- @firstBufSize = bufSize@ means that all chunks will use an underlying buffer+-- of size @bufSize@. This is recommended, if you know that you always output+-- more than @minBufSize@ bytes.+toLazyByteStringWith + :: Int -- ^ Buffer size (upper-bounds the resulting chunk size).+ -> Int -- ^ Minimal free buffer space for continuing filling+ -- the same buffer after a 'flush' or a direct bytestring+ -- insertion. This corresponds to the minimal desired+ -- chunk size.+ -> Int -- ^ Size of the first buffer to be used and copied for+ -- larger resulting sequences+ -> Builder -- ^ Builder to run.+ -> L.ByteString -- ^ Lazy bytestring to output after the builder is+ -- finished.+ -> L.ByteString -- ^ Resulting lazy bytestring+toLazyByteStringWith bufSize minBufSize firstBufSize (Builder b) k = + inlinePerformIO $ fillFirstBuffer (b finalStep)+ where+ finalStep pf _ = return $ Done pf+ -- fill a first very small buffer, if we need more space then copy it+ -- to the new buffer of size 'minBufSize'. This way we don't pay the+ -- allocation cost of the big 'bufSize' buffer, when outputting only+ -- small sequences.+ fillFirstBuffer !step0+ | minBufSize <= firstBufSize = fillNewBuffer firstBufSize step0+ | otherwise = do+ fpbuf <- S.mallocByteString firstBufSize+ withForeignPtr fpbuf $ \pf -> do+ let !pe = pf `plusPtr` firstBufSize+ mkbs pf' = S.PS fpbuf 0 (pf' `minusPtr` pf)+ {-# INLINE mkbs #-}+ next <- step0 pf pe+ case next of+ Done pf' + | pf' == pf -> return k+ | otherwise -> return $ L.Chunk (mkbs pf') k++ BufferFull newSize pf' nextStep -> do+ let !l = pf' `minusPtr` pf+ fillNewBuffer (max (l + newSize) minBufSize) $+ \pfNew peNew -> do+ copyBytes pfNew pf l+ nextStep (pfNew `plusPtr` l) peNew+ + ModifyChunks pf' bsk nextStep + | pf' == pf ->+ return $ bsk (inlinePerformIO $ fillNewBuffer bufSize nextStep)+ | otherwise ->+ return $ L.Chunk (mkbs pf')+ (bsk (inlinePerformIO $ fillNewBuffer bufSize nextStep))+ + -- allocate and fill a new buffer+ fillNewBuffer !size !step0 = do+ fpbuf <- S.mallocByteString size+ withForeignPtr fpbuf $ fillBuffer fpbuf+ where+ fillBuffer fpbuf !pbuf = fill pbuf step0+ where+ !pe = pbuf `plusPtr` size+ fill !pf !step = do+ next <- step pf pe+ let mkbs pf' = S.PS fpbuf (pf `minusPtr` pbuf) (pf' `minusPtr` pf)+ {-# INLINE mkbs #-}+ case next of+ Done pf' + | pf' == pf -> return k+ | otherwise -> return $ L.Chunk (mkbs pf') k++ BufferFull newSize pf' nextStep ->+ return $ L.Chunk (mkbs pf')+ (inlinePerformIO $ + fillNewBuffer (max newSize bufSize) nextStep)+ + ModifyChunks pf' bsk nextStep+ | pf' == pf ->+ return $ bsk (inlinePerformIO $ fill pf' nextStep)+ | minBufSize < pe `minusPtr` pf' ->+ return $ L.Chunk (mkbs pf')+ (bsk (inlinePerformIO $ fill pf' nextStep))+ | otherwise ->+ return $ L.Chunk (mkbs pf')+ (bsk (inlinePerformIO $ fillNewBuffer bufSize nextStep))+++-- | Extract the lazy 'L.ByteString' from the builder by running it with default+-- buffer sizes. Use this function, if you do not have any special+-- considerations with respect to buffer sizes.+--+-- @ 'toLazyByteString' b = 'toLazyByteStringWith' 'defaultBufferSize' 'defaultMinimalBufferSize' 'defaultFirstBufferSize' b L.empty@+--+-- Note that @'toLazyByteString'@ is a 'Monoid' homomorphism.+--+-- > toLazyByteString mempty == mempty+-- > toLazyByteString (x `mappend` y) == toLazyByteString x `mappend` toLazyByteString y+--+-- However, in the second equation, the left-hand-side is generally faster to+-- execute.+--+toLazyByteString :: Builder -> L.ByteString+toLazyByteString b = toLazyByteStringWith + defaultBufferSize defaultMinimalBufferSize defaultFirstBufferSize b L.empty+{-# INLINE toLazyByteString #-}++-- | Pack the chunks of a lazy bytestring into a single strict bytestring.+packChunks :: L.ByteString -> S.ByteString+packChunks lbs = do+ S.unsafeCreate (fromIntegral $ L.length lbs) (copyChunks lbs)+ where+ copyChunks !L.Empty !_pf = return ()+ copyChunks !(L.Chunk (S.PS fpbuf o l) lbs') !pf = do+ withForeignPtr fpbuf $ \pbuf ->+ copyBytes pf (pbuf `plusPtr` o) l+ copyChunks lbs' (pf `plusPtr` l)++-- | Run the builder to construct a strict bytestring containing the sequence+-- of bytes denoted by the builder. This is done by first serializing to a lazy bytestring and then packing its+-- chunks to a appropriately sized strict bytestring.+--+-- > toByteString = packChunks . toLazyByteString+--+-- Note that @'toByteString'@ is a 'Monoid' homomorphism.+--+-- > toByteString mempty == mempty+-- > toByteString (x `mappend` y) == toByteString x `mappend` toByteString y+--+-- However, in the second equation, the left-hand-side is generally faster to+-- execute.+--+toByteString :: Builder -> S.ByteString+toByteString = packChunks . toLazyByteString+++-- | @toByteStringIOWith bufSize io b@ runs the builder @b@ with a buffer of+-- at least the size @bufSize@ and executes the 'IO' action @io@ whenever the+-- buffer is full.+--+-- Compared to 'toLazyByteStringWith' this function requires less allocation,+-- as the output buffer is only allocated once at the start of the+-- serialization and whenever something bigger than the current buffer size has+-- to be copied into the buffer, which should happen very seldomly for the+-- default buffer size of 32kb. Hence, the pressure on the garbage collector is+-- reduced, which can be an advantage when building long sequences of bytes.+--+toByteStringIOWith :: Int -- ^ Buffer size (upper bounds+ -- the number of bytes forced+ -- per call to the 'IO' action).+ -> (S.ByteString -> IO ()) -- ^ 'IO' action to execute per+ -- full buffer, which is+ -- referenced by a strict+ -- 'S.ByteString'.+ -> Builder -- ^ 'Builder' to run.+ -> IO () -- ^ Resulting 'IO' action.+toByteStringIOWith bufSize io (Builder b) = + fillNewBuffer bufSize (b finalStep)+ where+ finalStep pf _ = return $ Done pf++ fillNewBuffer !size !step0 = do+ S.mallocByteString size >>= fillBuffer+ where+ fillBuffer fpbuf = fill step0+ where+ -- safe because the constructed ByteString references the foreign+ -- pointer AFTER its buffer was filled.+ pf = unsafeForeignPtrToPtr fpbuf+ fill !step = do+ next <- step pf (pf `plusPtr` size)+ case next of+ Done pf' ->+ unless (pf' == pf) (io $ S.PS fpbuf 0 (pf' `minusPtr` pf))++ BufferFull newSize pf' nextStep -> do+ io $ S.PS fpbuf 0 (pf' `minusPtr` pf)+ if bufSize < newSize+ then fillNewBuffer newSize nextStep+ else fill nextStep+ + ModifyChunks pf' bsk nextStep -> do+ unless (pf' == pf) (io $ S.PS fpbuf 0 (pf' `minusPtr` pf))+ -- was: mapM_ io $ L.toChunks (bsk L.empty)+ L.foldrChunks (\bs -> (io bs >>)) (return ()) (bsk L.empty)+ fill nextStep++-- | Run the builder with a 'defaultBufferSize'd buffer and execute the given+-- 'IO' action whenever the buffer is full or gets flushed.+--+-- @ 'toByteStringIO' = 'toByteStringIOWith' 'defaultBufferSize'@+--+-- This is a 'Monoid' homomorphism in the following sense.+--+-- > toByteStringIO io mempty == return ()+-- > toByteStringIO io (x `mappend` y) == toByteStringIO io x >> toByteStringIO io y+--+toByteStringIO :: (S.ByteString -> IO ()) -> Builder -> IO ()+toByteStringIO = toByteStringIOWith defaultBufferSize+{-# INLINE toByteStringIO #-}+
+ Blaze/ByteString/Builder/Word.hs view
@@ -0,0 +1,436 @@+{-# OPTIONS_GHC -fglasgow-exts #-}+-- for unboxed shifts++{-# LANGUAGE CPP #-}+-- |+-- Module : Blaze.ByteString.Builder.Word+-- Copyright : (c) 2010 Jasper Van der Jeugt & Simon Meier+--+-- Original serialization code from 'Data.Binary.Builder':+-- (c) Lennart Kolmodin, Ross Patterson+--+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- 'Write's and 'Builder's for serializing words.+--+-- Note that for serializing a three tuple @(x,y,z)@ of bytes (or other word+-- values) you should use the expression +--+-- > fromWrite $ writeWord8 x `mappend` writeWord8 y `mappend` writeWord z+--+-- instead of+--+-- > fromWord8 x `mappend` fromWord8 y `mappend` fromWord z+--+-- The first expression will result in a single atomic write of three bytes,+-- while the second expression will check for each byte, if there is free space+-- left in the output buffer. Coalescing these checks can improve performance+-- quite a bit, as long as you use it sensibly.+--+#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+#include "MachDeps.h"+#endif++module Blaze.ByteString.Builder.Word+ ( + -- * Writing words to a buffer++ writeWord8++ -- ** Big-endian writes+ , writeWord16be -- :: Word16 -> Write+ , writeWord32be -- :: Word32 -> Write+ , writeWord64be -- :: Word64 -> Write++ -- ** Little-endian writes+ , writeWord16le -- :: Word16 -> Write+ , writeWord32le -- :: Word32 -> Write+ , writeWord64le -- :: Word64 -> Write++ -- ** Host-endian writes+ , writeWordhost -- :: Word -> Write+ , writeWord16host -- :: Word16 -> Write+ , writeWord32host -- :: Word32 -> Write+ , writeWord64host -- :: Word64 -> Write++ -- * Creating builders from words+ + -- | We provide serialization functions both for singleton words as well as+ -- for lists of words. Using these list serialization functions is /much/ faster+ -- than using @mconcat . map fromWord/<n/>@, as the list serialization+ -- functions use a tighter inner loop and a chunked write like it is+ -- provided by functions such as 'fromWrite2List'.++ , fromWord8+ , fromWord8s++ -- ** Big-endian serialization+ , fromWord16be -- :: Word16 -> Builder+ , fromWord32be -- :: Word32 -> Builder+ , fromWord64be -- :: Word64 -> Builder+ , fromWord32sbe -- :: [Word32] -> Builder+ , fromWord16sbe -- :: [Word16] -> Builder+ , fromWord64sbe -- :: [Word64] -> Builder++ -- ** Little-endian serialization+ , fromWord16le -- :: Word16 -> Builder+ , fromWord32le -- :: Word32 -> Builder+ , fromWord64le -- :: Word64 -> Builder+ , fromWord16sle -- :: [Word16] -> Builder+ , fromWord32sle -- :: [Word32] -> Builder+ , fromWord64sle -- :: [Word64] -> Builder++ -- ** Host-endian serialization+ , fromWordhost -- :: Word -> Builder+ , fromWord16host -- :: Word16 -> Builder+ , fromWord32host -- :: Word32 -> Builder+ , fromWord64host -- :: Word64 -> Builder+ , fromWordshost -- :: [Word] -> Builder+ , fromWord16shost -- :: [Word16] -> Builder+ , fromWord32shost -- :: [Word32] -> Builder+ , fromWord64shost -- :: [Word64] -> Builder++ ) where++import Blaze.ByteString.Builder.Internal+import Blaze.ByteString.Builder.Write++import Foreign++#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+import GHC.Base+import GHC.Word (Word32(..),Word16(..),Word64(..))++#if WORD_SIZE_IN_BITS < 64 && __GLASGOW_HASKELL__ >= 608+import GHC.Word (uncheckedShiftRL64#)+#endif+#else+import Data.Word+#endif++------------------------------------------------------------------------------+-- Word writes+--------------+--+-- Based upon the 'putWordX' functions from "Data.Binary.Builder" from the+-- 'binary' package.+-- +------------------------------------------------------------------------------+++-- | Write a single byte.+--+writeWord8 :: Word8 -> Write+writeWord8 x = Write 1 (\pf -> poke pf x)+{-# INLINE writeWord8 #-}++--+-- We rely on the fromIntegral to do the right masking for us.+-- The inlining here is critical, and can be worth 4x performance+--++-- | Write a 'Word16' in big endian format.+writeWord16be :: Word16 -> Write+writeWord16be w = Write 2 $ \p -> do+ poke p (fromIntegral (shiftr_w16 w 8) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (w) :: Word8)+{-# INLINE writeWord16be #-}++-- | Write a 'Word16' in little endian format.+writeWord16le :: Word16 -> Write+writeWord16le w = Write 2 $ \p -> do+ poke p (fromIntegral (w) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (shiftr_w16 w 8) :: Word8)+{-# INLINE writeWord16le #-}++-- writeWord16le w16 = Write 2 (\p -> poke (castPtr p) w16)++-- | Write a 'Word32' in big endian format.+writeWord32be :: Word32 -> Write+writeWord32be w = Write 4 $ \p -> do+ poke p (fromIntegral (shiftr_w32 w 24) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 w 16) :: Word8)+ poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 w 8) :: Word8)+ poke (p `plusPtr` 3) (fromIntegral (w) :: Word8)+{-# INLINE writeWord32be #-}++-- | Write a 'Word32' in little endian format.+writeWord32le :: Word32 -> Write+writeWord32le w = Write 4 $ \p -> do+ poke p (fromIntegral (w) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 w 8) :: Word8)+ poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 w 16) :: Word8)+ poke (p `plusPtr` 3) (fromIntegral (shiftr_w32 w 24) :: Word8)+{-# INLINE writeWord32le #-}++-- on a little endian machine:+-- writeWord32le w32 = Write 4 (\p -> poke (castPtr p) w32)++-- | Write a 'Word64' in big endian format.+writeWord64be :: Word64 -> Write+#if WORD_SIZE_IN_BITS < 64+--+-- To avoid expensive 64 bit shifts on 32 bit machines, we cast to+-- Word32, and write that+--+writeWord64be w =+ let a = fromIntegral (shiftr_w64 w 32) :: Word32+ b = fromIntegral w :: Word32+ in Write 8 $ \p -> do+ poke p (fromIntegral (shiftr_w32 a 24) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 a 16) :: Word8)+ poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 a 8) :: Word8)+ poke (p `plusPtr` 3) (fromIntegral (a) :: Word8)+ poke (p `plusPtr` 4) (fromIntegral (shiftr_w32 b 24) :: Word8)+ poke (p `plusPtr` 5) (fromIntegral (shiftr_w32 b 16) :: Word8)+ poke (p `plusPtr` 6) (fromIntegral (shiftr_w32 b 8) :: Word8)+ poke (p `plusPtr` 7) (fromIntegral (b) :: Word8)+#else+writeWord64be w = Write 8 $ \p -> do+ poke p (fromIntegral (shiftr_w64 w 56) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (shiftr_w64 w 48) :: Word8)+ poke (p `plusPtr` 2) (fromIntegral (shiftr_w64 w 40) :: Word8)+ poke (p `plusPtr` 3) (fromIntegral (shiftr_w64 w 32) :: Word8)+ poke (p `plusPtr` 4) (fromIntegral (shiftr_w64 w 24) :: Word8)+ poke (p `plusPtr` 5) (fromIntegral (shiftr_w64 w 16) :: Word8)+ poke (p `plusPtr` 6) (fromIntegral (shiftr_w64 w 8) :: Word8)+ poke (p `plusPtr` 7) (fromIntegral (w) :: Word8)+#endif+{-# INLINE writeWord64be #-}++-- | Write a 'Word64' in little endian format.+writeWord64le :: Word64 -> Write++#if WORD_SIZE_IN_BITS < 64+writeWord64le w =+ let b = fromIntegral (shiftr_w64 w 32) :: Word32+ a = fromIntegral w :: Word32+ in Write 8 $ \p -> do+ poke (p) (fromIntegral (a) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 a 8) :: Word8)+ poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 a 16) :: Word8)+ poke (p `plusPtr` 3) (fromIntegral (shiftr_w32 a 24) :: Word8)+ poke (p `plusPtr` 4) (fromIntegral (b) :: Word8)+ poke (p `plusPtr` 5) (fromIntegral (shiftr_w32 b 8) :: Word8)+ poke (p `plusPtr` 6) (fromIntegral (shiftr_w32 b 16) :: Word8)+ poke (p `plusPtr` 7) (fromIntegral (shiftr_w32 b 24) :: Word8)+#else+writeWord64le w = Write 8 $ \p -> do+ poke p (fromIntegral (w) :: Word8)+ poke (p `plusPtr` 1) (fromIntegral (shiftr_w64 w 8) :: Word8)+ poke (p `plusPtr` 2) (fromIntegral (shiftr_w64 w 16) :: Word8)+ poke (p `plusPtr` 3) (fromIntegral (shiftr_w64 w 24) :: Word8)+ poke (p `plusPtr` 4) (fromIntegral (shiftr_w64 w 32) :: Word8)+ poke (p `plusPtr` 5) (fromIntegral (shiftr_w64 w 40) :: Word8)+ poke (p `plusPtr` 6) (fromIntegral (shiftr_w64 w 48) :: Word8)+ poke (p `plusPtr` 7) (fromIntegral (shiftr_w64 w 56) :: Word8)+#endif+{-# INLINE writeWord64le #-}++-- on a little endian machine:+-- writeWord64le w64 = Write 8 (\p -> poke (castPtr p) w64)++------------------------------------------------------------------------+-- Unaligned, word size ops++-- | Write a single native machine 'Word'. The 'Word' is written in host order,+-- host endian form, for the machine you're on. On a 64 bit machine the 'Word'+-- is an 8 byte value, on a 32 bit machine, 4 bytes. Values written this way+-- are not portable to different endian or word sized machines, without+-- conversion.+--+writeWordhost :: Word -> Write+writeWordhost w = + Write (sizeOf (undefined :: Word)) (\p -> poke (castPtr p) w)+{-# INLINE writeWordhost #-}++-- | Write a 'Word16' in native host order and host endianness.+writeWord16host :: Word16 -> Write+writeWord16host w16 = + Write (sizeOf (undefined :: Word16)) (\p -> poke (castPtr p) w16)+{-# INLINE writeWord16host #-}++-- | Write a 'Word32' in native host order and host endianness.+writeWord32host :: Word32 -> Write+writeWord32host w32 = + Write (sizeOf (undefined :: Word32)) (\p -> poke (castPtr p) w32)+{-# INLINE writeWord32host #-}++-- | Write a 'Word64' in native host order and host endianness.+writeWord64host :: Word64 -> Write+writeWord64host w = + Write (sizeOf (undefined :: Word64)) (\p -> poke (castPtr p) w)+{-# INLINE writeWord64host #-}++------------------------------------------------------------------------+-- Unchecked shifts++{-# INLINE shiftr_w16 #-}+shiftr_w16 :: Word16 -> Int -> Word16+{-# INLINE shiftr_w32 #-}+shiftr_w32 :: Word32 -> Int -> Word32+{-# INLINE shiftr_w64 #-}+shiftr_w64 :: Word64 -> Int -> Word64++#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+shiftr_w16 (W16# w) (I# i) = W16# (w `uncheckedShiftRL#` i)+shiftr_w32 (W32# w) (I# i) = W32# (w `uncheckedShiftRL#` i)++#if WORD_SIZE_IN_BITS < 64+shiftr_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftRL64#` i)++#if __GLASGOW_HASKELL__ <= 606+-- Exported by GHC.Word in GHC 6.8 and higher+foreign import ccall unsafe "stg_uncheckedShiftRL64"+ uncheckedShiftRL64# :: Word64# -> Int# -> Word64#+#endif++#else+shiftr_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftRL#` i)+#endif++#else+shiftr_w16 = shiftR+shiftr_w32 = shiftR+shiftr_w64 = shiftR+#endif+++------------------------------------------------------------------------------+-- Builders corresponding to the word writes+------------------------------------------------------------------------------++-- Single bytes+------------------------------------------------------------------------------++-- | Serialize a single byte.+--+fromWord8 :: Word8 -> Builder+fromWord8 = fromWriteSingleton writeWord8++-- | Serialize a list of bytes.+--+fromWord8s :: [Word8] -> Builder+fromWord8s = fromWrite8List writeWord8+++-- Word16+------------------------------------------------------------------------------++-- | Serialize a 'Word16' in big endian format.+fromWord16be :: Word16 -> Builder+fromWord16be = fromWriteSingleton writeWord16be +{-# INLINE fromWord16be #-}++-- | Serialize a list of 'Word16's in big endian format.+fromWord16sbe :: [Word16] -> Builder+fromWord16sbe = fromWrite4List writeWord16be +{-# INLINE fromWord16sbe #-}++-- | Serialize a 'Word16' in little endian format.+fromWord16le :: Word16 -> Builder+fromWord16le = fromWriteSingleton writeWord16le +{-# INLINE fromWord16le #-}++-- | Serialize a list of 'Word16's in little endian format.+fromWord16sle :: [Word16] -> Builder+fromWord16sle = fromWrite4List writeWord16le +{-# INLINE fromWord16sle #-}+++-- Word32+-----------------------------------------------------------------------------++-- | Serialize a 'Word32' in big endian format.+fromWord32be :: Word32 -> Builder+fromWord32be = fromWriteSingleton writeWord32be +{-# INLINE fromWord32be #-}++-- | Serialize a list of 'Word32's in big endian format.+fromWord32sbe :: [Word32] -> Builder+fromWord32sbe = fromWrite2List writeWord32be +{-# INLINE fromWord32sbe #-}++-- | Serialize a 'Word32' in little endian format.+fromWord32le :: Word32 -> Builder+fromWord32le = fromWriteSingleton writeWord32le +{-# INLINE fromWord32le #-}++-- | Serialize a list of 'Word32's in little endian format.+fromWord32sle :: [Word32] -> Builder+fromWord32sle = fromWrite2List writeWord32le +{-# INLINE fromWord32sle #-}++-- | Serialize a 'Word64' in big endian format.+fromWord64be :: Word64 -> Builder+fromWord64be = fromWriteSingleton writeWord64be +{-# INLINE fromWord64be #-}++-- | Serialize a list of 'Word64's in big endian format.+fromWord64sbe :: [Word64] -> Builder+fromWord64sbe = fromWrite1List writeWord64be +{-# INLINE fromWord64sbe #-}++-- | Serialize a 'Word64' in little endian format.+fromWord64le :: Word64 -> Builder+fromWord64le = fromWriteSingleton writeWord64le +{-# INLINE fromWord64le #-}++-- | Serialize a list of 'Word64's in little endian format.+fromWord64sle :: [Word64] -> Builder+fromWord64sle = fromWrite1List writeWord64le +{-# INLINE fromWord64sle #-}+++------------------------------------------------------------------------+-- Unaligned, word size ops++-- | Serialize a single native machine 'Word'. The 'Word' is serialized in host+-- order, host endian form, for the machine you're on. On a 64 bit machine the+-- 'Word' is an 8 byte value, on a 32 bit machine, 4 bytes. Values written this+-- way are not portable to different endian or word sized machines, without+-- conversion.+--+fromWordhost :: Word -> Builder+fromWordhost = fromWriteSingleton writeWordhost +{-# INLINE fromWordhost #-}++-- | Serialize a list of 'Word's.+-- See 'fromWordhost' for usage considerations.+fromWordshost :: [Word] -> Builder+fromWordshost = fromWrite2List writeWordhost +{-# INLINE fromWordshost #-}++-- | Write a 'Word16' in native host order and host endianness.+fromWord16host :: Word16 -> Builder+fromWord16host = fromWriteSingleton writeWord16host +{-# INLINE fromWord16host #-}++-- | Write a list of 'Word16's in native host order and host endianness.+fromWord16shost :: [Word16] -> Builder+fromWord16shost = fromWrite4List writeWord16host +{-# INLINE fromWord16shost #-}++-- | Write a 'Word32' in native host order and host endianness.+fromWord32host :: Word32 -> Builder+fromWord32host = fromWriteSingleton writeWord32host +{-# INLINE fromWord32host #-}++-- | Write a list of 'Word32's in native host order and host endianness.+fromWord32shost :: [Word32] -> Builder+fromWord32shost = fromWrite2List writeWord32host +{-# INLINE fromWord32shost #-}++-- | Write a 'Word64' in native host order and host endianness.+fromWord64host :: Word64 -> Builder+fromWord64host = fromWriteSingleton writeWord64host+{-# INLINE fromWord64host #-}++-- | Write a list of 'Word64's in native host order and host endianness.+fromWord64shost :: [Word64] -> Builder+fromWord64shost = fromWrite1List writeWord64host+{-# INLINE fromWord64shost #-}
+ Blaze/ByteString/Builder/Write.hs view
@@ -0,0 +1,384 @@+{-# LANGUAGE CPP, BangPatterns #-}++-- |+-- Module : Blaze.ByteString.Builder.Write+-- Copyright : (c) 2010 Jasper Van der Jeugt & Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- This module provides the 'Write' type, which abstracts direct writes to a+-- buffer. 'Write's form the public interface for lifting direct buffer+-- manipulations to 'Builder's.+--+module Blaze.ByteString.Builder.Write+ ( + -- * Atomic writes to a buffer+ Write (..)++ -- * Creating builders from 'Write' abstractions+ , fromWrite+ , fromWriteSingleton+ , fromWrite1List+ , fromWrite2List+ , fromWrite4List+ , fromWrite8List+ , fromWrite16List++ ) where++import Blaze.ByteString.Builder.Internal++import Foreign+import Data.Monoid +++------------------------------------------------------------------------------+-- Atomic writes to a buffer+------------------------------------------------------------------------------++-- | A value @Write n io@ denotes the write of @n@ bytes to a buffer. The+-- actual write is executed by calling @io@ with a pointer @pf@ to the first+-- free byte that the write should start with. Note that the caller of @io pf@+-- must ensure that @n@ bytes are free starting from @pf@.+--+-- For example, the function @'writeWord8'@ provided by+-- "Blaze.ByteString.Builder.Word" creates a 'Write' that writes a single fixed byte+-- to a buffer.+--+-- > writeWord8 :: Word8 -> Write+-- > writeWord8 x = Write 1 (\pf -> poke pf x)+--+-- The benefit of writes is that they abstract low-level manipulations (e.g.+-- 'poke' and 'copyBytes') of sequences of bytes in a form that that can be+-- completely optimized away in many cases.+--+-- For example, the 'Monoid' instance of 'Write' allows to formulate writing a+-- three-tuple of bytes as follows.+--+-- > writeThreeWord8 :: (Word8, Word8, Word8) -> Write+-- > writeThreeWord8 (x,y,z) = +-- > writeWord8 x `mappend` writeWord8 y `mappend` writeWord8 z+--+-- This expression will be optimized by the compiler to the following efficient+-- 'Write'.+--+-- > writeThreeWord8 (x, y, z) = Write 3 $ \pf -> do+-- > poke pf x+-- > poke (pf `plusPtr` 1) y+-- > poke (pf `plusPtr` 2) z+--+-- Writes are /atomic/. This means that the written data cannot be wrapped+-- over buffer boundaries as it can be done for builders. For writes it holds+-- that either the buffer has enough free space and the write can proceed or a+-- new buffer with a size larger or equal to the number of bytes to write has+-- to be allocated.+--+-- Moreover, for a 'Write', the size of the data to be written must be known+-- before the data can be written. Hence, if this size is data-dependent, the+-- control flow becomes complicated: first, all data must be forced and stored,+-- then the size check happens, and only afterwards the stored data can be+-- written. Therefore, because of cache misses, composing writes with+-- data-dependent size computations may actually be slower than combining the+-- resulting builders. Use benchmarking to make informed decisions.+--+data Write = Write+ {-# UNPACK #-} !Int -- Number of bytes that will be written.+ (Ptr Word8 -> IO ()) -- Function to write the bytes starting from the given+ -- pointer++-- A monoid interface for the 'Write' actions.+instance Monoid Write where+ mempty = Write 0 (const $ return ())+ {-# INLINE mempty #-}++ mappend (Write l1 f1) (Write l2 f2) = Write (l1 + l2) $ \ptr -> do+ f1 ptr+ f2 (ptr `plusPtr` l1)+ {-# INLINE mappend #-}+++-- Lifting Writes to Builders+-----------------------------++-- | Create a 'Builder' from a single write @w@. For good performance, @w@ must+-- feature an outermost 'Write' constructor such that the pattern match can be+-- eliminated during compilation.+--+-- Semantically, it holds that+--+-- > fromWrite . write = fromWriteSingleton write+--+-- However, performance-wise the right-hand side is more efficient due to+-- currently unknown reasons. Use the second form, when+-- defining functions for creating builders from writes of Haskell values.+--+-- (Use the standard benchmark in the @blaze-html@ package when investigating+-- this phenomenon.)+fromWrite :: Write -> Builder+fromWrite (Write size io) =+ Builder step+ where+ step k pf pe+ | pf `plusPtr` size <= pe = do+ io pf+ let pf' = pf `plusPtr` size+ pf' `seq` k pf' pe+ | otherwise = return $ BufferFull size pf (step k)+{-# INLINE fromWrite #-}++-- | Create a 'Builder' constructor from a single 'Write' constructor.+--+fromWriteSingleton :: (a -> Write) -> a -> Builder +fromWriteSingleton write = makeBuilder+ where + makeBuilder x = Builder step+ where+ step k pf pe+ | pf `plusPtr` size <= pe = do+ io pf+ let pf' = pf `plusPtr` size+ pf' `seq` k pf' pe+ | otherwise = return $ BufferFull size pf (step k)+ where+ Write size io = write x+{-# INLINE fromWriteSingleton #-}++-- | Construct a 'Builder' writing a list of data one element at a time from a 'Write' abstraction.+--+fromWrite1List :: (a -> Write) -> [a] -> Builder+fromWrite1List write = makeBuilder+ where+ makeBuilder [] = mempty+ makeBuilder xs0 = Builder $ step xs0+ where+ step xs1 k pf0 pe0 = go xs1 pf0+ where+ go [] !pf = k pf pe0+ go xs@(x':xs') !pf+ | pf `plusPtr` size <= pe0 = do+ io pf+ go xs' (pf `plusPtr` size)+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'+{-# INLINE fromWrite1List #-}++-- | Construct a 'Builder' writing a list of data two elements at a time from a+-- 'Write' abstraction.+--+fromWrite2List :: (a -> Write) -> [a] -> Builder+fromWrite2List write = makeBuilder+ where+ makeBuilder [] = mempty+ makeBuilder xs0 = Builder $ step xs0+ where+ step xs1 k pf0 pe0 = go xs1 pf0+ where+ go [] !pf = k pf pe0++ go xs@[x'1] !pf+ | pf' <= pe0 = do+ io pf+ k pf' pe0+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1+ pf' = pf `plusPtr` size++ go xs@(x'1:x'2:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ pf' = pf `plusPtr` size+{-# INLINE fromWrite2List #-}++-- | Construct a 'Builder' writing a list of data four elements at a time from a+-- 'Write' abstraction.+--+fromWrite4List :: (a -> Write) -> [a] -> Builder+fromWrite4List write = makeBuilder+ where+ makeBuilder [] = mempty+ makeBuilder xs0 = Builder $ step xs0+ where+ step xs1 k pf0 pe0 = go xs1 pf0+ where+ go xs@(x'1:x'2:x'3:x'4:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ `mappend` write x'3 + `mappend` write x'4 + pf' = pf `plusPtr` size++ go xs@(x'1:x'2:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ pf' = pf `plusPtr` size++ go xs@[x'1] !pf+ | pf' <= pe0 = do+ io pf+ k pf' pe0+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1+ pf' = pf `plusPtr` size++ go [] !pf = k pf pe0+{-# INLINE fromWrite4List #-}++-- | Construct a 'Builder' writing a list of data eight elements at a time from a+-- 'Write' abstraction.+--+fromWrite8List :: (a -> Write) -> [a] -> Builder+fromWrite8List write = makeBuilder+ where+ makeBuilder [] = mempty+ makeBuilder xs0 = Builder $ step xs0+ where+ step xs1 k pf0 pe0 = go xs1 pf0+ where+ go xs@(x'1:x'2:x'3:x'4:x'5:x'6:x'7:x'8:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ `mappend` write x'3 + `mappend` write x'4 + `mappend` write x'5 + `mappend` write x'6 + `mappend` write x'7 + `mappend` write x'8 + pf' = pf `plusPtr` size++ go xs@(x'1:x'2:x'3:x'4:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ `mappend` write x'3 + `mappend` write x'4 + pf' = pf `plusPtr` size++ go xs@(x'1:x'2:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ pf' = pf `plusPtr` size++ go xs@[x'1] !pf+ | pf' <= pe0 = do+ io pf+ k pf' pe0+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1+ pf' = pf `plusPtr` size++ go [] !pf = k pf pe0+{-# INLINE fromWrite8List #-}++-- | Construct a 'Builder' writing a list of data 16 elements at a time from a+-- 'Write' abstraction.+--+fromWrite16List :: (a -> Write) -> [a] -> Builder+fromWrite16List write = makeBuilder+ where+ makeBuilder [] = mempty+ makeBuilder xs0 = Builder $ step xs0+ where+ step xs1 k pf0 pe0 = go xs1 pf0+ where+ go xs@(x'1:x'2:x'3:x'4:x'5:x'6:x'7:x'8:x'9:x'10:x'11:x'12:x'13:x'14:x'15:x'16:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ `mappend` write x'3 + `mappend` write x'4 + `mappend` write x'5 + `mappend` write x'6 + `mappend` write x'7 + `mappend` write x'8 + `mappend` write x'9 + `mappend` write x'10+ `mappend` write x'11+ `mappend` write x'12+ `mappend` write x'13+ `mappend` write x'14+ `mappend` write x'15+ `mappend` write x'16+ pf' = pf `plusPtr` size++ go xs@(x'1:x'2:x'3:x'4:x'5:x'6:x'7:x'8:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ `mappend` write x'3 + `mappend` write x'4 + `mappend` write x'5 + `mappend` write x'6 + `mappend` write x'7 + `mappend` write x'8 + pf' = pf `plusPtr` size+++ go xs@(x'1:x'2:x'3:x'4:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ `mappend` write x'3 + `mappend` write x'4 + pf' = pf `plusPtr` size++ go xs@(x'1:x'2:xs') !pf+ | pf' <= pe0 = do+ io pf+ go xs' pf'+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1 `mappend` write x'2+ pf' = pf `plusPtr` size++ go xs@[x'1] !pf+ | pf' <= pe0 = do+ io pf+ k pf' pe0+ | otherwise = do return $ BufferFull size pf (step xs k)+ where+ Write size io = write x'1+ pf' = pf `plusPtr` size++ go [] !pf = k pf pe0+{-# INLINE fromWrite16List #-}++
+ CHANGES view
@@ -0,0 +1,34 @@++* blaze-builder-0.2.0.0++ Heavily restructured 'blaze-builder' such that 'Blaze.ByteString.Builder' serves as+ a drop-in replacement of 'binary:Data.Binary.Builder' which it improves upon+ with respect to both speed as well as expressivity. See the documentation and+ the benchmarks for details on improvements and new functionality.++ Changed module structure:+ Blaze.ByteString.Builder.Core -> Blaze.ByteString.Builder+ Blaze.ByteString.Builder.Utf8 -> Blaze.ByteString.Builder.Char.Utf8+ Blaze.ByteString.Builder.Html -> Blaze.ByteString.Builder.Html.Utf8 ++ Changed function names:+ writeByte -> writeWord8+ fromByte -> fromWord8+ fromWriteList -> fromWrite1List++ Possibly performance sensitive implementation changes:+ - 'fromByteString' and 'fromLazyByteString' check now if a direct insertion+ of the bytestring(s) would be cheaper than copying it. See their+ documentation on how to recover the old behaviour.++ Deprecated functions:+ 'empty' : use 'mempty' instead+ 'singleton': use 'fromWord8' instead+ 'append' : use 'mappend' instead+ ++* blaze-builder-0.1++ This is the first version of 'blaze-builder'. It is explicitely targeted at+ fast generation of UTF-8 encoded HTML documents in the 'blaze-html' and the+ 'hamlet' HTML templating libraries.
+ Makefile view
@@ -0,0 +1,101 @@++##############################################################################+## Benchmarks+##############################################################################++## Config+#########++GHC = ghc-6.12.3+# GHC = ghc-7.0.0.20100924++GHCI = ghci-6.12.3+++## All benchmarks+#################++bench-all: bench-compression bench-string-and-text bench-throughput bench-chunked-write++clean-bench-all:+ rm -f benchmarks/*.o benchmarks/*.hi+ rm -f benchmarks/Throughput/*.o benchmarks/Throughput/*.hi+ rm -f Text/Blaze/Builder.o Text/Blaze/Builder.hi+ rm -f Text/Blaze/Builder/*.o Text/Blaze/Builder/*.hi+ rm -f Text/Blaze/Builder/Char/*.o Text/Blaze/Builder/Char/*.hi+ rm -f Text/Blaze/Builder/Html/*.o Text/Blaze/Builder/Html/*.hi+ rm -f Text/Blaze/Builder/Core/*.o Text/Blaze/Builder/Core/*.hi+ rm -f benchmarks/Compression benchmarks/StringAndText benchmarks/BenchThroughput benchmarks/ChunkedWrite benchmarks/BlazeVsBinary+ rm -f Criterion/*.o Criterion/*.hi+ rm -f Criterion/ScalingBenchmark++## Individual benchmarks+########################++# 'blaze-builder' vs. 'binary' comparision+bench-blaze-vs-binary:+ $(GHC) --make -O2 -fforce-recomp -main-is BlazeVsBinary benchmarks/BlazeVsBinary.hs+ ./benchmarks/BlazeVsBinary --resamples 10000++# throughput benchmarks: interactive development+ghci-throughput: benchmarks/Throughput/CBenchmark.o + $(GHCI) -O2 -fforce-recomp -ibenchmarks -main-is BenchThroughput benchmarks/Throughput/CBenchmark.o benchmarks/BenchThroughput.hs++bench-throughput: benchmarks/Throughput/CBenchmark.o+ $(GHC) --make -O2 -fforce-recomp -fliberate-case-threshold=1000 -ibenchmarks -main-is BenchThroughput benchmarks/Throughput/CBenchmark.o benchmarks/BenchThroughput.hs+ ./benchmarks/BenchThroughput 100++benchmarks/Throughput/CBenchmark.o: benchmarks/Throughput/CBenchmark.c+ gcc -O3 -c $< -o $@++# Benchmark benefit of serializing several list elements at once+bench-chunked-write:+ $(GHC) --make -O2 -fforce-recomp -main-is ChunkedWrite benchmarks/ChunkedWrite.hs+ ./benchmarks/ChunkedWrite --resamples 10000++core-chunked-write:+ ghc-core -- --make -O2 -fforce-recomp -main-is ChunkedWrite benchmarks/ChunkedWrite.hs++# Benchmark best serialization techniques for 'String' and 'Text'+bench-string-and-text:+ $(GHC) --make -O2 -fforce-recomp -ibenchmarks -main-is StringAndText StringAndText+ echo $(GHC)+ ./benchmarks/StringAndText --resamples 10000++# Benchmark benefit of compaction before compression+bench-compression:+ $(GHC) --make -O2 -fforce-recomp -ibenchmarks -main-is Compression Compression+ ./benchmarks/Compression --resamples 10000++##############################################################################+## Plots+##############################################################################++plot-all:+ $(GHC) --make -O2 -fforce-recomp -main-is Criterion.ScalingBenchmark Criterion.ScalingBenchmark+ ./Criterion/ScalingBenchmark --resamples 10000+++##############################################################################+## Tests+##############################################################################++test:+ $(GHC) --make -O2 -itests -main-is Tests Tests+ ./tests/Tests++clean-tests:+ rm -f tests/Tests tests/*.o tests/*.hi++ghci-llvm-segfault: + $(GHCI) -itests -main-is LlvmSegfault tests/LlvmSegfault ++test-llvm-segfault: + ghc-7.0.0.20100924 --make -fllvm -itests -main-is LlvmSegfault tests/LlvmSegfault + ./tests/LlvmSegfault++##############################################################################+## All inclusive targets+##############################################################################++clean: clean-tests clean-bench-all
+ README.markdown view
@@ -0,0 +1,30 @@+blaze-builder+=============++This library allows to efficiently serialize Haskell values to lazy bytestrings+with a large average chunk size. The large average chunk size allows to make+good use of cache prefetching in later processing steps (e.g. compression) and+reduces the sytem call overhead when writing the resulting lazy bytestring to a+file or sending it over the network.++This library was inspired by the module Data.Binary.Builder provided by the+binary package. It was originally developed with the specific needs of the+blaze-html package in mind. Since then it has been restructured to serve as a+drop-in replacement for Data.Binary.Builder, which it improves upon both in+speed as well as expressivity.++To see the improvements in speed, run the throughput benchmark, which measures+serialization speeds for writing Word8, Word16, Word32 and Word64 in different+endian formats and different chunk sizes, using the command++ make bench-throughput++or run the list serialization comparison benchmark++ make bench-blaze-vs-binary++Checkout the combinators in the module "Blaze.ByteString.Builder.Write" to see+the improvements in expressivity. This module allows to incorporate efficient+primitive buffer manipulations as parts of a builder. We use this facility+in the 'blaze-html' HTML templating library to allow for the efficient+serialization of HTML escaped and UTF-8 encoded characters.
+ TODO view
@@ -0,0 +1,63 @@+ + * custom serialization functions for lists of 'WordX's+ - benchmark chunk size speedup for more complicated computations of list+ elements => to be expected that we get no speedup anymore or even a+ slowdown => adapt Blaze.ByteString.Builder.Word accordingly.++ * fast serialization for 'Text' values (currently unpacking to 'String' is+ the fastest :-/)++ * implementation+ - further encodings for 'Char'+ - think about end-of-buffer wrapping when copying bytestrings+ - toByteStringIO with accumulator capability => provide 'toByteStringIO_'+ - allow buildr/foldr deforestation to happen for input to 'fromWrite<n>List'+ (or whatever stream fusion framework is in place for lists)+ - implement 'toByteString' with an amortized O(n) runtime using the+ exponentional scaling trick. If the start size is chosen wisely this+ may even be faster than 'S.pack', as the one copy per element is+ cheaper than one list thunk per element. It is even likely that we can+ amortize three copies per element, which allows to avoid spilling any+ buffer space by doing a last compaction copy.++ * extend tests to new functions++ * benchmarks+ - understand why the declarative blaze-builder version is the fastest+ serializer for Word64 little-endian and big-endian + - check the cost of using `mappend` on builders instead of writes.+ - show that using toByteStringIO has an advantage over toLazyByteString+ - check performance of toByteStringIO+ - compare speed of 'L.pack' to speed of 'toLazyByteString . fromWord8s'++ * documentation+ - sort out formultion: "serialization" vs. "encoding"++ * check portability to Hugs++ * performance:+ - check if reordering 'pe' and 'pf' change performance; it seems that 'pe'+ is only a reader argument while 'pf' is a state argument.+ - perhaps we could improve performance by taking page size, page+ alignment, and memory access alignment into account.+ - detect machine endianness and use host order writes for the supported+ endianness.+ - introduce a type 'BoundedWrite' that encapsulates a 'Write' generator+ with a bound on the number of bytes maximally written by the write.+ This way we can achieve data independence for the size check by+ sacrificing just a little bit of buffer space at buffer ends.+ - investigate where we would profit from static bounds on number of bytes+ written (e.g. to make the control flow more linear)++ * testing+ - port tests from 'Data.Binary.Builder' to ensure that the word writes+ and builders are working correctly. I may have missed some pitfalls+ about word types in Haskell during porting the functions from+ 'Data.Binary.Builder'.++ * portability+ - port to Hugs+ - test lower versions of GHC++ * deployment+ - add source repository to 'blaze-html' and 'blaze-builder' cabal files
− Text/Blaze/Builder/Core.hs
@@ -1,219 +0,0 @@--- | The builder monoid from BlazeHtml.------ Usage is fairly straightforward. Builders can be constructed from many--- values, including 'String' and 'Text' values.------ > strings :: [String]--- > strings = replicate 10000 "Hello there!"------ Concatenation should happen through the 'Monoid' interface.------ > concatenation :: Builder--- > concatenation = mconcat $ map fromString strings------ There is only one way to efficiently obtain the result: to convert the--- 'Builder' to a lazy 'L.ByteString' using 'toLazyByteString'.------ > result :: L.ByteString--- > result = toLazyByteString concatenation----{-# LANGUAGE BangPatterns, OverloadedStrings #-}-module Text.Blaze.Builder.Core- ( - -- * Main builder type- Builder-- -- * Custom writes to the builder- , Write (..)- , writeByte- , writeByteString- , writeSingleton- , writeList-- -- * Creating builders- , singleton- , fromByteString-- -- * Extracting the result from a builder- , toLazyByteString- ) where--import Foreign-import Data.Monoid (Monoid, mempty, mappend, mconcat)-import qualified Data.ByteString.Char8 ()-import qualified Data.ByteString as S-import qualified Data.ByteString.Internal as S-import qualified Data.ByteString.Lazy as L---- | Main builder type. It simply contains a function to extract the actual--- data.----newtype Builder = Builder (BuildStep -> BuildStep)---- | A buildsignal is a signal returned from a write to the builder, it tells us--- what should happen next.----data BuildSignal- -- | Signal the completion of the write process.- = Done {-# UNPACK #-} !(Ptr Word8) -- ^ Pointer to the next free byte- -- | Signal that the buffer is full and a new one needs to be allocated.- -- It contains the minimal size required for the next buffer, a pointer to the- -- next free byte, and a continuation.- | BufferFull- {-# UNPACK #-} !Int- {-# UNPACK #-} !(Ptr Word8)- {-# UNPACK #-} !BuildStep---- | Type for a single build step. Every build step checks that------ > free + bytes-written <= last----type BuildStep = Ptr Word8 -- ^ Ptr to the next free byte in the buffer- -> Ptr Word8 -- ^ Ptr to the first byte AFTER the buffer- -> IO BuildSignal -- ^ Signal the next step to be taken--instance Monoid Builder where- mempty = Builder id- {-# INLINE mempty #-}- mappend (Builder f) (Builder g) = Builder $ f . g- {-# INLINE mappend #-}- mconcat = foldr mappend mempty- {-# INLINE mconcat #-}---- | Write abstraction so we can avoid some gory and bloody details. A write--- abstration holds the exact size of the write in bytes, and a function to--- carry out the write operation.----data Write = Write- {-# UNPACK #-} !Int- (Ptr Word8 -> IO ())---- A monoid interface for the write actions.-instance Monoid Write where- mempty = Write 0 (const $ return ())- {-# INLINE mempty #-}- mappend (Write l1 f1) (Write l2 f2) = Write (l1 + l2) $ \ptr -> do- f1 ptr- f2 (ptr `plusPtr` l1)- {-# INLINE mappend #-}---- | Write a single byte.----writeByte :: Word8 -- ^ Byte to write- -> Write -- ^ Resulting write-writeByte x = Write 1 (\pf -> poke pf x)-{-# INLINE writeByte #-}---- | Write a strict 'S.ByteString'.----writeByteString :: S.ByteString -- ^ 'S.ByteString' to write- -> Write -- ^ Resulting write-writeByteString bs = Write l io- where- (fptr, o, l) = S.toForeignPtr bs- io pf = withForeignPtr fptr $ \p -> copyBytes pf (p `plusPtr` o) l-{-# INLINE writeByteString #-}---- | Construct a 'Builder' from a single 'Write' abstraction.----writeSingleton :: (a -> Write) -- ^ 'Write' abstraction- -> a -- ^ Actual value to write- -> Builder -- ^ Resulting 'Builder'-writeSingleton write = makeBuilder- where - makeBuilder x = Builder step- where- step k pf pe- | pf `plusPtr` size <= pe = do- io pf- let pf' = pf `plusPtr` size- pf' `seq` k pf' pe- | otherwise = return $ BufferFull size pf (step k)- where- Write size io = write x-{-# INLINE writeSingleton #-}---- | Construct a builder writing a list of data from a write abstraction.----writeList :: (a -> Write) -- ^ 'Write' abstraction- -> [a] -- ^ List of values to write- -> Builder -- ^ Resulting 'Builder'-writeList write = makeBuilder- where- makeBuilder [] = mempty- makeBuilder xs0 = Builder $ step xs0- where- step xs1 k pf0 pe0 = go xs1 pf0- where- go [] !pf = k pf pe0- go xs@(x':xs') !pf- | pf `plusPtr` size <= pe0 = do- io pf- go xs' (pf `plusPtr` size)- | otherwise = do return $ BufferFull size pf (step xs k)- where- Write size io = write x'-{-# INLINE writeList #-}---- | Construct a 'Builder' from a single byte.----singleton :: Word8 -- ^ Byte to create a 'Builder' from- -> Builder -- ^ Resulting 'Builder'-singleton = writeSingleton writeByte---- | /O(n)./ A Builder taking a 'S.ByteString`, copying it.----fromByteString :: S.ByteString -- ^ Strict 'S.ByteString' to copy- -> Builder -- ^ Resulting 'Builder'-fromByteString = writeSingleton writeByteString-{-# INLINE fromByteString #-}---- | Copied from Data.ByteString.Lazy.----defaultSize :: Int-defaultSize = 32 * k - overhead- where k = 1024- overhead = 2 * sizeOf (undefined :: Int)---- | Run the builder with the default buffer size.----runBuilder :: Builder -> [S.ByteString] -> [S.ByteString]-runBuilder = runBuilderWith defaultSize-{-# INLINE runBuilder #-}---- | Run the builder with buffers of at least the given size.------ Note that the builders should guarantee that on average the desired buffer--- size is attained almost perfectly. "Almost" because builders may decide to--- start a new buffer and not completely fill the existing buffer, if this is--- faster. However, they should not spill too much of the buffer, if they--- cannot compensate for it.----runBuilderWith :: Int -> Builder -> [S.ByteString] -> [S.ByteString]-runBuilderWith bufSize (Builder b) k = - S.inlinePerformIO $ go bufSize (b finalStep)- where- finalStep pf _ = return $ Done pf-- go !size !step = do- buf <- S.mallocByteString size- withForeignPtr buf $ \pf -> do- next <- step pf (pf `plusPtr` size)- case next of- Done pf'- | pf == pf' -> return k- | otherwise -> return $ S.PS buf 0 (pf' `minusPtr` pf) : k - BufferFull newSize pf' nextStep- | pf == pf' -> bufferFullError- | otherwise -> return $ S.PS buf 0 (pf' `minusPtr` pf) : - S.inlinePerformIO (go (max newSize bufSize) nextStep)-- bufferFullError =- error "runBuilder: buffer cannot be full; no data was written."---- | /O(n)./ Extract the lazy 'L.ByteString' from the builder.----toLazyByteString :: Builder -- ^ 'Builder' to evaluate- -> L.ByteString -- ^ Resulting UTF-8 encoded 'L.ByteString'-toLazyByteString = L.fromChunks . flip runBuilder []-{-# INLINE toLazyByteString #-}
− Text/Blaze/Builder/Html.hs
@@ -1,55 +0,0 @@--- | A module that extends the builder monoid from BlazeHtml with function to--- insert HTML, including HTML escaping and the like.----{-# LANGUAGE OverloadedStrings #-}-module Text.Blaze.Builder.Html- ( - -- * Custom writes to the builder- writeHtmlEscapedChar-- -- * Creating builders- , fromHtmlEscapedChar- , fromHtmlEscapedString- , fromHtmlEscapedText- ) where--import Data.ByteString.Char8 ()-import Data.Monoid (mempty, mappend)--import Data.Text (Text)-import qualified Data.Text as T--import Text.Blaze.Builder.Core-import Text.Blaze.Builder.Utf8---- | Write an unicode character to a 'Builder', doing HTML escaping.----writeHtmlEscapedChar :: Char -- ^ Character to write- -> Write -- ^ Resulting write-writeHtmlEscapedChar '<' = writeByteString "<"-writeHtmlEscapedChar '>' = writeByteString ">"-writeHtmlEscapedChar '&' = writeByteString "&"-writeHtmlEscapedChar '"' = writeByteString """-writeHtmlEscapedChar '\'' = writeByteString "'"-writeHtmlEscapedChar c = writeChar c-{-# INLINE writeHtmlEscapedChar #-}---- | A HTML escaped 'Char'.----fromHtmlEscapedChar :: Char -- ^ Character to write- -> Builder -- ^ Resulting 'Builder'-fromHtmlEscapedChar = writeSingleton writeHtmlEscapedChar---- | A HTML escaped 'String'.----fromHtmlEscapedString :: String -- ^ String to create a 'Builder' from- -> Builder -- ^ Resulting 'Builder'-fromHtmlEscapedString = writeList writeHtmlEscapedChar---- | An HTML escaped piece of 'Text'.----fromHtmlEscapedText :: Text -- ^ 'Text' to insert- -> Builder -- ^ Resulting 'Builder'-fromHtmlEscapedText = writeSingleton (T.foldl appendChar mempty)- where- appendChar w c = w `mappend` writeHtmlEscapedChar c
− Text/Blaze/Builder/Utf8.hs
@@ -1,91 +0,0 @@--- | A module that extends the builder monoid from BlazeHtml with a number of--- functions to insert unicode as UTF-8.----module Text.Blaze.Builder.Utf8- ( - -- * Custom writes to the builder- writeChar-- -- * Creating builders- , fromChar- , fromString- , fromText- ) where--import Foreign-import Data.Char (ord)-import Data.Monoid (mempty, mappend)--import Data.Text (Text)-import qualified Data.Text as T--import Text.Blaze.Builder.Core---- | Write a Unicode character, encoding it as UTF-8.----writeChar :: Char -- ^ Character to write- -> Write -- ^ Resulting write-writeChar = encodeCharUtf8 f1 f2 f3 f4- where- f1 x = Write 1 $ \ptr -> poke ptr x-- f2 x1 x2 = Write 2 $ \ptr -> do poke ptr x1- poke (ptr `plusPtr` 1) x2-- f3 x1 x2 x3 = Write 3 $ \ptr -> do poke ptr x1- poke (ptr `plusPtr` 1) x2- poke (ptr `plusPtr` 2) x3-- f4 x1 x2 x3 x4 = Write 4 $ \ptr -> do poke ptr x1- poke (ptr `plusPtr` 1) x2- poke (ptr `plusPtr` 2) x3- poke (ptr `plusPtr` 3) x4-{-# INLINE writeChar #-}---- | Encode a Unicode character to another datatype, using UTF-8. This function--- acts as an abstract way of encoding characters, as it is unaware of what--- needs to happen with the resulting bytes: you have to specify functions to--- deal with those.----encodeCharUtf8 :: (Word8 -> a) -- ^ 1-byte UTF-8- -> (Word8 -> Word8 -> a) -- ^ 2-byte UTF-8- -> (Word8 -> Word8 -> Word8 -> a) -- ^ 3-byte UTF-8- -> (Word8 -> Word8 -> Word8 -> Word8 -> a) -- ^ 4-byte UTF-8- -> Char -- ^ Input 'Char'- -> a -- ^ Result-encodeCharUtf8 f1 f2 f3 f4 c = case ord c of- x | x <= 0x7F -> f1 $ fromIntegral x- | x <= 0x07FF ->- let x1 = fromIntegral $ (x `shiftR` 6) + 0xC0- x2 = fromIntegral $ (x .&. 0x3F) + 0x80- in f2 x1 x2- | x <= 0xFFFF ->- let x1 = fromIntegral $ (x `shiftR` 12) + 0xE0- x2 = fromIntegral $ ((x `shiftR` 6) .&. 0x3F) + 0x80- x3 = fromIntegral $ (x .&. 0x3F) + 0x80- in f3 x1 x2 x3- | otherwise ->- let x1 = fromIntegral $ (x `shiftR` 18) + 0xF0- x2 = fromIntegral $ ((x `shiftR` 12) .&. 0x3F) + 0x80- x3 = fromIntegral $ ((x `shiftR` 6) .&. 0x3F) + 0x80- x4 = fromIntegral $ (x .&. 0x3F) + 0x80- in f4 x1 x2 x3 x4-{-# INLINE encodeCharUtf8 #-}---- | An unescaped, utf8 encoded character.----fromChar :: Char -- ^ 'Char' to insert- -> Builder -- ^ Resulting 'Builder'-fromChar = writeSingleton writeChar---- | A list of unescaped, utf8 encoded characters.----fromString :: String -- ^ 'String' to insert- -> Builder -- ^ Resulting 'Builder'-fromString = writeList writeChar---- | Create an UTF-8 encoded 'Builder' from some 'Text'.----fromText :: Text -- ^ 'Text' to insert- -> Builder -- ^ Resulting 'Builder'-fromText = writeSingleton (T.foldl (\w c -> w `mappend` writeChar c) mempty)
+ benchmarks/BenchThroughput.hs view
@@ -0,0 +1,239 @@+-----------------------------------------------------------------------------+-- |+-- Module : BenchThroughput+-- Copyright : Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- This benchmark is based on 'tests/Benchmark.hs' from the 'binary-0.5.0.2'+-- package.+--+-- Benchmark the throughput of 'blaze-builder' and 'binary' for serializing+-- sequences of 'Word8' .. 'Word64' values in little-endian, big-endian, and+-- "host-endian" formats.+--+-- The results on a Core2 Duo T7500 with Linux 2.6.32-24 i686 and GHC 6.12.3+-- are as follows:+--+-- Using the Blaze.Builder directly (i.e. not encapsulated in a writer monad+-- as Put is doing it) gives the best scalability. Up to 'Word32', it holds+-- that the bigger the chunk size, the bigger the relative speedup of using+-- the Blaze.Builder. For 'Word64', the speedup is not as impressive.+-- Probably due to the more expensive writes.+--+-----------------------------------------------------------------------------++module BenchThroughput (main) where++import qualified Throughput.BinaryBuilder as BinaryBuilder+import qualified Throughput.BinaryPut as BinaryPut+import qualified Throughput.BinaryBuilderDeclarative as BinaryBuilderDecl++import qualified Throughput.BlazeBuilder as BlazeBuilder+import qualified Throughput.BlazePut as BlazePut+import qualified Throughput.BlazeBuilderDeclarative as BlazeBuilderDecl++import Throughput.Utils+import Throughput.Memory++import qualified Data.ByteString.Lazy as L+import Debug.Trace+import Data.Binary+import Data.Binary.Put+import Data.Binary.Get++import Control.Exception+import Control.Monad+import System.CPUTime+import Numeric+import Text.Printf+import System.Environment+import System.IO++import Data.Maybe+import Data.Accessor+import Data.Colour+import Data.Colour.Names+import Graphics.Rendering.Chart+import Graphics.Rendering.Chart.Gtk+++-- The different serialization functions+----------------------------------------++supportAllSizes f wS cS e i = return $ f wS cS e i++blazeLineStyle = solidLine 1 . opaque+binaryLineStyle = dashedLine 1 [5, 5] . opaque++blazeBuilder = + ( "BlazeBuilder"+ , blazeLineStyle green+ , supportAllSizes $ BlazeBuilder.serialize)++blazeBuilderDecl = + ( "BlazeBuilderDecl"+ , blazeLineStyle blue+ , supportAllSizes $ BlazeBuilderDecl.serialize)++blazePut = + ( "BlazePut"+ , blazeLineStyle red+ , supportAllSizes $ BlazePut.serialize)++binaryBuilder = + ( "BinaryBuilder"+ , binaryLineStyle green+ , supportAllSizes $ BinaryBuilder.serialize)++binaryBuilderDecl = + ( "BinaryBuilderDecl"+ , binaryLineStyle blue+ , BinaryBuilderDecl.serialize)++binaryPut = + ( "BinaryPut"+ , binaryLineStyle red+ , supportAllSizes $ BinaryPut.serialize)+++main :: IO ()+main = do+ mb <- getArgs >>= readIO . head+ -- memBench (mb*10) + putStrLn ""+ putStrLn "Binary serialisation benchmarks:"++ -- do bytewise + -- sequence_+ -- [ test wordSize chunkSize Host mb+ -- | wordSize <- [1]+ -- , chunkSize <- [1,2,4,8,16]+ -- ]++ -- now Word16 .. Word64+ let lift f wS cS e i = return $ f wS cS e i+ serializers = + [ blazeBuilder , blazeBuilderDecl , blazePut+ , binaryBuilder, binaryBuilderDecl, binaryPut+ ]+ wordSizes = [1,2,4,8]+ chunkSizes = [1,2,4,8,16] + endians = [Host,Big,Little]++ let compares = + [ compareResults serialize wordSize chunkSize end mb+ | wordSize <- wordSizes+ , chunkSize <- chunkSizes+ , end <- endians+ , serialize <- serializers+ , wordSize /= 1 || end == Host -- no endianess for Word8+ ]+ -- putStrLn "checking equality of serialization results:"+ -- sequence_ compares+++ let serializes = + [ [ ( serialize+ , [ (chunkSize, test serialize wordSize chunkSize end mb)+ | chunkSize <- [1,2,4,8,16]+ ]+ )+ | serialize <- serializers+ ]+ | wordSize <- [1,2,4,8]+ , end <- [Host,Big,Little]+ , wordSize /= 1 || end == Host -- no endianess for Word8+ ]+++ putStrLn "\n\nbenchmarking serialization speed:"+ results <- mapM mkChart serializes+ print results++mkChart :: [((String,CairoLineStyle,a), [(Int, IO (Maybe Double))])] -> IO ()+mkChart task = do+ lines <- catMaybes `liftM` mapM measureSerializer task+ let plottedLines = flip map lines $ \ ((name,lineStyle,_), points) ->+ plot_lines_title ^= name $+ plot_lines_style ^= lineStyle $+ plot_lines_values ^= [points] $ + defaultPlotLines+ let layout = + defaultLayout1+ { layout1_plots_ = map (Right . toPlot) plottedLines }+ -- renderableToWindow (toRenderable layout) 640 480 +++measureSerializer :: (a, [(Int, IO (Maybe Double))]) -> IO (Maybe (a, [(Int,Double)]))+measureSerializer (info, tests) = do+ optPoints <- forM tests $ \ (x, test) -> do+ optY <- test+ case optY of + Nothing -> return Nothing+ Just y -> return $ Just (x, y)+ case catMaybes optPoints of+ [] -> return Nothing+ points -> return $ Just (info, points)++------------------------------------------------------------------------++time :: IO a -> IO Double+time action = do+ start <- getCPUTime+ action+ end <- getCPUTime+ return $! (fromIntegral (end - start)) / (10^12)++------------------------------------------------------------------------++test :: (String, a, Int -> Int -> Endian -> Int -> Maybe L.ByteString) + -> Int -> Int -> Endian -> Int -> IO (Maybe Double)+test (serializeName, _, serialize) wordSize chunkSize end mb = do+ let bytes :: Int+ bytes = mb * 2^20+ iterations = bytes `div` wordSize+ case serialize wordSize chunkSize end iterations of+ Nothing -> return Nothing+ Just bs -> do+ _ <- printf "%17s: %dMB of Word%-2d in chunks of %2d (%6s endian):"+ serializeName (mb :: Int) (8 * wordSize :: Int) (chunkSize :: Int) (show end)++ putSeconds <- time $ evaluate (L.length bs)+ -- getSeconds <- time $ evaluate sum+ -- print (L.length bs, sum)+ let putThroughput = fromIntegral mb / putSeconds+ -- getThroughput = fromIntegral mb / getSeconds++ _ <- printf "%6.1f MB/s write\n"+ putThroughput+ -- getThroughput+ -- (getThroughput/putThroughput)+ + hFlush stdout+ return $ Just putThroughput++------------------------------------------------------------------------++compareResults :: (String, a, Int -> Int -> Endian -> Int -> Maybe L.ByteString) + -> Int -> Int -> Endian -> Int -> IO ()+compareResults (serializeName, _, serialize) wordSize chunkSize end mb0 = do+ let mb :: Int+ mb = max 1 (mb0 `div` 100)+ bytes :: Int+ bytes = mb * 2^20+ iterations = bytes `div` wordSize+ bs0 = BinaryBuilder.serialize wordSize chunkSize end iterations+ case serialize wordSize chunkSize end iterations of+ Nothing -> return ()+ Just bs1 -> do+ _ <- printf "%17s: %dMB of Word%-2d in chunks of %2d (%6s endian):"+ serializeName (mb :: Int) (8 * wordSize :: Int) (chunkSize :: Int) (show end)+ if (bs0 == bs1) + then putStrLn " Ok"+ else putStrLn " Failed"+ hFlush stdout+
+ benchmarks/BlazeVsBinary.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Module : BlazeVsBinary+-- Copyright : (c) 2010 Jasper Van der Jeught & Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- A comparison between 'blaze-builder' and the Data.Binary.Builder from+-- 'binary'. The goal is to measure the performance on serializing dynamic+-- data referenced by a list.+--+-- Note that some of the benchmarks are a bit unfair with respect to+-- blaze-builder, as it does more than 'binary':+--+-- 1. It encodes chars as utf-8 strings and does not just truncate character+-- value to one byte.+--+-- 2. It copies the contents of the lazy bytestring chunks if they are+-- shorter than 4kb. This ensures efficient processing of the resulting+-- lazy bytestring. 'binary' just inserts the chunks directly in the+-- resulting output stream.+--+module BlazeVsBinary where++import Data.Char (ord)+import Data.Monoid (mconcat)+import Data.Word (Word8)++import qualified Data.Binary.Builder as Binary+import Criterion.Main+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as S+import Data.Text (Text)+import Data.Text.Encoding (encodeUtf8)++import qualified Blaze.ByteString.Builder as Blaze+import qualified Blaze.ByteString.Builder.Char.Utf8 as Blaze++main :: IO ()+main = defaultMain $ concat+ [ benchmark "[String]"+ (mconcat . concatMap (map $ Binary.singleton . fromIntegral . ord))+ (mconcat . map Blaze.fromString)+ strings+ , benchmark "L.ByteString"+ (Binary.fromLazyByteString)+ (Blaze.fromLazyByteString)+ byteStrings+ , benchmark "[Text]"+ (mconcat . map (Binary.fromByteString . encodeUtf8))+ (mconcat . map Blaze.fromText)+ texts+ , benchmark "[Word8]"+ (mconcat . map Binary.singleton)+ (Blaze.fromWord8s)+ word8s+ ]+ where+ benchmark name binaryF blazeF x =+ [ bench (name ++ " (Data.Binary builder)") $+ whnf (L.length . Binary.toLazyByteString . binaryF) x+ , bench (name ++ " (blaze builder)") $+ whnf (L.length . Blaze.toLazyByteString . blazeF) x+ ]++strings :: [String]+strings = replicate 10000 "<img>"+{-# NOINLINE strings #-}++byteStrings :: L.ByteString+byteStrings = L.fromChunks $ replicate 10000 "<img>"+{-# NOINLINE byteStrings #-}++texts :: [Text]+texts = replicate 10000 "<img>"+{-# NOINLINE texts #-}++word8s :: [Word8]+word8s = replicate 10000 $ fromIntegral $ ord 'a'+{-# NOINLINE word8s #-}
+ benchmarks/ChunkedWrite.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Module : ChunkedWrite+-- Copyright : (c) 2010 Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- Test different strategies for writing lists of simple values:+--+-- 1. Using 'mconcat . map from<Value>'+--+-- 2. Using the specialized 'fromWrite<n>List' function where 'n' denotes+-- the number of elements to write at the same time. Writing chunks of+-- elements reduces the overhead from the buffer overflow test that has+-- to be done before every write.+--+module ChunkedWrite where++import Data.Char (chr)+import Data.Int (Int64)+import Data.Word (Word8, Word32)+import Data.Monoid++import Criterion.Main+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as S++import qualified Blaze.ByteString.Builder as BB+import qualified Blaze.ByteString.Builder.Char.Utf8 as BB++main :: IO ()+main = defaultMain + [ bench "S.pack: [Word8] -> S.ByteString" $ + whnf (S.pack) word8s++ , bench "toByteString . fromWord8s: [Word8] -> Builder -> S.ByteString" $ + whnf (BB.toByteString . BB.fromWord8s) word8s++ , bench "L.pack: [Word8] -> L.ByteString" $ + whnf (L.length . L.pack) word8s++ , bench "mconcat . map fromByte: [Word8] -> Builder -> L.ByteString" $ + whnf benchMConcatWord8s word8s+ , bench "fromWrite1List: [Word8] -> Builder -> L.ByteString" $ + whnf bench1Word8s word8s+ , bench "fromWrite2List: [Word8] -> Builder -> L.ByteString" $ + whnf bench2Word8s word8s+ , bench "fromWrite4List: [Word8] -> Builder -> L.ByteString" $ + whnf bench4Word8s word8s+ , bench "fromWrite8List: [Word8] -> Builder -> L.ByteString" $ + whnf bench8Word8s word8s+ , bench "fromWrite16List: [Word8] -> Builder -> L.ByteString" $ + whnf bench16Word8s word8s++ , bench "mconcat . map fromByte: [Char] -> Builder -> L.ByteString" $ + whnf benchMConcatChars chars+ , bench "fromWrite1List: [Char] -> Builder -> L.ByteString" $ + whnf bench1Chars chars+ , bench "fromWrite2List: [Char] -> Builder -> L.ByteString" $ + whnf bench2Chars chars+ , bench "fromWrite4List: [Char] -> Builder -> L.ByteString" $ + whnf bench4Chars chars+ , bench "fromWrite8List: [Char] -> Builder -> L.ByteString" $ + whnf bench8Chars chars+ , bench "fromWrite16List: [Char] -> Builder -> L.ByteString" $ + whnf bench16Chars chars++ , bench "mconcat . map fromWord32host: [Word32] -> Builder -> L.ByteString" $ + whnf benchMConcatWord32s word32s+ , bench "fromWrite1List: [Word32] -> Builder -> L.ByteString" $ + whnf bench1Word32s word32s+ , bench "fromWrite2List: [Word32] -> Builder -> L.ByteString" $ + whnf bench2Word32s word32s+ , bench "fromWrite4List: [Word32] -> Builder -> L.ByteString" $ + whnf bench4Word32s word32s+ , bench "fromWrite8List: [Word32] -> Builder -> L.ByteString" $ + whnf bench8Word32s word32s+ , bench "fromWrite16List: [Word32] -> Builder -> L.ByteString" $ + whnf bench16Word32s word32s+ ]+ where+ n = 100000++ word8s :: [Word8]+ word8s = take n $ map fromIntegral $ [(1::Int)..]+ {-# NOINLINE word8s #-}++ word32s :: [Word32]+ word32s = take n $ [1..]+ {-# NOINLINE word32s #-}++ chars :: String+ chars = take n $ map (chr . fromIntegral) $ word8s+ {-# NOINLINE chars #-}++-- Char++benchMConcatChars :: [Char] -> Int64+benchMConcatChars = L.length . BB.toLazyByteString . mconcat . map BB.fromChar++bench1Chars :: [Char] -> Int64+bench1Chars = L.length . BB.toLazyByteString . BB.fromWrite1List BB.writeChar++bench2Chars :: [Char] -> Int64+bench2Chars = L.length . BB.toLazyByteString . BB.fromWrite2List BB.writeChar++bench4Chars :: [Char] -> Int64+bench4Chars = L.length . BB.toLazyByteString . BB.fromWrite4List BB.writeChar++bench8Chars :: [Char] -> Int64+bench8Chars = L.length . BB.toLazyByteString . BB.fromWrite8List BB.writeChar++bench16Chars :: [Char] -> Int64+bench16Chars = L.length . BB.toLazyByteString . BB.fromWrite16List BB.writeChar++-- Word8++benchMConcatWord8s :: [Word8] -> Int64+benchMConcatWord8s = L.length . BB.toLazyByteString . mconcat . map BB.fromWord8++bench1Word8s :: [Word8] -> Int64+bench1Word8s = L.length . BB.toLazyByteString . BB.fromWrite1List BB.writeWord8++bench2Word8s :: [Word8] -> Int64+bench2Word8s = L.length . BB.toLazyByteString . BB.fromWrite2List BB.writeWord8++bench4Word8s :: [Word8] -> Int64+bench4Word8s = L.length . BB.toLazyByteString . BB.fromWrite4List BB.writeWord8++bench8Word8s :: [Word8] -> Int64+bench8Word8s = L.length . BB.toLazyByteString . BB.fromWrite8List BB.writeWord8++bench16Word8s :: [Word8] -> Int64+bench16Word8s = L.length . BB.toLazyByteString . BB.fromWrite16List BB.writeWord8++-- Word32++benchMConcatWord32s :: [Word32] -> Int64+benchMConcatWord32s = L.length . BB.toLazyByteString . mconcat . map BB.fromWord32host++bench1Word32s :: [Word32] -> Int64+bench1Word32s = L.length . BB.toLazyByteString . BB.fromWrite1List BB.writeWord32host++bench2Word32s :: [Word32] -> Int64+bench2Word32s = L.length . BB.toLazyByteString . BB.fromWrite2List BB.writeWord32host++bench4Word32s :: [Word32] -> Int64+bench4Word32s = L.length . BB.toLazyByteString . BB.fromWrite4List BB.writeWord32host++bench8Word32s :: [Word32] -> Int64+bench8Word32s = L.length . BB.toLazyByteString . BB.fromWrite8List BB.writeWord32host++bench16Word32s :: [Word32] -> Int64+bench16Word32s = L.length . BB.toLazyByteString . BB.fromWrite16List BB.writeWord32host+
+ benchmarks/Compression.hs view
@@ -0,0 +1,55 @@+-- |+-- Module : Compression+-- Copyright : (c) 2010 Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- Benchmark the effect of first compacting the input stream for the 'zlib'+-- compression package.+--+-- On a Core2 Duo T7500 with Linux 2.6.32-24 i686 and GHC 6.12.3 compacting+-- first is worth its price up to chunks of 2kb size. Hence, in most+-- serialization scenarios it is better to first use a builder and only then+-- compress the output.+--+module Compression where++import Data.Int+import Data.Monoid (mconcat, mappend)++import Criterion.Main+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Char8 as S++import qualified Blaze.ByteString.Builder as B+import Codec.Compression.GZip++main = defaultMain + [ bench "compress directly (chunksize 10)" $+ whnf benchCompressDirectly byteString10+ , bench "compress compacted (chunksize 10)" $+ whnf benchCompressCompacted byteString10+ , bench "compress directly (chunksize 2kb)" $+ whnf benchCompressDirectly byteString2kb+ , bench "compress compacted (chunksize 2kb)" $+ whnf benchCompressCompacted byteString2kb+ ]+ where+ n = 100000++ byteString10 = L.fromChunks $ replicate n $ S.pack $ take 10 ['\x0'..]+ {-# NOINLINE byteString10 #-}++ byteString2kb = L.fromChunks $ replicate (n `div` 200) $ S.pack $ take 2048 ['\x0'..]+ {-# NOINLINE byteString2kb #-}+++benchCompressDirectly :: L.ByteString -> Int64+benchCompressDirectly = L.length . compress++benchCompressCompacted :: L.ByteString -> Int64+benchCompressCompacted = + L.length . compress . B.toLazyByteString . B.fromLazyByteString
+ benchmarks/PlotTest.hs view
@@ -0,0 +1,218 @@+-----------------------------------------------------------------------------+-- |+-- Module : PlotTest+-- Copyright : Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- Test plotting for the benchmarks.+-- package.+--+-----------------------------------------------------------------------------++module PlotTest where++import Prelude hiding (lines)++import Data.List (unfoldr)+import Data.Word (Word8)++import Data.Maybe+import Data.Accessor+import Data.Colour+import Data.Colour.Names++import Graphics.Rendering.Chart+import Graphics.Rendering.Chart.Grid+import Graphics.Rendering.Chart.Gtk++import Criterion+import Criterion.Environment+import Criterion.Monad+import Criterion.Types+import Criterion.Config++import Control.Monad+import Control.Monad.Trans+import Control.Monad.Trans.Reader++import Statistics.Types++import qualified System.Random as R++-- Plots to be generated+------------------------++{-++Compression:+ 1 plot (title "compressing <n> MB of random data using 'zlib')+ 3 lines (direct, compacted using a Builder, compaction time) [chunk size/ms]+++ChunkedWrite:+ 1 plot (title "serializing a list of <n> elements")+ 1 line per type of element [chunk size/ms]+++Throughput:+ 5 x 3 plots (word type x endianness) (title "<n> MB of <type> (<endianness>)")+ 1 line per type of Word [chunk size/ MB/s]++-}++-- | A pseudo-random stream of 'Word8' always started from the same initial+-- seed.+randomWord8s :: [Word8]+randomWord8s = map fromIntegral $ unfoldr (Just . R.next) (R.mkStdGen 666) ++-- Main function+----------------++main :: IO ()+main = undefined+++-- Benchmarking Infrastructure+------------------------------++type MyCriterion a = ReaderT Environment Criterion a++-- | Run a list of benchmarks; flattening benchmark groups to a path of strings.+runFlattenedBenchmarks :: [Benchmark] -> MyCriterion [([String],Sample)]+runFlattenedBenchmarks = + (concat `liftM`) . mapM (go id)+ where+ go path (Benchmark name b) = do+ env <- ask+ sample <- lift $ runBenchmark env b+ return [(path [name], sample)]+ go path (BenchGroup name bs) = + concat `liftM` mapM (go (path . (name:))) bs++-- | Run a benchmark for a series of data points; e.g. to measure scalability+-- properties.+runSeriesBenchmark :: (a -> Benchmark) -> [a] -> MyCriterion [(a,Sample)]+runSeriesBenchmark mkBench xs =+ (zip xs . map snd) `liftM` runFlattenedBenchmarks (map mkBench xs)+++-- | Use the given config to measure the environment and then run the embedded+-- criterion operation with this information about the environment.+runMyCriterion :: Config -> MyCriterion a -> IO a+runMyCriterion config criterion = do+ env <- withConfig config measureEnvironment+ withConfig config (runReaderT criterion env)+ +++-- Plotting Infrastructure+--------------------------++colorPalette :: [Colour Double]+colorPalette = [blue, green, red, yellow, magenta, cyan]++lineStylePalette :: [CairoLineStyle]+lineStylePalette = + map (solidLine 1 . opaque) colorPalette +++ map (dashedLine 1 [5, 5] . opaque) colorPalette++-- | > ((title, xName, yName), [(lineName,[(x,y)])])+type PlotData = ((String, String, String), [(String, [(Int, Double)])])++layoutPlot :: PlotData -> Layout1 Int Double+layoutPlot ((title, xName, yName), lines) =+ layout1_plots ^= map (Right . toPlot) plots $ + layout1_title ^= title $+ layout1_bottom_axis ^= mkLinearAxis xName $+ layout1_right_axis ^= mkLogAxis yName $+ defaultLayout1+ where+ (linesName, linesData) = unzip lines+ plots = zipWith3 plotLine linesName (cycle lineStylePalette) linesData++-- | Plot a single named line using the given line style.+plotLine :: String -> CairoLineStyle -> [(Int,Double)] -> PlotLines Int Double+plotLine name style points = + plot_lines_title ^= name $+ plot_lines_style ^= style $+ plot_lines_values ^= [points] $ + defaultPlotLines++mkLinearAxis :: String -> LayoutAxis Int+mkLinearAxis name = laxis_title ^= name $ defaultLayoutAxis++mkLogAxis :: String -> LayoutAxis Double+mkLogAxis name = + laxis_title ^= name $ + laxis_generate ^= autoScaledLogAxis defaultLogAxis $+ defaultLayoutAxis+++++{-+-- Plot Experiments+-------------------+++testData :: [(Int,Double)]+testData = zip xs (map (fromIntegral . (^2)) xs)+ where xs = [1,2,4,8,16,32]+++blazeLineStyle = solidLine 1 . opaque+binaryLineStyle = dashedLine 1 [5, 5] . opaque+++plots :: [PlotLines Int Double]+plots = [ plotLine [c] style testData + | (c, style) <- zip ['a'..] (cycle lineStylePalette) ]+++mkLayout xname yname title p = + layout1_plots ^= [Right p] $ + layout1_title ^= title $+ layout1_bottom_axis ^= mkLinearAxis xname $+ layout1_right_axis ^= mkLogAxis yname $+ defaultLayout1++layouts = zipWith (mkLayout "chunksize" "MB/s") (map return ['A'..]) (map toPlot plots)++testGrid = aboveN $ map (besideN . map (flip tspan (1,1) . toRenderable)) [l1,l2]+ where+ (l1,l2) = splitAt 3 layouts++testIt = renderableToWindow (gridToRenderable testGrid) 640 480+-}++{-+mkChart :: [((String,CairoLineStyle,a), [(Int, IO (Maybe Double))])] -> IO ()+mkChart task = do+ lines <- catMaybes `liftM` mapM measureSerializer task+ let plottedLines = flip map lines $ \ ((name,lineStyle,_), points) ->+ plot_lines_title ^= name $+ plot_lines_style ^= lineStyle $+ plot_lines_values ^= [points] $ + defaultPlotLines+ let layout = + defaultLayout1+ { layout1_plots_ = map (Right . toPlot) plottedLines }+ renderableToWindow (toRenderable layout) 640 480 +++measureSerializer :: (a, [(Int, IO (Maybe Double))]) -> IO (Maybe (a, [(Int,Double)]))+measureSerializer (info, tests) = do+ optPoints <- forM tests $ \ (x, test) -> do+ optY <- test+ case optY of + Nothing -> return Nothing+ Just y -> return $ Just (x, y)+ case catMaybes optPoints of+ [] -> return Nothing+ points -> return $ Just (info, points)++-}
+ benchmarks/StringAndText.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Module : StringAndText+-- Copyright : (c) 2010 Simon Meier+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Simon Meier <iridcode@gmail.com>+-- Stability : experimental+-- Portability : tested on GHC only+--+-- Benchmarking of String and Text serialization.+module StringAndText (main) where++import Data.Char (ord)+import Data.Monoid ++import Criterion.Main++import Foreign (plusPtr)+import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L+import qualified Data.Text as TS+import qualified Data.Text.Encoding as TS+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Encoding as TL++import qualified Blaze.ByteString.Builder as Blaze+import qualified Blaze.ByteString.Builder.Internal as Blaze+import qualified Blaze.ByteString.Builder.Html.Utf8 as Blaze++main :: IO ()+main = defaultMain + [ bench "TL.unpack :: LazyText -> String" $ nf+ TL.unpack benchLazyText++ , bench "TL.foldr :: LazyText -> String" $ nf+ (TL.foldr (:) []) benchLazyText+ + , bench "fromString :: String --[Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . Blaze.fromString) benchString++ , bench "fromStrictTextUnpacked :: StrictText --[Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . Blaze.fromText) benchStrictText+ + , bench "fromStrictTextFolded :: StrictText --[Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . fromStrictTextFolded) benchStrictText++ , bench "TS.encodeUtf8 :: StrictText --[Utf8 encoding]--> S.ByteString" $ whnf+ (TS.encodeUtf8) benchStrictText++ , bench "fromLazyTextUnpacked :: LazyText --[Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . Blaze.fromLazyText) benchLazyText++ , bench "fromLazyTextFolded :: LazyText --[Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . fromLazyTextFolded) benchLazyText++ , bench "TL.encodeUtf8 :: LazyText --[Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . TL.encodeUtf8) benchLazyText++ , bench "fromHtmlEscapedString :: String --[Html esc. & Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . Blaze.fromHtmlEscapedString) benchString++ , bench "fromHtmlEscapedStrictTextUnpacked :: StrictText --[HTML esc. & Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . Blaze.fromHtmlEscapedText) benchStrictText+ + , bench "fromHtmlEscapedLazyTextUnpacked :: LazyText --[HTML esc. & Utf8 encoding]--> L.ByteString" $ whnf+ (L.length . Blaze.toLazyByteString . Blaze.fromHtmlEscapedLazyText) benchLazyText+ + ]++n :: Int+n = 100000++benchString :: String+benchString = take n $ concatMap show [(1::Int)..]+{-# NOINLINE benchString #-}++benchStrictText :: TS.Text+benchStrictText = TS.pack benchString+{-# NOINLINE benchStrictText #-}++benchLazyText :: TL.Text+benchLazyText = TL.pack benchString+{-# NOINLINE benchLazyText #-}+++-- | Encode the 'TS.Text' as UTF-8 by folding it and filling the raw buffer+-- directly.+fromStrictTextFolded :: TS.Text -> Blaze.Builder+fromStrictTextFolded t = Blaze.Builder $ \k -> TS.foldr step k t+ where+ step c k pf pe+ | pf' <= pe = do+ io pf+ k pf' pe -- here it would be great, if we wouldn't have to pass+ -- around pe: requires a more powerful fold for StrictText.+ | otherwise =+ return $ Blaze.BufferFull size pf $ \pfNew peNew -> do+ let pfNew' = pfNew `plusPtr` size+ io pfNew+ k pfNew' peNew+ where+ pf' = pf `plusPtr` size+ Blaze.Write size io = Blaze.writeChar c+{-# INLINE fromStrictTextFolded #-}++-- | Encode the 'TL.Text' as UTF-8 by folding it and filling the raw buffer+-- directly.+fromLazyTextFolded :: TL.Text -> Blaze.Builder+fromLazyTextFolded t = Blaze.Builder $ \k -> TL.foldr step k t+ where+ step c k pf pe+ | pf' <= pe = do+ io pf+ k pf' pe -- here it would be great, if we wouldn't have to pass+ -- around pe: requires a more powerful fold for StrictText.+ | otherwise =+ return $ Blaze.BufferFull size pf $ \pfNew peNew -> do+ let pfNew' = pfNew `plusPtr` size+ io pfNew+ k pfNew' peNew+ where+ pf' = pf `plusPtr` size+ Blaze.Write size io = Blaze.writeChar c+{-# INLINE fromLazyTextFolded #-}
+ benchmarks/Throughput/BinaryBuilder.hs view
@@ -0,0 +1,697 @@+{-# LANGUAGE BangPatterns #-}+module Throughput.BinaryBuilder (serialize) where++import Data.Monoid+import qualified Data.ByteString.Lazy as L+import Data.Binary.Builder++import Throughput.Utils++serialize :: Int -> Int -> Endian -> Int -> L.ByteString+serialize wordSize chunkSize end = toLazyByteString .+ case (wordSize, chunkSize, end) of+ (1, 1,_) -> writeByteN1+ (1, 2,_) -> writeByteN2+ (1, 4,_) -> writeByteN4+ (1, 8,_) -> writeByteN8+ (1, 16, _) -> writeByteN16++ (2, 1, Big) -> writeWord16N1Big+ (2, 2, Big) -> writeWord16N2Big+ (2, 4, Big) -> writeWord16N4Big+ (2, 8, Big) -> writeWord16N8Big+ (2, 16, Big) -> writeWord16N16Big+ (2, 1, Little) -> writeWord16N1Little+ (2, 2, Little) -> writeWord16N2Little+ (2, 4, Little) -> writeWord16N4Little+ (2, 8, Little) -> writeWord16N8Little+ (2, 16, Little) -> writeWord16N16Little+ (2, 1, Host) -> writeWord16N1Host+ (2, 2, Host) -> writeWord16N2Host+ (2, 4, Host) -> writeWord16N4Host+ (2, 8, Host) -> writeWord16N8Host+ (2, 16, Host) -> writeWord16N16Host++ (4, 1, Big) -> writeWord32N1Big+ (4, 2, Big) -> writeWord32N2Big+ (4, 4, Big) -> writeWord32N4Big+ (4, 8, Big) -> writeWord32N8Big+ (4, 16, Big) -> writeWord32N16Big+ (4, 1, Little) -> writeWord32N1Little+ (4, 2, Little) -> writeWord32N2Little+ (4, 4, Little) -> writeWord32N4Little+ (4, 8, Little) -> writeWord32N8Little+ (4, 16, Little) -> writeWord32N16Little+ (4, 1, Host) -> writeWord32N1Host+ (4, 2, Host) -> writeWord32N2Host+ (4, 4, Host) -> writeWord32N4Host+ (4, 8, Host) -> writeWord32N8Host+ (4, 16, Host) -> writeWord32N16Host++ (8, 1, Host) -> writeWord64N1Host+ (8, 2, Host) -> writeWord64N2Host+ (8, 4, Host) -> writeWord64N4Host+ (8, 8, Host) -> writeWord64N8Host+ (8, 16, Host) -> writeWord64N16Host+ (8, 1, Big) -> writeWord64N1Big+ (8, 2, Big) -> writeWord64N2Big+ (8, 4, Big) -> writeWord64N4Big+ (8, 8, Big) -> writeWord64N8Big+ (8, 16, Big) -> writeWord64N16Big+ (8, 1, Little) -> writeWord64N1Little+ (8, 2, Little) -> writeWord64N2Little+ (8, 4, Little) -> writeWord64N4Little+ (8, 8, Little) -> writeWord64N8Little+ (8, 16, Little) -> writeWord64N16Little++------------------------------------------------------------------------++writeByteN1 bytes = loop 0 0+ where loop !s !n | n == bytes = mempty+ | otherwise = singleton s `mappend`+ loop (s+1) (n+1)++writeByteN2 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ singleton (s+0) `mappend`+ singleton (s+1)) `mappend`+ loop (s+2) (n-2)++writeByteN4 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ singleton (s+0) `mappend`+ singleton (s+1) `mappend`+ singleton (s+2) `mappend`+ singleton (s+3)) `mappend`+ loop (s+4) (n-4)++writeByteN8 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ singleton (s+0) `mappend`+ singleton (s+1) `mappend`+ singleton (s+2) `mappend`+ singleton (s+3) `mappend`+ singleton (s+4) `mappend`+ singleton (s+5) `mappend`+ singleton (s+6) `mappend`+ singleton (s+7)) `mappend`+ loop (s+8) (n-8)++writeByteN16 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ singleton (s+0) `mappend`+ singleton (s+1) `mappend`+ singleton (s+2) `mappend`+ singleton (s+3) `mappend`+ singleton (s+4) `mappend`+ singleton (s+5) `mappend`+ singleton (s+6) `mappend`+ singleton (s+7) `mappend`+ singleton (s+8) `mappend`+ singleton (s+9) `mappend`+ singleton (s+10) `mappend`+ singleton (s+11) `mappend`+ singleton (s+12) `mappend`+ singleton (s+13) `mappend`+ singleton (s+14) `mappend`+ singleton (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord16N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16be (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord16N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16be (s+0) `mappend`+ putWord16be (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord16N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16be (s+0) `mappend`+ putWord16be (s+1) `mappend`+ putWord16be (s+2) `mappend`+ putWord16be (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord16N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16be (s+0) `mappend`+ putWord16be (s+1) `mappend`+ putWord16be (s+2) `mappend`+ putWord16be (s+3) `mappend`+ putWord16be (s+4) `mappend`+ putWord16be (s+5) `mappend`+ putWord16be (s+6) `mappend`+ putWord16be (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord16N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16be (s+0) `mappend`+ putWord16be (s+1) `mappend`+ putWord16be (s+2) `mappend`+ putWord16be (s+3) `mappend`+ putWord16be (s+4) `mappend`+ putWord16be (s+5) `mappend`+ putWord16be (s+6) `mappend`+ putWord16be (s+7) `mappend`+ putWord16be (s+8) `mappend`+ putWord16be (s+9) `mappend`+ putWord16be (s+10) `mappend`+ putWord16be (s+11) `mappend`+ putWord16be (s+12) `mappend`+ putWord16be (s+13) `mappend`+ putWord16be (s+14) `mappend`+ putWord16be (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Little endian, word16 writes++writeWord16N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = + (putWord16le (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord16N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16le (s+0) `mappend`+ putWord16le (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord16N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16le (s+0) `mappend`+ putWord16le (s+1) `mappend`+ putWord16le (s+2) `mappend`+ putWord16le (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord16N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16le (s+0) `mappend`+ putWord16le (s+1) `mappend`+ putWord16le (s+2) `mappend`+ putWord16le (s+3) `mappend`+ putWord16le (s+4) `mappend`+ putWord16le (s+5) `mappend`+ putWord16le (s+6) `mappend`+ putWord16le (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord16N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16le (s+0) `mappend`+ putWord16le (s+1) `mappend`+ putWord16le (s+2) `mappend`+ putWord16le (s+3) `mappend`+ putWord16le (s+4) `mappend`+ putWord16le (s+5) `mappend`+ putWord16le (s+6) `mappend`+ putWord16le (s+7) `mappend`+ putWord16le (s+8) `mappend`+ putWord16le (s+9) `mappend`+ putWord16le (s+10) `mappend`+ putWord16le (s+11) `mappend`+ putWord16le (s+12) `mappend`+ putWord16le (s+13) `mappend`+ putWord16le (s+14) `mappend`+ putWord16le (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Host endian, unaligned, word16 writes++writeWord16N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16host (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord16N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16host (s+0) `mappend`+ putWord16host (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord16N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16host (s+0) `mappend`+ putWord16host (s+1) `mappend`+ putWord16host (s+2) `mappend`+ putWord16host (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord16N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16host (s+0) `mappend`+ putWord16host (s+1) `mappend`+ putWord16host (s+2) `mappend`+ putWord16host (s+3) `mappend`+ putWord16host (s+4) `mappend`+ putWord16host (s+5) `mappend`+ putWord16host (s+6) `mappend`+ putWord16host (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord16N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord16host (s+0) `mappend`+ putWord16host (s+1) `mappend`+ putWord16host (s+2) `mappend`+ putWord16host (s+3) `mappend`+ putWord16host (s+4) `mappend`+ putWord16host (s+5) `mappend`+ putWord16host (s+6) `mappend`+ putWord16host (s+7) `mappend`+ putWord16host (s+8) `mappend`+ putWord16host (s+9) `mappend`+ putWord16host (s+10) `mappend`+ putWord16host (s+11) `mappend`+ putWord16host (s+12) `mappend`+ putWord16host (s+13) `mappend`+ putWord16host (s+14) `mappend`+ putWord16host (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32be (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord32N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32be (s+0) `mappend`+ putWord32be (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord32N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32be (s+0) `mappend`+ putWord32be (s+1) `mappend`+ putWord32be (s+2) `mappend`+ putWord32be (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord32N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32be (s+0) `mappend`+ putWord32be (s+1) `mappend`+ putWord32be (s+2) `mappend`+ putWord32be (s+3) `mappend`+ putWord32be (s+4) `mappend`+ putWord32be (s+5) `mappend`+ putWord32be (s+6) `mappend`+ putWord32be (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord32N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32be (s+0) `mappend`+ putWord32be (s+1) `mappend`+ putWord32be (s+2) `mappend`+ putWord32be (s+3) `mappend`+ putWord32be (s+4) `mappend`+ putWord32be (s+5) `mappend`+ putWord32be (s+6) `mappend`+ putWord32be (s+7) `mappend`+ putWord32be (s+8) `mappend`+ putWord32be (s+9) `mappend`+ putWord32be (s+10) `mappend`+ putWord32be (s+11) `mappend`+ putWord32be (s+12) `mappend`+ putWord32be (s+13) `mappend`+ putWord32be (s+14) `mappend`+ putWord32be (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32le (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord32N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32le (s+0) `mappend`+ putWord32le (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord32N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32le (s+0) `mappend`+ putWord32le (s+1) `mappend`+ putWord32le (s+2) `mappend`+ putWord32le (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord32N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32le (s+0) `mappend`+ putWord32le (s+1) `mappend`+ putWord32le (s+2) `mappend`+ putWord32le (s+3) `mappend`+ putWord32le (s+4) `mappend`+ putWord32le (s+5) `mappend`+ putWord32le (s+6) `mappend`+ putWord32le (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord32N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32le (s+0) `mappend`+ putWord32le (s+1) `mappend`+ putWord32le (s+2) `mappend`+ putWord32le (s+3) `mappend`+ putWord32le (s+4) `mappend`+ putWord32le (s+5) `mappend`+ putWord32le (s+6) `mappend`+ putWord32le (s+7) `mappend`+ putWord32le (s+8) `mappend`+ putWord32le (s+9) `mappend`+ putWord32le (s+10) `mappend`+ putWord32le (s+11) `mappend`+ putWord32le (s+12) `mappend`+ putWord32le (s+13) `mappend`+ putWord32le (s+14) `mappend`+ putWord32le (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32host (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord32N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32host (s+0) `mappend`+ putWord32host (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord32N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32host (s+0) `mappend`+ putWord32host (s+1) `mappend`+ putWord32host (s+2) `mappend`+ putWord32host (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord32N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32host (s+0) `mappend`+ putWord32host (s+1) `mappend`+ putWord32host (s+2) `mappend`+ putWord32host (s+3) `mappend`+ putWord32host (s+4) `mappend`+ putWord32host (s+5) `mappend`+ putWord32host (s+6) `mappend`+ putWord32host (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord32N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord32host (s+0) `mappend`+ putWord32host (s+1) `mappend`+ putWord32host (s+2) `mappend`+ putWord32host (s+3) `mappend`+ putWord32host (s+4) `mappend`+ putWord32host (s+5) `mappend`+ putWord32host (s+6) `mappend`+ putWord32host (s+7) `mappend`+ putWord32host (s+8) `mappend`+ putWord32host (s+9) `mappend`+ putWord32host (s+10) `mappend`+ putWord32host (s+11) `mappend`+ putWord32host (s+12) `mappend`+ putWord32host (s+13) `mappend`+ putWord32host (s+14) `mappend`+ putWord32host (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64be (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord64N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64be (s+0) `mappend`+ putWord64be (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord64N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64be (s+0) `mappend`+ putWord64be (s+1) `mappend`+ putWord64be (s+2) `mappend`+ putWord64be (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord64N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64be (s+0) `mappend`+ putWord64be (s+1) `mappend`+ putWord64be (s+2) `mappend`+ putWord64be (s+3) `mappend`+ putWord64be (s+4) `mappend`+ putWord64be (s+5) `mappend`+ putWord64be (s+6) `mappend`+ putWord64be (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord64N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64be (s+0) `mappend`+ putWord64be (s+1) `mappend`+ putWord64be (s+2) `mappend`+ putWord64be (s+3) `mappend`+ putWord64be (s+4) `mappend`+ putWord64be (s+5) `mappend`+ putWord64be (s+6) `mappend`+ putWord64be (s+7) `mappend`+ putWord64be (s+8) `mappend`+ putWord64be (s+9) `mappend`+ putWord64be (s+10) `mappend`+ putWord64be (s+11) `mappend`+ putWord64be (s+12) `mappend`+ putWord64be (s+13) `mappend`+ putWord64be (s+14) `mappend`+ putWord64be (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64le (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord64N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64le (s+0) `mappend`+ putWord64le (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord64N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64le (s+0) `mappend`+ putWord64le (s+1) `mappend`+ putWord64le (s+2) `mappend`+ putWord64le (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord64N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64le (s+0) `mappend`+ putWord64le (s+1) `mappend`+ putWord64le (s+2) `mappend`+ putWord64le (s+3) `mappend`+ putWord64le (s+4) `mappend`+ putWord64le (s+5) `mappend`+ putWord64le (s+6) `mappend`+ putWord64le (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord64N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64le (s+0) `mappend`+ putWord64le (s+1) `mappend`+ putWord64le (s+2) `mappend`+ putWord64le (s+3) `mappend`+ putWord64le (s+4) `mappend`+ putWord64le (s+5) `mappend`+ putWord64le (s+6) `mappend`+ putWord64le (s+7) `mappend`+ putWord64le (s+8) `mappend`+ putWord64le (s+9) `mappend`+ putWord64le (s+10) `mappend`+ putWord64le (s+11) `mappend`+ putWord64le (s+12) `mappend`+ putWord64le (s+13) `mappend`+ putWord64le (s+14) `mappend`+ putWord64le (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64host (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord64N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64host (s+0) `mappend`+ putWord64host (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord64N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64host (s+0) `mappend`+ putWord64host (s+1) `mappend`+ putWord64host (s+2) `mappend`+ putWord64host (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord64N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64host (s+0) `mappend`+ putWord64host (s+1) `mappend`+ putWord64host (s+2) `mappend`+ putWord64host (s+3) `mappend`+ putWord64host (s+4) `mappend`+ putWord64host (s+5) `mappend`+ putWord64host (s+6) `mappend`+ putWord64host (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord64N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = (+ putWord64host (s+0) `mappend`+ putWord64host (s+1) `mappend`+ putWord64host (s+2) `mappend`+ putWord64host (s+3) `mappend`+ putWord64host (s+4) `mappend`+ putWord64host (s+5) `mappend`+ putWord64host (s+6) `mappend`+ putWord64host (s+7) `mappend`+ putWord64host (s+8) `mappend`+ putWord64host (s+9) `mappend`+ putWord64host (s+10) `mappend`+ putWord64host (s+11) `mappend`+ putWord64host (s+12) `mappend`+ putWord64host (s+13) `mappend`+ putWord64host (s+14) `mappend`+ putWord64host (s+15)) `mappend`+ loop (s+16) (n-16)+
+ benchmarks/Throughput/BinaryBuilderDeclarative.hs view
@@ -0,0 +1,118 @@+module Throughput.BinaryBuilderDeclarative (+ serialize+) where++import Data.Monoid+import Data.Word+import qualified Data.ByteString.Lazy as L++import Data.Binary.Builder++import Control.Monad++import Throughput.Utils++serialize :: Int -> Int -> Endian -> Int -> Maybe L.ByteString+serialize wordSize chunkSize end iters = fmap toLazyByteString $+ case (wordSize, chunkSize, end) of+ (1, 1,_) -> return $ writeByteN1 iters++ (2, 1, Big) -> return $ writeWord16N1Big iters+ (2, 1, Little) -> return $ writeWord16N1Little iters+ (2, 1, Host) -> return $ writeWord16N1Host iters++ (4, 1, Big) -> return $ writeWord32N1Big iters+ (4, 1, Little) -> return $ writeWord32N1Little iters+ (4, 1, Host) -> return $ writeWord32N1Host iters++ (8, 1, Host) -> return $ writeWord64N1Host iters+ (8, 1, Big) -> return $ writeWord64N1Big iters+ (8, 1, Little) -> return $ writeWord64N1Little iters++ _ -> mzero++------------------------------------------------------------------------+-- Word8+------------------------------------------------------------------------++word8List :: Int -> [Word8]+word8List n = take n $ cycle $ [0..]++------------------------------------------------------------------------++writeByteN1 = mconcat . map singleton . word8List+++------------------------------------------------------------------------+-- Word16+------------------------------------------------------------------------++word16List :: Int -> [Word16]+word16List n = take n $ cycle $ [0..]++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord16N1Big = mconcat . map putWord16be . word16List+++------------------------------------------------------------------------+-- Little endian, word16 writes++writeWord16N1Little = mconcat . map putWord16le . word16List+++------------------------------------------------------------------------+-- Host endian, unaligned, word16 writes++writeWord16N1Host = mconcat . map putWord16host . word16List+++------------------------------------------------------------------------+-- Word32+------------------------------------------------------------------------++word32List :: Int -> [Word32]+word32List n = [0..fromIntegral (n-1)]++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord32N1Big = mconcat . map putWord32be . word32List+++------------------------------------------------------------------------+-- Little endian, word32 writes++writeWord32N1Little = mconcat . map putWord32le . word32List+++------------------------------------------------------------------------+-- Host endian, unaligned, word32 writes++writeWord32N1Host = mconcat . map putWord32host . word32List+++------------------------------------------------------------------------+-- Word64+------------------------------------------------------------------------++word64List :: Int -> [Word64]+word64List n = [0..fromIntegral (n-1)]++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord64N1Big = mconcat . map putWord64be . word64List+++------------------------------------------------------------------------+-- Little endian, word64 writes++writeWord64N1Little = mconcat . map putWord64le . word64List++------------------------------------------------------------------------+-- Host endian, unaligned, word64 writes++writeWord64N1Host = mconcat . map putWord64host . word64List+
+ benchmarks/Throughput/BinaryPut.hs view
@@ -0,0 +1,696 @@+{-# LANGUAGE BangPatterns #-}+module Throughput.BinaryPut (serialize) where++import qualified Data.ByteString.Lazy as L+import Data.Binary.Put++import Throughput.Utils++serialize :: Int -> Int -> Endian -> Int -> L.ByteString+serialize wordSize chunkSize end = runPut .+ case (wordSize, chunkSize, end) of+ (1, 1,_) -> putWord8N1+ (1, 2,_) -> putWord8N2+ (1, 4,_) -> putWord8N4+ (1, 8,_) -> putWord8N8+ (1, 16, _) -> putWord8N16++ (2, 1, Big) -> putWord16N1Big+ (2, 2, Big) -> putWord16N2Big+ (2, 4, Big) -> putWord16N4Big+ (2, 8, Big) -> putWord16N8Big+ (2, 16, Big) -> putWord16N16Big+ (2, 1, Little) -> putWord16N1Little+ (2, 2, Little) -> putWord16N2Little+ (2, 4, Little) -> putWord16N4Little+ (2, 8, Little) -> putWord16N8Little+ (2, 16, Little) -> putWord16N16Little+ (2, 1, Host) -> putWord16N1Host+ (2, 2, Host) -> putWord16N2Host+ (2, 4, Host) -> putWord16N4Host+ (2, 8, Host) -> putWord16N8Host+ (2, 16, Host) -> putWord16N16Host++ (4, 1, Big) -> putWord32N1Big+ (4, 2, Big) -> putWord32N2Big+ (4, 4, Big) -> putWord32N4Big+ (4, 8, Big) -> putWord32N8Big+ (4, 16, Big) -> putWord32N16Big+ (4, 1, Little) -> putWord32N1Little+ (4, 2, Little) -> putWord32N2Little+ (4, 4, Little) -> putWord32N4Little+ (4, 8, Little) -> putWord32N8Little+ (4, 16, Little) -> putWord32N16Little+ (4, 1, Host) -> putWord32N1Host+ (4, 2, Host) -> putWord32N2Host+ (4, 4, Host) -> putWord32N4Host+ (4, 8, Host) -> putWord32N8Host+ (4, 16, Host) -> putWord32N16Host++ (8, 1, Host) -> putWord64N1Host+ (8, 2, Host) -> putWord64N2Host+ (8, 4, Host) -> putWord64N4Host+ (8, 8, Host) -> putWord64N8Host+ (8, 16, Host) -> putWord64N16Host+ (8, 1, Big) -> putWord64N1Big+ (8, 2, Big) -> putWord64N2Big+ (8, 4, Big) -> putWord64N4Big+ (8, 8, Big) -> putWord64N8Big+ (8, 16, Big) -> putWord64N16Big+ (8, 1, Little) -> putWord64N1Little+ (8, 2, Little) -> putWord64N2Little+ (8, 4, Little) -> putWord64N4Little+ (8, 8, Little) -> putWord64N8Little+ (8, 16, Little) -> putWord64N16Little++------------------------------------------------------------------------++putWord8N1 bytes = loop 0 0+ where loop !s !n | n == bytes = return ()+ | otherwise = do putWord8 s+ loop (s+1) (n+1)++putWord8N2 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ loop (s+2) (n-2)++putWord8N4 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ putWord8 (s+2)+ putWord8 (s+3)+ loop (s+4) (n-4)++putWord8N8 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ putWord8 (s+2)+ putWord8 (s+3)+ putWord8 (s+4)+ putWord8 (s+5)+ putWord8 (s+6)+ putWord8 (s+7)+ loop (s+8) (n-8)++putWord8N16 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ putWord8 (s+2)+ putWord8 (s+3)+ putWord8 (s+4)+ putWord8 (s+5)+ putWord8 (s+6)+ putWord8 (s+7)+ putWord8 (s+8)+ putWord8 (s+9)+ putWord8 (s+10)+ putWord8 (s+11)+ putWord8 (s+12)+ putWord8 (s+13)+ putWord8 (s+14)+ putWord8 (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Big endian, word16 writes++putWord16N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ loop (s+1) (n-1)++putWord16N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ loop (s+2) (n-2)++putWord16N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ putWord16be (s+2)+ putWord16be (s+3)+ loop (s+4) (n-4)++putWord16N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ putWord16be (s+2)+ putWord16be (s+3)+ putWord16be (s+4)+ putWord16be (s+5)+ putWord16be (s+6)+ putWord16be (s+7)+ loop (s+8) (n-8)++putWord16N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ putWord16be (s+2)+ putWord16be (s+3)+ putWord16be (s+4)+ putWord16be (s+5)+ putWord16be (s+6)+ putWord16be (s+7)+ putWord16be (s+8)+ putWord16be (s+9)+ putWord16be (s+10)+ putWord16be (s+11)+ putWord16be (s+12)+ putWord16be (s+13)+ putWord16be (s+14)+ putWord16be (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Little endian, word16 writes++putWord16N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ loop (s+1) (n-1)++putWord16N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ loop (s+2) (n-2)++putWord16N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ putWord16le (s+2)+ putWord16le (s+3)+ loop (s+4) (n-4)++putWord16N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ putWord16le (s+2)+ putWord16le (s+3)+ putWord16le (s+4)+ putWord16le (s+5)+ putWord16le (s+6)+ putWord16le (s+7)+ loop (s+8) (n-8)++putWord16N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ putWord16le (s+2)+ putWord16le (s+3)+ putWord16le (s+4)+ putWord16le (s+5)+ putWord16le (s+6)+ putWord16le (s+7)+ putWord16le (s+8)+ putWord16le (s+9)+ putWord16le (s+10)+ putWord16le (s+11)+ putWord16le (s+12)+ putWord16le (s+13)+ putWord16le (s+14)+ putWord16le (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Host endian, unaligned, word16 writes++putWord16N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ loop (s+1) (n-1)++putWord16N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ loop (s+2) (n-2)++putWord16N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ putWord16host (s+2)+ putWord16host (s+3)+ loop (s+4) (n-4)++putWord16N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ putWord16host (s+2)+ putWord16host (s+3)+ putWord16host (s+4)+ putWord16host (s+5)+ putWord16host (s+6)+ putWord16host (s+7)+ loop (s+8) (n-8)++putWord16N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ putWord16host (s+2)+ putWord16host (s+3)+ putWord16host (s+4)+ putWord16host (s+5)+ putWord16host (s+6)+ putWord16host (s+7)+ putWord16host (s+8)+ putWord16host (s+9)+ putWord16host (s+10)+ putWord16host (s+11)+ putWord16host (s+12)+ putWord16host (s+13)+ putWord16host (s+14)+ putWord16host (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord32N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ loop (s+1) (n-1)++putWord32N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ loop (s+2) (n-2)++putWord32N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ putWord32be (s+2)+ putWord32be (s+3)+ loop (s+4) (n-4)++putWord32N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ putWord32be (s+2)+ putWord32be (s+3)+ putWord32be (s+4)+ putWord32be (s+5)+ putWord32be (s+6)+ putWord32be (s+7)+ loop (s+8) (n-8)++putWord32N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ putWord32be (s+2)+ putWord32be (s+3)+ putWord32be (s+4)+ putWord32be (s+5)+ putWord32be (s+6)+ putWord32be (s+7)+ putWord32be (s+8)+ putWord32be (s+9)+ putWord32be (s+10)+ putWord32be (s+11)+ putWord32be (s+12)+ putWord32be (s+13)+ putWord32be (s+14)+ putWord32be (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord32N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ loop (s+1) (n-1)++putWord32N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ loop (s+2) (n-2)++putWord32N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ putWord32le (s+2)+ putWord32le (s+3)+ loop (s+4) (n-4)++putWord32N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ putWord32le (s+2)+ putWord32le (s+3)+ putWord32le (s+4)+ putWord32le (s+5)+ putWord32le (s+6)+ putWord32le (s+7)+ loop (s+8) (n-8)++putWord32N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ putWord32le (s+2)+ putWord32le (s+3)+ putWord32le (s+4)+ putWord32le (s+5)+ putWord32le (s+6)+ putWord32le (s+7)+ putWord32le (s+8)+ putWord32le (s+9)+ putWord32le (s+10)+ putWord32le (s+11)+ putWord32le (s+12)+ putWord32le (s+13)+ putWord32le (s+14)+ putWord32le (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord32N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ loop (s+1) (n-1)++putWord32N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ loop (s+2) (n-2)++putWord32N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ putWord32host (s+2)+ putWord32host (s+3)+ loop (s+4) (n-4)++putWord32N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ putWord32host (s+2)+ putWord32host (s+3)+ putWord32host (s+4)+ putWord32host (s+5)+ putWord32host (s+6)+ putWord32host (s+7)+ loop (s+8) (n-8)++putWord32N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ putWord32host (s+2)+ putWord32host (s+3)+ putWord32host (s+4)+ putWord32host (s+5)+ putWord32host (s+6)+ putWord32host (s+7)+ putWord32host (s+8)+ putWord32host (s+9)+ putWord32host (s+10)+ putWord32host (s+11)+ putWord32host (s+12)+ putWord32host (s+13)+ putWord32host (s+14)+ putWord32host (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord64N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ loop (s+1) (n-1)++putWord64N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ loop (s+2) (n-2)++putWord64N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ putWord64be (s+2)+ putWord64be (s+3)+ loop (s+4) (n-4)++putWord64N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ putWord64be (s+2)+ putWord64be (s+3)+ putWord64be (s+4)+ putWord64be (s+5)+ putWord64be (s+6)+ putWord64be (s+7)+ loop (s+8) (n-8)++putWord64N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ putWord64be (s+2)+ putWord64be (s+3)+ putWord64be (s+4)+ putWord64be (s+5)+ putWord64be (s+6)+ putWord64be (s+7)+ putWord64be (s+8)+ putWord64be (s+9)+ putWord64be (s+10)+ putWord64be (s+11)+ putWord64be (s+12)+ putWord64be (s+13)+ putWord64be (s+14)+ putWord64be (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord64N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ loop (s+1) (n-1)++putWord64N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ loop (s+2) (n-2)++putWord64N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ putWord64le (s+2)+ putWord64le (s+3)+ loop (s+4) (n-4)++putWord64N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ putWord64le (s+2)+ putWord64le (s+3)+ putWord64le (s+4)+ putWord64le (s+5)+ putWord64le (s+6)+ putWord64le (s+7)+ loop (s+8) (n-8)++putWord64N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ putWord64le (s+2)+ putWord64le (s+3)+ putWord64le (s+4)+ putWord64le (s+5)+ putWord64le (s+6)+ putWord64le (s+7)+ putWord64le (s+8)+ putWord64le (s+9)+ putWord64le (s+10)+ putWord64le (s+11)+ putWord64le (s+12)+ putWord64le (s+13)+ putWord64le (s+14)+ putWord64le (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord64N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ loop (s+1) (n-1)++putWord64N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ loop (s+2) (n-2)++putWord64N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ putWord64host (s+2)+ putWord64host (s+3)+ loop (s+4) (n-4)++putWord64N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ putWord64host (s+2)+ putWord64host (s+3)+ putWord64host (s+4)+ putWord64host (s+5)+ putWord64host (s+6)+ putWord64host (s+7)+ loop (s+8) (n-8)++putWord64N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ putWord64host (s+2)+ putWord64host (s+3)+ putWord64host (s+4)+ putWord64host (s+5)+ putWord64host (s+6)+ putWord64host (s+7)+ putWord64host (s+8)+ putWord64host (s+9)+ putWord64host (s+10)+ putWord64host (s+11)+ putWord64host (s+12)+ putWord64host (s+13)+ putWord64host (s+14)+ putWord64host (s+15)+ loop (s+16) (n-16)+
+ benchmarks/Throughput/BlazeBuilder.hs view
@@ -0,0 +1,702 @@+{-# LANGUAGE BangPatterns #-}+module Throughput.BlazeBuilder (+ serialize+) where++import Data.Monoid+import qualified Data.ByteString.Lazy as L++import Blaze.ByteString.Builder++import Throughput.Utils++serialize :: Int -> Int -> Endian -> Int -> L.ByteString+serialize wordSize chunkSize end = toLazyByteString . + case (wordSize, chunkSize, end) of+ (1, 1,_) -> writeByteN1+ (1, 2,_) -> writeByteN2+ (1, 4,_) -> writeByteN4+ (1, 8,_) -> writeByteN8+ (1, 16, _) -> writeByteN16++ (2, 1, Big) -> writeWord16N1Big+ (2, 2, Big) -> writeWord16N2Big+ (2, 4, Big) -> writeWord16N4Big+ (2, 8, Big) -> writeWord16N8Big+ (2, 16, Big) -> writeWord16N16Big+ (2, 1, Little) -> writeWord16N1Little+ (2, 2, Little) -> writeWord16N2Little+ (2, 4, Little) -> writeWord16N4Little+ (2, 8, Little) -> writeWord16N8Little+ (2, 16, Little) -> writeWord16N16Little+ (2, 1, Host) -> writeWord16N1Host+ (2, 2, Host) -> writeWord16N2Host+ (2, 4, Host) -> writeWord16N4Host+ (2, 8, Host) -> writeWord16N8Host+ (2, 16, Host) -> writeWord16N16Host++ (4, 1, Big) -> writeWord32N1Big+ (4, 2, Big) -> writeWord32N2Big+ (4, 4, Big) -> writeWord32N4Big+ (4, 8, Big) -> writeWord32N8Big+ (4, 16, Big) -> writeWord32N16Big+ (4, 1, Little) -> writeWord32N1Little+ (4, 2, Little) -> writeWord32N2Little+ (4, 4, Little) -> writeWord32N4Little+ (4, 8, Little) -> writeWord32N8Little+ (4, 16, Little) -> writeWord32N16Little+ (4, 1, Host) -> writeWord32N1Host+ (4, 2, Host) -> writeWord32N2Host+ (4, 4, Host) -> writeWord32N4Host+ (4, 8, Host) -> writeWord32N8Host+ (4, 16, Host) -> writeWord32N16Host++ (8, 1, Host) -> writeWord64N1Host+ (8, 2, Host) -> writeWord64N2Host+ (8, 4, Host) -> writeWord64N4Host+ (8, 8, Host) -> writeWord64N8Host+ (8, 16, Host) -> writeWord64N16Host+ (8, 1, Big) -> writeWord64N1Big+ (8, 2, Big) -> writeWord64N2Big+ (8, 4, Big) -> writeWord64N4Big+ (8, 8, Big) -> writeWord64N8Big+ (8, 16, Big) -> writeWord64N16Big+ (8, 1, Little) -> writeWord64N1Little+ (8, 2, Little) -> writeWord64N2Little+ (8, 4, Little) -> writeWord64N4Little+ (8, 8, Little) -> writeWord64N8Little+ (8, 16, Little) -> writeWord64N16Little++------------------------------------------------------------------------+++------------------------------------------------------------------------++writeByteN1 bytes = loop 0 0+ where loop !s !n | n == bytes = mempty+ | otherwise = fromWord8 s `mappend`+ loop (s+1) (n+1)++writeByteN2 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1)) `mappend`+ loop (s+2) (n-2)++writeByteN4 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1) `mappend`+ writeWord8 (s+2) `mappend`+ writeWord8 (s+3)) `mappend`+ loop (s+4) (n-4)++writeByteN8 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1) `mappend`+ writeWord8 (s+2) `mappend`+ writeWord8 (s+3) `mappend`+ writeWord8 (s+4) `mappend`+ writeWord8 (s+5) `mappend`+ writeWord8 (s+6) `mappend`+ writeWord8 (s+7)) `mappend`+ loop (s+8) (n-8)++writeByteN16 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1) `mappend`+ writeWord8 (s+2) `mappend`+ writeWord8 (s+3) `mappend`+ writeWord8 (s+4) `mappend`+ writeWord8 (s+5) `mappend`+ writeWord8 (s+6) `mappend`+ writeWord8 (s+7) `mappend`+ writeWord8 (s+8) `mappend`+ writeWord8 (s+9) `mappend`+ writeWord8 (s+10) `mappend`+ writeWord8 (s+11) `mappend`+ writeWord8 (s+12) `mappend`+ writeWord8 (s+13) `mappend`+ writeWord8 (s+14) `mappend`+ writeWord8 (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord16N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16be (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord16N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord16N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1) `mappend`+ writeWord16be (s+2) `mappend`+ writeWord16be (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord16N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1) `mappend`+ writeWord16be (s+2) `mappend`+ writeWord16be (s+3) `mappend`+ writeWord16be (s+4) `mappend`+ writeWord16be (s+5) `mappend`+ writeWord16be (s+6) `mappend`+ writeWord16be (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord16N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1) `mappend`+ writeWord16be (s+2) `mappend`+ writeWord16be (s+3) `mappend`+ writeWord16be (s+4) `mappend`+ writeWord16be (s+5) `mappend`+ writeWord16be (s+6) `mappend`+ writeWord16be (s+7) `mappend`+ writeWord16be (s+8) `mappend`+ writeWord16be (s+9) `mappend`+ writeWord16be (s+10) `mappend`+ writeWord16be (s+11) `mappend`+ writeWord16be (s+12) `mappend`+ writeWord16be (s+13) `mappend`+ writeWord16be (s+14) `mappend`+ writeWord16be (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Little endian, word16 writes++writeWord16N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = + fromWrite (writeWord16le (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord16N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord16N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1) `mappend`+ writeWord16le (s+2) `mappend`+ writeWord16le (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord16N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1) `mappend`+ writeWord16le (s+2) `mappend`+ writeWord16le (s+3) `mappend`+ writeWord16le (s+4) `mappend`+ writeWord16le (s+5) `mappend`+ writeWord16le (s+6) `mappend`+ writeWord16le (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord16N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1) `mappend`+ writeWord16le (s+2) `mappend`+ writeWord16le (s+3) `mappend`+ writeWord16le (s+4) `mappend`+ writeWord16le (s+5) `mappend`+ writeWord16le (s+6) `mappend`+ writeWord16le (s+7) `mappend`+ writeWord16le (s+8) `mappend`+ writeWord16le (s+9) `mappend`+ writeWord16le (s+10) `mappend`+ writeWord16le (s+11) `mappend`+ writeWord16le (s+12) `mappend`+ writeWord16le (s+13) `mappend`+ writeWord16le (s+14) `mappend`+ writeWord16le (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Host endian, unaligned, word16 writes++writeWord16N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16host (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord16N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord16N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1) `mappend`+ writeWord16host (s+2) `mappend`+ writeWord16host (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord16N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1) `mappend`+ writeWord16host (s+2) `mappend`+ writeWord16host (s+3) `mappend`+ writeWord16host (s+4) `mappend`+ writeWord16host (s+5) `mappend`+ writeWord16host (s+6) `mappend`+ writeWord16host (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord16N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1) `mappend`+ writeWord16host (s+2) `mappend`+ writeWord16host (s+3) `mappend`+ writeWord16host (s+4) `mappend`+ writeWord16host (s+5) `mappend`+ writeWord16host (s+6) `mappend`+ writeWord16host (s+7) `mappend`+ writeWord16host (s+8) `mappend`+ writeWord16host (s+9) `mappend`+ writeWord16host (s+10) `mappend`+ writeWord16host (s+11) `mappend`+ writeWord16host (s+12) `mappend`+ writeWord16host (s+13) `mappend`+ writeWord16host (s+14) `mappend`+ writeWord16host (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32be (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord32N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord32N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1) `mappend`+ writeWord32be (s+2) `mappend`+ writeWord32be (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord32N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1) `mappend`+ writeWord32be (s+2) `mappend`+ writeWord32be (s+3) `mappend`+ writeWord32be (s+4) `mappend`+ writeWord32be (s+5) `mappend`+ writeWord32be (s+6) `mappend`+ writeWord32be (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord32N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1) `mappend`+ writeWord32be (s+2) `mappend`+ writeWord32be (s+3) `mappend`+ writeWord32be (s+4) `mappend`+ writeWord32be (s+5) `mappend`+ writeWord32be (s+6) `mappend`+ writeWord32be (s+7) `mappend`+ writeWord32be (s+8) `mappend`+ writeWord32be (s+9) `mappend`+ writeWord32be (s+10) `mappend`+ writeWord32be (s+11) `mappend`+ writeWord32be (s+12) `mappend`+ writeWord32be (s+13) `mappend`+ writeWord32be (s+14) `mappend`+ writeWord32be (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32le (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord32N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord32N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1) `mappend`+ writeWord32le (s+2) `mappend`+ writeWord32le (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord32N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1) `mappend`+ writeWord32le (s+2) `mappend`+ writeWord32le (s+3) `mappend`+ writeWord32le (s+4) `mappend`+ writeWord32le (s+5) `mappend`+ writeWord32le (s+6) `mappend`+ writeWord32le (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord32N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1) `mappend`+ writeWord32le (s+2) `mappend`+ writeWord32le (s+3) `mappend`+ writeWord32le (s+4) `mappend`+ writeWord32le (s+5) `mappend`+ writeWord32le (s+6) `mappend`+ writeWord32le (s+7) `mappend`+ writeWord32le (s+8) `mappend`+ writeWord32le (s+9) `mappend`+ writeWord32le (s+10) `mappend`+ writeWord32le (s+11) `mappend`+ writeWord32le (s+12) `mappend`+ writeWord32le (s+13) `mappend`+ writeWord32le (s+14) `mappend`+ writeWord32le (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32host (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord32N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord32N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1) `mappend`+ writeWord32host (s+2) `mappend`+ writeWord32host (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord32N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1) `mappend`+ writeWord32host (s+2) `mappend`+ writeWord32host (s+3) `mappend`+ writeWord32host (s+4) `mappend`+ writeWord32host (s+5) `mappend`+ writeWord32host (s+6) `mappend`+ writeWord32host (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord32N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1) `mappend`+ writeWord32host (s+2) `mappend`+ writeWord32host (s+3) `mappend`+ writeWord32host (s+4) `mappend`+ writeWord32host (s+5) `mappend`+ writeWord32host (s+6) `mappend`+ writeWord32host (s+7) `mappend`+ writeWord32host (s+8) `mappend`+ writeWord32host (s+9) `mappend`+ writeWord32host (s+10) `mappend`+ writeWord32host (s+11) `mappend`+ writeWord32host (s+12) `mappend`+ writeWord32host (s+13) `mappend`+ writeWord32host (s+14) `mappend`+ writeWord32host (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64be (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord64N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord64N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1) `mappend`+ writeWord64be (s+2) `mappend`+ writeWord64be (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord64N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1) `mappend`+ writeWord64be (s+2) `mappend`+ writeWord64be (s+3) `mappend`+ writeWord64be (s+4) `mappend`+ writeWord64be (s+5) `mappend`+ writeWord64be (s+6) `mappend`+ writeWord64be (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord64N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1) `mappend`+ writeWord64be (s+2) `mappend`+ writeWord64be (s+3) `mappend`+ writeWord64be (s+4) `mappend`+ writeWord64be (s+5) `mappend`+ writeWord64be (s+6) `mappend`+ writeWord64be (s+7) `mappend`+ writeWord64be (s+8) `mappend`+ writeWord64be (s+9) `mappend`+ writeWord64be (s+10) `mappend`+ writeWord64be (s+11) `mappend`+ writeWord64be (s+12) `mappend`+ writeWord64be (s+13) `mappend`+ writeWord64be (s+14) `mappend`+ writeWord64be (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64le (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord64N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord64N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1) `mappend`+ writeWord64le (s+2) `mappend`+ writeWord64le (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord64N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1) `mappend`+ writeWord64le (s+2) `mappend`+ writeWord64le (s+3) `mappend`+ writeWord64le (s+4) `mappend`+ writeWord64le (s+5) `mappend`+ writeWord64le (s+6) `mappend`+ writeWord64le (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord64N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1) `mappend`+ writeWord64le (s+2) `mappend`+ writeWord64le (s+3) `mappend`+ writeWord64le (s+4) `mappend`+ writeWord64le (s+5) `mappend`+ writeWord64le (s+6) `mappend`+ writeWord64le (s+7) `mappend`+ writeWord64le (s+8) `mappend`+ writeWord64le (s+9) `mappend`+ writeWord64le (s+10) `mappend`+ writeWord64le (s+11) `mappend`+ writeWord64le (s+12) `mappend`+ writeWord64le (s+13) `mappend`+ writeWord64le (s+14) `mappend`+ writeWord64le (s+15)) `mappend`+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64host (s+0)) `mappend`+ loop (s+1) (n-1)++writeWord64N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1)) `mappend`+ loop (s+2) (n-2)++writeWord64N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1) `mappend`+ writeWord64host (s+2) `mappend`+ writeWord64host (s+3)) `mappend`+ loop (s+4) (n-4)++writeWord64N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1) `mappend`+ writeWord64host (s+2) `mappend`+ writeWord64host (s+3) `mappend`+ writeWord64host (s+4) `mappend`+ writeWord64host (s+5) `mappend`+ writeWord64host (s+6) `mappend`+ writeWord64host (s+7)) `mappend`+ loop (s+8) (n-8)++writeWord64N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n = fromWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1) `mappend`+ writeWord64host (s+2) `mappend`+ writeWord64host (s+3) `mappend`+ writeWord64host (s+4) `mappend`+ writeWord64host (s+5) `mappend`+ writeWord64host (s+6) `mappend`+ writeWord64host (s+7) `mappend`+ writeWord64host (s+8) `mappend`+ writeWord64host (s+9) `mappend`+ writeWord64host (s+10) `mappend`+ writeWord64host (s+11) `mappend`+ writeWord64host (s+12) `mappend`+ writeWord64host (s+13) `mappend`+ writeWord64host (s+14) `mappend`+ writeWord64host (s+15)) `mappend`+ loop (s+16) (n-16)
+ benchmarks/Throughput/BlazeBuilderDeclarative.hs view
@@ -0,0 +1,197 @@+{-# LANGUAGE BangPatterns #-}+module Throughput.BlazeBuilderDeclarative (+ serialize+) where++import Data.Monoid+import Data.Word+import qualified Data.ByteString.Lazy as L++import Blaze.ByteString.Builder++import Throughput.Utils++serialize :: Int -> Int -> Endian -> Int -> L.ByteString+serialize wordSize chunkSize end = toLazyByteString . + case (wordSize, chunkSize, end) of+ (1, 1,_) -> writeByteN1+ (1, 2,_) -> writeByteN2+ (1, 4,_) -> writeByteN4+ (1, 8,_) -> writeByteN8+ (1, 16, _) -> writeByteN16++ (2, 1, Big) -> writeWord16N1Big+ (2, 2, Big) -> writeWord16N2Big+ (2, 4, Big) -> writeWord16N4Big+ (2, 8, Big) -> writeWord16N8Big+ (2, 16, Big) -> writeWord16N16Big+ (2, 1, Little) -> writeWord16N1Little+ (2, 2, Little) -> writeWord16N2Little+ (2, 4, Little) -> writeWord16N4Little+ (2, 8, Little) -> writeWord16N8Little+ (2, 16, Little) -> writeWord16N16Little+ (2, 1, Host) -> writeWord16N1Host+ (2, 2, Host) -> writeWord16N2Host+ (2, 4, Host) -> writeWord16N4Host+ (2, 8, Host) -> writeWord16N8Host+ (2, 16, Host) -> writeWord16N16Host++ (4, 1, Big) -> writeWord32N1Big+ (4, 2, Big) -> writeWord32N2Big+ (4, 4, Big) -> writeWord32N4Big+ (4, 8, Big) -> writeWord32N8Big+ (4, 16, Big) -> writeWord32N16Big+ (4, 1, Little) -> writeWord32N1Little+ (4, 2, Little) -> writeWord32N2Little+ (4, 4, Little) -> writeWord32N4Little+ (4, 8, Little) -> writeWord32N8Little+ (4, 16, Little) -> writeWord32N16Little+ (4, 1, Host) -> writeWord32N1Host+ (4, 2, Host) -> writeWord32N2Host+ (4, 4, Host) -> writeWord32N4Host+ (4, 8, Host) -> writeWord32N8Host+ (4, 16, Host) -> writeWord32N16Host++ (8, 1, Host) -> writeWord64N1Host+ (8, 2, Host) -> writeWord64N2Host+ (8, 4, Host) -> writeWord64N4Host+ (8, 8, Host) -> writeWord64N8Host+ (8, 16, Host) -> writeWord64N16Host+ (8, 1, Big) -> writeWord64N1Big+ (8, 2, Big) -> writeWord64N2Big+ (8, 4, Big) -> writeWord64N4Big+ (8, 8, Big) -> writeWord64N8Big+ (8, 16, Big) -> writeWord64N16Big+ (8, 1, Little) -> writeWord64N1Little+ (8, 2, Little) -> writeWord64N2Little+ (8, 4, Little) -> writeWord64N4Little+ (8, 8, Little) -> writeWord64N8Little+ (8, 16, Little) -> writeWord64N16Little++------------------------------------------------------------------------+-- Word8+------------------------------------------------------------------------++word8List :: Int -> [Word8]+word8List n = take n $ cycle $ [0..]++------------------------------------------------------------------------++writeByteN1 = fromWrite1List writeWord8 . word8List+writeByteN2 = fromWrite2List writeWord8 . word8List+writeByteN4 = fromWrite4List writeWord8 . word8List+writeByteN8 = fromWrite8List writeWord8 . word8List+writeByteN16 = fromWrite16List writeWord8 . word8List+++------------------------------------------------------------------------+-- Word16+------------------------------------------------------------------------++word16List :: Int -> [Word16]+word16List n = take n $ cycle $ [0..]++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord16N1Big = fromWrite1List writeWord16be . word16List+writeWord16N2Big = fromWrite2List writeWord16be . word16List+writeWord16N4Big = fromWrite4List writeWord16be . word16List+writeWord16N8Big = fromWrite8List writeWord16be . word16List+writeWord16N16Big = fromWrite16List writeWord16be . word16List+++------------------------------------------------------------------------+-- Little endian, word16 writes++writeWord16N1Little = fromWrite1List writeWord16le . word16List+writeWord16N2Little = fromWrite2List writeWord16le . word16List+writeWord16N4Little = fromWrite4List writeWord16le . word16List+writeWord16N8Little = fromWrite8List writeWord16le . word16List+writeWord16N16Little = fromWrite16List writeWord16le . word16List+++------------------------------------------------------------------------+-- Host endian, unaligned, word16 writes++writeWord16N1Host = fromWrite1List writeWord16host . word16List+writeWord16N2Host = fromWrite2List writeWord16host . word16List+writeWord16N4Host = fromWrite4List writeWord16host . word16List+writeWord16N8Host = fromWrite8List writeWord16host . word16List+writeWord16N16Host = fromWrite16List writeWord16host . word16List+++------------------------------------------------------------------------+-- Word32+------------------------------------------------------------------------++word32List :: Int -> [Word32]+word32List n = [0..fromIntegral (n-1)]++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord32N1Big = fromWrite1List writeWord32be . word32List+writeWord32N2Big = fromWrite2List writeWord32be . word32List+writeWord32N4Big = fromWrite4List writeWord32be . word32List+writeWord32N8Big = fromWrite8List writeWord32be . word32List+writeWord32N16Big = fromWrite16List writeWord32be . word32List+++------------------------------------------------------------------------+-- Little endian, word32 writes++writeWord32N1Little = fromWrite1List writeWord32le . word32List+writeWord32N2Little = fromWrite2List writeWord32le . word32List+writeWord32N4Little = fromWrite4List writeWord32le . word32List+writeWord32N8Little = fromWrite8List writeWord32le . word32List+writeWord32N16Little = fromWrite16List writeWord32le . word32List+++------------------------------------------------------------------------+-- Host endian, unaligned, word32 writes++writeWord32N1Host = fromWrite1List writeWord32host . word32List+writeWord32N2Host = fromWrite2List writeWord32host . word32List+writeWord32N4Host = fromWrite4List writeWord32host . word32List+writeWord32N8Host = fromWrite8List writeWord32host . word32List+writeWord32N16Host = fromWrite16List writeWord32host . word32List+++------------------------------------------------------------------------+-- Word64+------------------------------------------------------------------------++word64List :: Int -> [Word64]+word64List n = [0..fromIntegral (n-1)]++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord64N1Big = fromWrite1List writeWord64be . word64List+writeWord64N2Big = fromWrite2List writeWord64be . word64List+writeWord64N4Big = fromWrite4List writeWord64be . word64List+writeWord64N8Big = fromWrite8List writeWord64be . word64List+writeWord64N16Big = fromWrite16List writeWord64be . word64List+++------------------------------------------------------------------------+-- Little endian, word64 writes++writeWord64N1Little = fromWrite1List writeWord64le . word64List+writeWord64N2Little = fromWrite2List writeWord64le . word64List+writeWord64N4Little = fromWrite4List writeWord64le . word64List+writeWord64N8Little = fromWrite8List writeWord64le . word64List+writeWord64N16Little = fromWrite16List writeWord64le . word64List+++------------------------------------------------------------------------+-- Host endian, unaligned, word64 writes++writeWord64N1Host = fromWrite1List writeWord64host . word64List+writeWord64N2Host = fromWrite2List writeWord64host . word64List+writeWord64N4Host = fromWrite4List writeWord64host . word64List+writeWord64N8Host = fromWrite8List writeWord64host . word64List+writeWord64N16Host = fromWrite16List writeWord64host . word64List++
+ benchmarks/Throughput/BlazePut.hs view
@@ -0,0 +1,742 @@+{-# LANGUAGE BangPatterns #-}+module Throughput.BlazePut (serialize) where++import qualified Data.ByteString.Lazy as L+import Blaze.ByteString.Builder +import Throughput.BlazePutMonad as Put+import Data.Monoid++import Throughput.Utils+++------------------------------------------------------------------------++serialize :: Int -> Int -> Endian -> Int -> L.ByteString+serialize wordSize chunkSize end = runPut . + case (wordSize, chunkSize, end) of+ (1, 1,_) -> writeByteN1+ (1, 2,_) -> writeByteN2+ (1, 4,_) -> writeByteN4+ (1, 8,_) -> writeByteN8+ (1, 16, _) -> writeByteN16++ (2, 1, Big) -> writeWord16N1Big+ (2, 2, Big) -> writeWord16N2Big+ (2, 4, Big) -> writeWord16N4Big+ (2, 8, Big) -> writeWord16N8Big+ (2, 16, Big) -> writeWord16N16Big+ (2, 1, Little) -> writeWord16N1Little+ (2, 2, Little) -> writeWord16N2Little+ (2, 4, Little) -> writeWord16N4Little+ (2, 8, Little) -> writeWord16N8Little+ (2, 16, Little) -> writeWord16N16Little+ (2, 1, Host) -> writeWord16N1Host+ (2, 2, Host) -> writeWord16N2Host+ (2, 4, Host) -> writeWord16N4Host+ (2, 8, Host) -> writeWord16N8Host+ (2, 16, Host) -> writeWord16N16Host++ (4, 1, Big) -> writeWord32N1Big+ (4, 2, Big) -> writeWord32N2Big+ (4, 4, Big) -> writeWord32N4Big+ (4, 8, Big) -> writeWord32N8Big+ (4, 16, Big) -> writeWord32N16Big+ (4, 1, Little) -> writeWord32N1Little+ (4, 2, Little) -> writeWord32N2Little+ (4, 4, Little) -> writeWord32N4Little+ (4, 8, Little) -> writeWord32N8Little+ (4, 16, Little) -> writeWord32N16Little+ (4, 1, Host) -> writeWord32N1Host+ (4, 2, Host) -> writeWord32N2Host+ (4, 4, Host) -> writeWord32N4Host+ (4, 8, Host) -> writeWord32N8Host+ (4, 16, Host) -> writeWord32N16Host++ (8, 1, Host) -> writeWord64N1Host+ (8, 2, Host) -> writeWord64N2Host+ (8, 4, Host) -> writeWord64N4Host+ (8, 8, Host) -> writeWord64N8Host+ (8, 16, Host) -> writeWord64N16Host+ (8, 1, Big) -> writeWord64N1Big+ (8, 2, Big) -> writeWord64N2Big+ (8, 4, Big) -> writeWord64N4Big+ (8, 8, Big) -> writeWord64N8Big+ (8, 16, Big) -> writeWord64N16Big+ (8, 1, Little) -> writeWord64N1Little+ (8, 2, Little) -> writeWord64N2Little+ (8, 4, Little) -> writeWord64N4Little+ (8, 8, Little) -> writeWord64N8Little+ (8, 16, Little) -> writeWord64N16Little++------------------------------------------------------------------------++writeByteN1 bytes = loop 0 0+ where loop !s !n | n == bytes = return ()+ | otherwise = do + Put.putWrite ( writeWord8 s)+ loop (s+1) (n+1)++writeByteN2 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ do Put.putWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1))+ loop (s+2) (n-2)++writeByteN4 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1) `mappend`+ writeWord8 (s+2) `mappend`+ writeWord8 (s+3))+ loop (s+4) (n-4)++writeByteN8 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1) `mappend`+ writeWord8 (s+2) `mappend`+ writeWord8 (s+3) `mappend`+ writeWord8 (s+4) `mappend`+ writeWord8 (s+5) `mappend`+ writeWord8 (s+6) `mappend`+ writeWord8 (s+7))+ loop (s+8) (n-8)++writeByteN16 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord8 (s+0) `mappend`+ writeWord8 (s+1) `mappend`+ writeWord8 (s+2) `mappend`+ writeWord8 (s+3) `mappend`+ writeWord8 (s+4) `mappend`+ writeWord8 (s+5) `mappend`+ writeWord8 (s+6) `mappend`+ writeWord8 (s+7) `mappend`+ writeWord8 (s+8) `mappend`+ writeWord8 (s+9) `mappend`+ writeWord8 (s+10) `mappend`+ writeWord8 (s+11) `mappend`+ writeWord8 (s+12) `mappend`+ writeWord8 (s+13) `mappend`+ writeWord8 (s+14) `mappend`+ writeWord8 (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Big endian, word16 writes++writeWord16N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord16be (s+0)+ loop (s+1) (n-1)++writeWord16N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1))+ loop (s+2) (n-2)++writeWord16N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1) `mappend`+ writeWord16be (s+2) `mappend`+ writeWord16be (s+3))+ loop (s+4) (n-4)++writeWord16N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1) `mappend`+ writeWord16be (s+2) `mappend`+ writeWord16be (s+3) `mappend`+ writeWord16be (s+4) `mappend`+ writeWord16be (s+5) `mappend`+ writeWord16be (s+6) `mappend`+ writeWord16be (s+7))+ loop (s+8) (n-8)++writeWord16N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16be (s+0) `mappend`+ writeWord16be (s+1) `mappend`+ writeWord16be (s+2) `mappend`+ writeWord16be (s+3) `mappend`+ writeWord16be (s+4) `mappend`+ writeWord16be (s+5) `mappend`+ writeWord16be (s+6) `mappend`+ writeWord16be (s+7) `mappend`+ writeWord16be (s+8) `mappend`+ writeWord16be (s+9) `mappend`+ writeWord16be (s+10) `mappend`+ writeWord16be (s+11) `mappend`+ writeWord16be (s+12) `mappend`+ writeWord16be (s+13) `mappend`+ writeWord16be (s+14) `mappend`+ writeWord16be (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Little endian, word16 writes++writeWord16N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = + do Put.putWord16le (s+0)+ loop (s+1) (n-1)++writeWord16N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1))+ loop (s+2) (n-2)++writeWord16N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1) `mappend`+ writeWord16le (s+2) `mappend`+ writeWord16le (s+3))+ loop (s+4) (n-4)++writeWord16N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1) `mappend`+ writeWord16le (s+2) `mappend`+ writeWord16le (s+3) `mappend`+ writeWord16le (s+4) `mappend`+ writeWord16le (s+5) `mappend`+ writeWord16le (s+6) `mappend`+ writeWord16le (s+7))+ loop (s+8) (n-8)++writeWord16N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16le (s+0) `mappend`+ writeWord16le (s+1) `mappend`+ writeWord16le (s+2) `mappend`+ writeWord16le (s+3) `mappend`+ writeWord16le (s+4) `mappend`+ writeWord16le (s+5) `mappend`+ writeWord16le (s+6) `mappend`+ writeWord16le (s+7) `mappend`+ writeWord16le (s+8) `mappend`+ writeWord16le (s+9) `mappend`+ writeWord16le (s+10) `mappend`+ writeWord16le (s+11) `mappend`+ writeWord16le (s+12) `mappend`+ writeWord16le (s+13) `mappend`+ writeWord16le (s+14) `mappend`+ writeWord16le (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Host endian, unaligned, word16 writes++writeWord16N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord16host (s+0)+ loop (s+1) (n-1)++writeWord16N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1))+ loop (s+2) (n-2)++writeWord16N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1) `mappend`+ writeWord16host (s+2) `mappend`+ writeWord16host (s+3))+ loop (s+4) (n-4)++writeWord16N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1) `mappend`+ writeWord16host (s+2) `mappend`+ writeWord16host (s+3) `mappend`+ writeWord16host (s+4) `mappend`+ writeWord16host (s+5) `mappend`+ writeWord16host (s+6) `mappend`+ writeWord16host (s+7))+ loop (s+8) (n-8)++writeWord16N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord16host (s+0) `mappend`+ writeWord16host (s+1) `mappend`+ writeWord16host (s+2) `mappend`+ writeWord16host (s+3) `mappend`+ writeWord16host (s+4) `mappend`+ writeWord16host (s+5) `mappend`+ writeWord16host (s+6) `mappend`+ writeWord16host (s+7) `mappend`+ writeWord16host (s+8) `mappend`+ writeWord16host (s+9) `mappend`+ writeWord16host (s+10) `mappend`+ writeWord16host (s+11) `mappend`+ writeWord16host (s+12) `mappend`+ writeWord16host (s+13) `mappend`+ writeWord16host (s+14) `mappend`+ writeWord16host (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord32be (s+0)+ loop (s+1) (n-1)++writeWord32N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1))+ loop (s+2) (n-2)++writeWord32N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1) `mappend`+ writeWord32be (s+2) `mappend`+ writeWord32be (s+3))+ loop (s+4) (n-4)++writeWord32N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1) `mappend`+ writeWord32be (s+2) `mappend`+ writeWord32be (s+3) `mappend`+ writeWord32be (s+4) `mappend`+ writeWord32be (s+5) `mappend`+ writeWord32be (s+6) `mappend`+ writeWord32be (s+7))+ loop (s+8) (n-8)++writeWord32N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32be (s+0) `mappend`+ writeWord32be (s+1) `mappend`+ writeWord32be (s+2) `mappend`+ writeWord32be (s+3) `mappend`+ writeWord32be (s+4) `mappend`+ writeWord32be (s+5) `mappend`+ writeWord32be (s+6) `mappend`+ writeWord32be (s+7) `mappend`+ writeWord32be (s+8) `mappend`+ writeWord32be (s+9) `mappend`+ writeWord32be (s+10) `mappend`+ writeWord32be (s+11) `mappend`+ writeWord32be (s+12) `mappend`+ writeWord32be (s+13) `mappend`+ writeWord32be (s+14) `mappend`+ writeWord32be (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord32le (s+0)+ loop (s+1) (n-1)++writeWord32N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1))+ loop (s+2) (n-2)++writeWord32N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1) `mappend`+ writeWord32le (s+2) `mappend`+ writeWord32le (s+3))+ loop (s+4) (n-4)++writeWord32N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1) `mappend`+ writeWord32le (s+2) `mappend`+ writeWord32le (s+3) `mappend`+ writeWord32le (s+4) `mappend`+ writeWord32le (s+5) `mappend`+ writeWord32le (s+6) `mappend`+ writeWord32le (s+7))+ loop (s+8) (n-8)++writeWord32N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32le (s+0) `mappend`+ writeWord32le (s+1) `mappend`+ writeWord32le (s+2) `mappend`+ writeWord32le (s+3) `mappend`+ writeWord32le (s+4) `mappend`+ writeWord32le (s+5) `mappend`+ writeWord32le (s+6) `mappend`+ writeWord32le (s+7) `mappend`+ writeWord32le (s+8) `mappend`+ writeWord32le (s+9) `mappend`+ writeWord32le (s+10) `mappend`+ writeWord32le (s+11) `mappend`+ writeWord32le (s+12) `mappend`+ writeWord32le (s+13) `mappend`+ writeWord32le (s+14) `mappend`+ writeWord32le (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord32N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord32host (s+0)+ loop (s+1) (n-1)++writeWord32N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1))+ loop (s+2) (n-2)++writeWord32N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1) `mappend`+ writeWord32host (s+2) `mappend`+ writeWord32host (s+3))+ loop (s+4) (n-4)++writeWord32N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1) `mappend`+ writeWord32host (s+2) `mappend`+ writeWord32host (s+3) `mappend`+ writeWord32host (s+4) `mappend`+ writeWord32host (s+5) `mappend`+ writeWord32host (s+6) `mappend`+ writeWord32host (s+7))+ loop (s+8) (n-8)++writeWord32N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord32host (s+0) `mappend`+ writeWord32host (s+1) `mappend`+ writeWord32host (s+2) `mappend`+ writeWord32host (s+3) `mappend`+ writeWord32host (s+4) `mappend`+ writeWord32host (s+5) `mappend`+ writeWord32host (s+6) `mappend`+ writeWord32host (s+7) `mappend`+ writeWord32host (s+8) `mappend`+ writeWord32host (s+9) `mappend`+ writeWord32host (s+10) `mappend`+ writeWord32host (s+11) `mappend`+ writeWord32host (s+12) `mappend`+ writeWord32host (s+13) `mappend`+ writeWord32host (s+14) `mappend`+ writeWord32host (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord64be (s+0)+ loop (s+1) (n-1)++writeWord64N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1))+ loop (s+2) (n-2)++writeWord64N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1) `mappend`+ writeWord64be (s+2) `mappend`+ writeWord64be (s+3))+ loop (s+4) (n-4)++writeWord64N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1) `mappend`+ writeWord64be (s+2) `mappend`+ writeWord64be (s+3) `mappend`+ writeWord64be (s+4) `mappend`+ writeWord64be (s+5) `mappend`+ writeWord64be (s+6) `mappend`+ writeWord64be (s+7))+ loop (s+8) (n-8)++writeWord64N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64be (s+0) `mappend`+ writeWord64be (s+1) `mappend`+ writeWord64be (s+2) `mappend`+ writeWord64be (s+3) `mappend`+ writeWord64be (s+4) `mappend`+ writeWord64be (s+5) `mappend`+ writeWord64be (s+6) `mappend`+ writeWord64be (s+7) `mappend`+ writeWord64be (s+8) `mappend`+ writeWord64be (s+9) `mappend`+ writeWord64be (s+10) `mappend`+ writeWord64be (s+11) `mappend`+ writeWord64be (s+12) `mappend`+ writeWord64be (s+13) `mappend`+ writeWord64be (s+14) `mappend`+ writeWord64be (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord64le (s+0)+ loop (s+1) (n-1)++writeWord64N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1))+ loop (s+2) (n-2)++writeWord64N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1) `mappend`+ writeWord64le (s+2) `mappend`+ writeWord64le (s+3))+ loop (s+4) (n-4)++writeWord64N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1) `mappend`+ writeWord64le (s+2) `mappend`+ writeWord64le (s+3) `mappend`+ writeWord64le (s+4) `mappend`+ writeWord64le (s+5) `mappend`+ writeWord64le (s+6) `mappend`+ writeWord64le (s+7))+ loop (s+8) (n-8)++writeWord64N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64le (s+0) `mappend`+ writeWord64le (s+1) `mappend`+ writeWord64le (s+2) `mappend`+ writeWord64le (s+3) `mappend`+ writeWord64le (s+4) `mappend`+ writeWord64le (s+5) `mappend`+ writeWord64le (s+6) `mappend`+ writeWord64le (s+7) `mappend`+ writeWord64le (s+8) `mappend`+ writeWord64le (s+9) `mappend`+ writeWord64le (s+10) `mappend`+ writeWord64le (s+11) `mappend`+ writeWord64le (s+12) `mappend`+ writeWord64le (s+13) `mappend`+ writeWord64le (s+14) `mappend`+ writeWord64le (s+15))+ loop (s+16) (n-16)++------------------------------------------------------------------------++writeWord64N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWord64host (s+0)+ loop (s+1) (n-1)++writeWord64N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1))+ loop (s+2) (n-2)++writeWord64N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1) `mappend`+ writeWord64host (s+2) `mappend`+ writeWord64host (s+3))+ loop (s+4) (n-4)++writeWord64N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1) `mappend`+ writeWord64host (s+2) `mappend`+ writeWord64host (s+3) `mappend`+ writeWord64host (s+4) `mappend`+ writeWord64host (s+5) `mappend`+ writeWord64host (s+6) `mappend`+ writeWord64host (s+7))+ loop (s+8) (n-8)++writeWord64N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do + Put.putWrite (+ writeWord64host (s+0) `mappend`+ writeWord64host (s+1) `mappend`+ writeWord64host (s+2) `mappend`+ writeWord64host (s+3) `mappend`+ writeWord64host (s+4) `mappend`+ writeWord64host (s+5) `mappend`+ writeWord64host (s+6) `mappend`+ writeWord64host (s+7) `mappend`+ writeWord64host (s+8) `mappend`+ writeWord64host (s+9) `mappend`+ writeWord64host (s+10) `mappend`+ writeWord64host (s+11) `mappend`+ writeWord64host (s+12) `mappend`+ writeWord64host (s+13) `mappend`+ writeWord64host (s+14) `mappend`+ writeWord64host (s+15))+ loop (s+16) (n-16)+
+ benchmarks/Throughput/BlazePutMonad.hs view
@@ -0,0 +1,222 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Data.Binary.Put+-- Copyright : Lennart Kolmodin+-- License : BSD3-style (see LICENSE)+-- +-- Maintainer : Lennart Kolmodin <kolmodin@dtek.chalmers.se>+-- Stability : stable+-- Portability : Portable to Hugs and GHC. Requires MPTCs+--+-- The Put monad. A monad for efficiently constructing lazy bytestrings using+-- the Builder developed for blaze-html.+--+-----------------------------------------------------------------------------++module Throughput.BlazePutMonad (++ -- * The Put type+ Put+ , PutM(..)+ , runPut+ , runPutM+ , putBuilder+ , execPut++ -- * Flushing the implicit parse state+ , flush++ -- * Primitives+ , putWrite+ , putWord8+ , putByteString+ , putLazyByteString++ -- * Big-endian primitives+ , putWord16be+ , putWord32be+ , putWord64be++ -- * Little-endian primitives+ , putWord16le+ , putWord32le+ , putWord64le++ -- * Host-endian, unaligned writes+ , putWordhost -- :: Word -> Put+ , putWord16host -- :: Word16 -> Put+ , putWord32host -- :: Word32 -> Put+ , putWord64host -- :: Word64 -> Put++ ) where++import Data.Monoid+import Blaze.ByteString.Builder (Builder, toLazyByteString)+import qualified Blaze.ByteString.Builder as B++import Data.Word+import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L++#ifdef APPLICATIVE_IN_BASE+import Control.Applicative+#endif+++------------------------------------------------------------------------++-- XXX Strict in buffer only. +data PairS a = PairS a {-# UNPACK #-}!Builder++sndS :: PairS a -> Builder+sndS (PairS _ b) = b++-- | The PutM type. A Writer monad over the efficient Builder monoid.+newtype PutM a = Put { unPut :: PairS a }++-- | Put merely lifts Builder into a Writer monad, applied to ().+type Put = PutM ()++instance Functor PutM where+ fmap f m = Put $ let PairS a w = unPut m in PairS (f a) w+ {-# INLINE fmap #-}++#ifdef APPLICATIVE_IN_BASE+instance Applicative PutM where+ pure = return+ m <*> k = Put $+ let PairS f w = unPut m+ PairS x w' = unPut k+ in PairS (f x) (w `mappend` w')+#endif++-- Standard Writer monad, with aggressive inlining+instance Monad PutM where+ return a = Put $ PairS a mempty+ {-# INLINE return #-}++ m >>= k = Put $+ let PairS a w = unPut m+ PairS b w' = unPut (k a)+ in PairS b (w `mappend` w')+ {-# INLINE (>>=) #-}++ m >> k = Put $+ let PairS _ w = unPut m+ PairS b w' = unPut k+ in PairS b (w `mappend` w')+ {-# INLINE (>>) #-}++tell :: Builder -> Put+tell b = Put $ PairS () b+{-# INLINE tell #-}++putBuilder :: Builder -> Put+putBuilder = tell+{-# INLINE putBuilder #-}++-- | Run the 'Put' monad+execPut :: PutM a -> Builder+execPut = sndS . unPut+{-# INLINE execPut #-}++-- | Run the 'Put' monad with a serialiser+runPut :: Put -> L.ByteString+runPut = toLazyByteString . sndS . unPut+{-# INLINE runPut #-}++-- | Run the 'Put' monad with a serialiser and get its result+runPutM :: PutM a -> (a, L.ByteString)+runPutM (Put (PairS f s)) = (f, toLazyByteString s)+{-# INLINE runPutM #-}++------------------------------------------------------------------------++-- | Pop the ByteString we have constructed so far, if any, yielding a+-- new chunk in the result ByteString.+flush :: Put+flush = tell B.flush+{-# INLINE flush #-}++-- | Efficiently write a byte into the output buffer+putWord8 :: Word8 -> Put+putWord8 = tell . B.fromWord8+{-# INLINE putWord8 #-}++-- | Execute a write on the output buffer.+putWrite :: B.Write -> Put+putWrite = tell . B.fromWrite++-- | An efficient primitive to write a strict ByteString into the output buffer.+-- It flushes the current buffer, and writes the argument into a new chunk.+putByteString :: S.ByteString -> Put+putByteString = tell . B.fromByteString+{-# INLINE putByteString #-}++-- | Write a lazy ByteString efficiently, simply appending the lazy+-- ByteString chunks to the output buffer+putLazyByteString :: L.ByteString -> Put+putLazyByteString = tell . B.fromLazyByteString+{-# INLINE putLazyByteString #-}++-- | Write a Word16 in big endian format+putWord16be :: Word16 -> Put+putWord16be = tell . B.fromWord16be+{-# INLINE putWord16be #-}++-- | Write a Word16 in little endian format+putWord16le :: Word16 -> Put+putWord16le = tell . B.fromWord16le+{-# INLINE putWord16le #-}++-- | Write a Word32 in big endian format+putWord32be :: Word32 -> Put+putWord32be = tell . B.fromWord32be+{-# INLINE putWord32be #-}++-- | Write a Word32 in little endian format+putWord32le :: Word32 -> Put+putWord32le = tell . B.fromWord32le+{-# INLINE putWord32le #-}++-- | Write a Word64 in big endian format+putWord64be :: Word64 -> Put+putWord64be = tell . B.fromWord64be+{-# INLINE putWord64be #-}++-- | Write a Word64 in little endian format+putWord64le :: Word64 -> Put+putWord64le = tell . B.fromWord64le+{-# INLINE putWord64le #-}++------------------------------------------------------------------------++-- | /O(1)./ Write a single native machine word. The word is+-- written in host order, host endian form, for the machine you're on.+-- On a 64 bit machine the Word is an 8 byte value, on a 32 bit machine,+-- 4 bytes. Values written this way are not portable to+-- different endian or word sized machines, without conversion.+--+putWordhost :: Word -> Put+putWordhost = tell . B.fromWordhost+{-# INLINE putWordhost #-}++-- | /O(1)./ Write a Word16 in native host order and host endianness.+-- For portability issues see @putWordhost@.+putWord16host :: Word16 -> Put+putWord16host = tell . B.fromWord16host+{-# INLINE putWord16host #-}++-- | /O(1)./ Write a Word32 in native host order and host endianness.+-- For portability issues see @putWordhost@.+putWord32host :: Word32 -> Put+putWord32host = tell . B.fromWord32host+{-# INLINE putWord32host #-}++-- | /O(1)./ Write a Word64 in native host order+-- On a 32 bit machine we write two host order Word32s, in big endian form.+-- For portability issues see @putWordhost@.+putWord64host :: Word64 -> Put+putWord64host = tell . B.fromWord64host+{-# INLINE putWord64host #-}
+ benchmarks/Throughput/CBenchmark.c view
@@ -0,0 +1,39 @@+#include "CBenchmark.h"++void bytewrite(unsigned char *a, int bytes) {+ unsigned char n = 0;+ int i = 0;+ int iterations = bytes;+ while (i < iterations) {+ a[i++] = n++;+ }+}++unsigned char byteread(unsigned char *a, int bytes) {+ unsigned char n = 0;+ int i = 0;+ int iterations = bytes;+ while (i < iterations) {+ n += a[i++];+ }+ return n;+}++void wordwrite(unsigned long *a, int bytes) {+ unsigned long n = 0;+ int i = 0;+ int iterations = bytes / sizeof(unsigned long) ;+ while (i < iterations) {+ a[i++] = n++;+ }+}++unsigned int wordread(unsigned long *a, int bytes) {+ unsigned long n = 0;+ int i = 0;+ int iterations = bytes / sizeof(unsigned long);+ while (i < iterations) {+ n += a[i++];+ }+ return n;+}
+ benchmarks/Throughput/CBenchmark.h view
@@ -0,0 +1,4 @@+void bytewrite(unsigned char *a, int bytes);+unsigned char byteread(unsigned char *a, int bytes);+void wordwrite(unsigned long *a, int bytes);+unsigned int wordread(unsigned long *a, int bytes);
+ benchmarks/Throughput/Memory.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE ForeignFunctionInterface, BangPatterns #-}+module Throughput.Memory (memBench) where++import Foreign+import Foreign.C++import Control.Exception+import System.CPUTime+import Numeric++memBench :: Int -> IO ()+memBench mb = do+ let bytes = mb * 2^20+ allocaBytes bytes $ \ptr -> do+ let bench label test = do+ seconds <- time $ test (castPtr ptr) (fromIntegral bytes)+ let throughput = fromIntegral mb / seconds+ putStrLn $ show mb ++ "MB of " ++ label+ ++ " in " ++ showFFloat (Just 3) seconds "s, at: "+ ++ showFFloat (Just 1) throughput "MB/s"+ bench "setup " c_wordwrite+ putStrLn ""+ putStrLn "C memory throughput benchmarks:"+ bench "bytes written " c_bytewrite+ bench "bytes read " c_byteread+ bench "words written " c_wordwrite+ bench "words read " c_wordread+ putStrLn ""+ putStrLn "Haskell memory throughput benchmarks:"+ bench "bytes written " hs_bytewrite+ bench "bytes written (loop unrolled once)" hs_bytewrite2+ bench "bytes read " hs_byteread+ bench "words written " hs_wordwrite+ bench "words read " hs_wordread++hs_bytewrite :: Ptr CUChar -> Int -> IO ()+hs_bytewrite !ptr bytes = loop 0 0+ where iterations = bytes+ loop :: Int -> CUChar -> IO ()+ loop !i !n | i == iterations = return ()+ | otherwise = do pokeByteOff ptr i n+ loop (i+1) (n+1)++hs_bytewrite2 :: Ptr CUChar -> Int -> IO ()+hs_bytewrite2 !start bytes = loop start 0+ where end = start `plusPtr` bytes+ loop :: Ptr CUChar -> CUChar -> IO ()+ loop !ptr !n | ptr `plusPtr` 2 < end = do+ poke ptr n+ poke (ptr `plusPtr` 1) (n+1)+ loop (ptr `plusPtr` 2) (n+2)+ | ptr `plusPtr` 1 < end =+ poke ptr n+ | otherwise = return ()++hs_byteread :: Ptr CUChar -> Int -> IO CUChar+hs_byteread !ptr bytes = loop 0 0+ where iterations = bytes+ loop :: Int -> CUChar -> IO CUChar+ loop !i !n | i == iterations = return n+ | otherwise = do x <- peekByteOff ptr i+ loop (i+1) (n+x)++hs_wordwrite :: Ptr CULong -> Int -> IO ()+hs_wordwrite !ptr bytes = loop 0 0+ where iterations = bytes `div` sizeOf (undefined :: CULong)+ loop :: Int -> CULong -> IO ()+ loop !i !n | i == iterations = return ()+ | otherwise = do pokeByteOff ptr i n+ loop (i+1) (n+1)++hs_wordread :: Ptr CULong -> Int -> IO CULong+hs_wordread !ptr bytes = loop 0 0+ where iterations = bytes `div` sizeOf (undefined :: CULong)+ loop :: Int -> CULong -> IO CULong+ loop !i !n | i == iterations = return n+ | otherwise = do x <- peekByteOff ptr i+ loop (i+1) (n+x)+++foreign import ccall unsafe "CBenchmark.h byteread"+ c_byteread :: Ptr CUChar -> CInt -> IO ()++foreign import ccall unsafe "CBenchmark.h bytewrite"+ c_bytewrite :: Ptr CUChar -> CInt -> IO ()++foreign import ccall unsafe "CBenchmark.h wordread"+ c_wordread :: Ptr CUInt -> CInt -> IO ()++foreign import ccall unsafe "CBenchmark.h wordwrite"+ c_wordwrite :: Ptr CUInt -> CInt -> IO ()++time :: IO a -> IO Double+time action = do+ start <- getCPUTime+ action+ end <- getCPUTime+ return $! (fromIntegral (end - start)) / (10^12)
+ benchmarks/Throughput/Utils.hs view
@@ -0,0 +1,12 @@+module Throughput.Utils (+ Endian(..) +) where+++data Endian+ = Big+ | Little+ | Host+ deriving (Eq,Ord,Show)++
blaze-builder.cabal view
@@ -1,29 +1,56 @@ Name: blaze-builder-Version: 0.1-Synopsis: Builder to efficiently append text.-Description: Builder to efficiently append text, optimized for- HTML generation. There is a small usage example in the- Text.Blaze.Builder.Core module, you should begin by- reading that.-Homepage: http://jaspervdj.be/blaze-Bug-Reports: http://github.com/jaspervdj/BlazeHtml/issues+Version: 0.2.0.0+Synopsis: Efficient construction of bytestrings.++Description: + This library provides an abstraction of buffered output of+ byte streams and several convenience functions to exploit+ it. For example, it allows to efficiently serialize+ Haskell values to lazy bytestrings with a large average+ chunk size. The large average chunk size allows to make+ good use of cache prefetching in later processing steps+ (e.g. compression) and reduces the sytem call overhead+ when writing the resulting lazy bytestring to a file or+ sending it over the network.++Homepage: http://github.com/jaspervdj/blaze-builder+Bug-Reports: http://github.com/jaspervdj/blaze-builder/issues License: BSD3 License-file: LICENSE Author: Jasper Van der Jeugt, Simon Meier-Maintainer: jaspervdj@gmail.com, simon.meier@inf.ethz.ch+Maintainer: jaspervdj@gmail.com, iridcode@gmail.com Stability: Experimental-Category: Text+Category: Data Build-type: Simple-Cabal-version: >=1.2+Cabal-version: >= 1.6++Extra-source-files: + Makefile+ README.markdown+ TODO+ CHANGES++ benchmarks/*.hs+ benchmarks/Throughput/*.hs+ benchmarks/Throughput/*.h+ benchmarks/Throughput/*.c++ tests/*.hs+ Library- Ghc-Options: -Wall+ ghc-options: -Wall - -- Modules exported by the library.- Exposed-modules: Text.Blaze.Builder.Core- Text.Blaze.Builder.Utf8- Text.Blaze.Builder.Html+ exposed-modules: Blaze.ByteString.Builder+ Blaze.ByteString.Builder.Write+ Blaze.ByteString.Builder.Int+ Blaze.ByteString.Builder.Word+ Blaze.ByteString.Builder.ByteString+ Blaze.ByteString.Builder.Char.Utf8+ Blaze.ByteString.Builder.Html.Utf8++ Blaze.ByteString.Builder.Internal - -- Packages needed in order to build this package.- Build-depends: base >= 4 && < 5,- text >= 0.7,- bytestring >= 0.9+ -- FIXME: Ensure dependencies are strict enough+ build-depends: base == 4.* ,+ bytestring == 0.9.* ,+ text == 0.10.*
+ tests/LlvmSegfault.hs view
@@ -0,0 +1,35 @@+-- Author: Simon Meier <iridcode@gmail.com>, 10/06/2010+--+-- Attempt to find a small test-case for the segfaults that happen when+-- compiling the benchmarks with LLVM and GHC-7.0.1+--+module LlvmSegfault where++import Data.Word+import Data.Monoid+import qualified Data.ByteString.Lazy as L++import Foreign++import Blaze.ByteString.Builder.Internal++++fromWord8 :: Word8 -> Builder+fromWord8 w =+ Builder step+ where+ step k pf pe+ | pf < pe = do+ poke pf w+ let pf' = pf `plusPtr` 1+ pf' `seq` k pf' pe+ | otherwise = return $ BufferFull 1 pf (step k)+++word8s :: Builder+word8s = map (fromWord8 . fromIntegral) $ [(1::Int)..1000]++main :: IO ()+main = + print $ toLazyByteStringWith 10 10 (mconcat word8s) L.empty
+ tests/Tests.hs view
@@ -0,0 +1,96 @@+-- | Tests for the Blaze builder+--+{-# LANGUAGE OverloadedStrings #-}+module Tests where++import Control.Applicative ((<$>))+import Data.Monoid (mempty, mappend, mconcat)++import qualified Data.Text as T+import qualified Data.ByteString.Lazy as LB+import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.Framework.Providers.HUnit+import Test.QuickCheck+import Test.HUnit hiding (Test)+import Codec.Binary.UTF8.String (decode)++import Blaze.ByteString.Builder+import Blaze.ByteString.Builder.Char.Utf8+import Blaze.ByteString.Builder.Html.Utf8++main :: IO ()+main = defaultMain $ return $ testGroup "Tests" tests++tests :: [Test]+tests =+ [ testProperty "left identity Monoid law" monoidLeftIdentity+ , testProperty "right identity Monoid law" monoidRightIdentity+ , testProperty "associativity Monoid law" monoidAssociativity+ , testProperty "mconcat Monoid law" monoidConcat+ , testProperty "string → builder → string" fromStringToString+ , testProperty "string and text" stringAndText+ , testProperty "lazy bytestring identity" identityLazyByteString+ , testProperty "flushing identity" identityFlushing+ , testCase "escaping case 1" escaping1+ , testCase "escaping case 2" escaping2+ , testCase "escaping case 3" escaping3+ ]++monoidLeftIdentity :: Builder -> Bool+monoidLeftIdentity b = mappend mempty b == b++monoidRightIdentity :: Builder -> Bool+monoidRightIdentity b = mappend b mempty == b++monoidAssociativity :: Builder -> Builder -> Builder -> Bool+monoidAssociativity x y z = mappend x (mappend y z) == mappend (mappend x y) z++monoidConcat :: [Builder] -> Bool+monoidConcat xs = mconcat xs == foldr mappend mempty xs++fromStringToString :: String -> Bool+fromStringToString string = string == convert string+ where+ convert = decode . LB.unpack . toLazyByteString . fromString++stringAndText :: String -> Bool+stringAndText string = fromString string == fromText (T.pack string)++identityLazyByteString :: LB.ByteString -> Bool+identityLazyByteString lbs = lbs == toLazyByteString (fromLazyByteString lbs)++identityFlushing :: String -> String -> Bool+identityFlushing s1 s2 =+ let b1 = fromString s1+ b2 = fromString s2+ in b1 `mappend` b2 == b1 `mappend` flush `mappend` b2++escaping1 :: Assertion+escaping1 = fromString "<hello>" @?= fromHtmlEscapedString "<hello>"++escaping2 :: Assertion+escaping2 = fromString "f &&& g" @?= fromHtmlEscapedString "f &&& g"++escaping3 :: Assertion+escaping3 = fromString ""'" @?= fromHtmlEscapedString "\"'"++instance Show Builder where+ show = show . toLazyByteString++instance Eq Builder where+ b1 == b2 = + -- different and small buffer sizses for testing wrapping behaviour+ toLazyByteStringWith 1024 1024 256 b1 mempty ==+ toLazyByteStringWith 2001 511 256 b2 mempty++-- | Artificially scale up size to ensures that buffer wrapping behaviour is+-- also tested.+numRepetitions :: Int+numRepetitions = 250++instance Arbitrary Builder where+ arbitrary = (mconcat . replicate numRepetitions . fromString) <$> arbitrary++instance Arbitrary LB.ByteString where+ arbitrary = (LB.concat . replicate numRepetitions . LB.pack) <$> arbitrary