@@ -14,26 +14,29 @@ import Data.Monoid
14
14
import Data.Semigroup
15
15
import Data.String
16
16
import Test.Tasty.Bench
17
+
17
18
import Prelude hiding (words )
18
19
import qualified Data.List as List
19
20
import Control.DeepSeq
21
+ import Control.Exception
20
22
21
23
import qualified Data.ByteString as S
22
24
import qualified Data.ByteString.Char8 as S8
23
25
import qualified Data.ByteString.Lazy as L
24
26
import qualified Data.ByteString.Lazy.Char8 as L8
25
27
26
28
import Data.ByteString.Builder
27
- import Data.ByteString.Builder.Extra (byteStringCopy ,
28
- byteStringInsert ,
29
- intHost )
30
- import Data.ByteString.Builder.Internal (ensureFree )
29
+ import qualified Data.ByteString.Builder.Extra as Extra
30
+ import qualified Data.ByteString.Builder.Internal as BI
31
31
import Data.ByteString.Builder.Prim (BoundedPrim , FixedPrim ,
32
32
(>$<) )
33
33
import qualified Data.ByteString.Builder.Prim as P
34
34
import qualified Data.ByteString.Builder.Prim.Internal as PI
35
35
36
36
import Foreign
37
+ import Foreign.ForeignPtr
38
+ import qualified GHC.Exts as Exts
39
+ import GHC.Ptr (Ptr (.. ))
37
40
38
41
import System.Random
39
42
@@ -121,15 +124,45 @@ loremIpsum = S8.unlines $ map S8.pack
121
124
-- benchmark wrappers
122
125
---------------------
123
126
124
- {-# INLINE benchB #-}
125
127
benchB :: String -> a -> (a -> Builder ) -> Benchmark
126
- benchB name x b =
127
- bench (name ++ " (" ++ show nRepl ++ " )" ) $
128
- whnf (L. length . toLazyByteString . b) x
128
+ {-# INLINE benchB #-}
129
+ benchB name x b = benchB' (name ++ " (" ++ show nRepl ++ " )" ) x b
129
130
130
- {-# INLINE benchB' #-}
131
131
benchB' :: String -> a -> (a -> Builder ) -> Benchmark
132
- benchB' name x b = bench name $ whnf (L. length . toLazyByteString . b) x
132
+ {-# INLINE benchB' #-}
133
+ benchB' name x mkB =
134
+ env (BI. newBuffer BI. defaultChunkSize) $ \ buf ->
135
+ bench name $ whnfAppIO (runBuildStepOn buf . BI. runBuilder . mkB) x
136
+
137
+ benchB'_ :: String -> Builder -> Benchmark
138
+ {-# INLINE benchB'_ #-}
139
+ benchB'_ name b =
140
+ env (BI. newBuffer BI. defaultChunkSize) $ \ buf ->
141
+ bench name $ whnfIO (runBuildStepOn buf (BI. runBuilder b))
142
+
143
+ -- | @runBuilderOn@ runs a @BuildStep@'s actions all on the same @Buffer@.
144
+ -- It is used to avoid measuring driver allocation overhead.
145
+ runBuildStepOn :: BI. Buffer -> BI. BuildStep () -> IO ()
146
+ {-# NOINLINE runBuildStepOn #-}
147
+ runBuildStepOn (BI. Buffer fp br@ (BI. BufferRange op ope)) b = go b
148
+ where
149
+ ! len = ope `minusPtr` op
150
+
151
+ go :: BI. BuildStep () -> IO ()
152
+ go bs = BI. fillWithBuildStep bs doneH fullH insertChunkH br
153
+
154
+ doneH :: Ptr Word8 -> () -> IO ()
155
+ doneH _ _ = touchForeignPtr fp
156
+ -- 'touchForeignPtr' is adequate because the given BuildStep
157
+ -- will always terminate. (We won't measure an infinite loop!)
158
+
159
+ fullH :: Ptr Word8 -> Int -> BI. BuildStep () -> IO ()
160
+ fullH _ minLen nextStep
161
+ | len < minLen = throwIO (ErrorCall " runBuilderOn: action expects too long of a BufferRange" )
162
+ | otherwise = go nextStep
163
+
164
+ insertChunkH :: Ptr Word8 -> S. ByteString -> BI. BuildStep () -> IO ()
165
+ insertChunkH _ _ nextStep = go nextStep
133
166
134
167
{-# INLINE benchBInts #-}
135
168
benchBInts :: String -> ([Int ] -> Builder ) -> Benchmark
@@ -247,18 +280,53 @@ largeTraversalInput = S.concat (replicate 10 byteStringData)
247
280
smallTraversalInput :: S. ByteString
248
281
smallTraversalInput = S8. pack " The quick brown fox"
249
282
283
+ asciiBuf , utf8Buf , halfNullBuf , allNullBuf :: Ptr Word8
284
+ asciiBuf = Ptr " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" #
285
+ utf8Buf = Ptr " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\xc0\x80 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" #
286
+ halfNullBuf = Ptr " \xc0\x80 xx\xc0\x80 x\xc0\x80\xc0\x80 x\xc0\x80\xc0\x80 xx\xc0\x80\xc0\x80 xxx\xc0\x80 x\xc0\x80 x\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80 xxx\xc0\x80 x\xc0\x80 xx\xc0\x80\xc0\x80 xxxxxxxxxx\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80 x\xc0\x80\xc0\x80 x\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80 xxx" #
287
+ allNullBuf = Ptr " \xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80 " #
288
+
289
+ asciiLit , utf8Lit :: Ptr Word8 -> Builder
290
+ asciiLit (Ptr p# ) = P. cstring p#
291
+ utf8Lit (Ptr p# ) = P. cstringUtf8 p#
292
+
293
+ asciiStr , utf8Str :: String
294
+ asciiStr = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
295
+ utf8Str = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
296
+
250
297
main :: IO ()
251
298
main = do
252
299
defaultMain
253
300
[ bgroup " Data.ByteString.Builder"
254
301
[ bgroup " Small payload"
255
- [ benchB' " mempty" () (const mempty )
256
- , benchB' " ensureFree 8" () (const (ensureFree 8 ))
257
- , benchB' " intHost 1" 1 intHost
258
- , benchB' " UTF-8 String (naive)" " hello world\0" fromString
259
- , benchB' " UTF-8 String" () $ \ () -> P. cstringUtf8 " hello world\0" #
260
- , benchB' " String (naive)" " hello world!" fromString
261
- , benchB' " String" () $ \ () -> P. cstring " hello world!" #
302
+ [ benchB'_ " mempty" mempty
303
+ , bench " toLazyByteString mempty" $ nf toLazyByteString mempty
304
+ , benchB'_ " empty (10000 times)" $
305
+ stimes (10000 :: Int ) (Exts. lazy BI. empty)
306
+ , benchB'_ " ensureFree 8" (BI. ensureFree 8 )
307
+ , benchB' " intHost 1" 1 Extra. intHost
308
+ , benchB' " UTF-8 String (12B, naive)" " hello world\0" fromString
309
+ , benchB'_ " UTF-8 String (12B)" $ utf8Lit (Ptr " hello world\xc0\x80 " # )
310
+ , benchB' " UTF-8 String (64B, naive)" utf8Str fromString
311
+ , benchB'_ " UTF-8 String (64B, one null)" $ utf8Lit utf8Buf
312
+ , benchB'
313
+ " UTF-8 String (64B, one null, no shared work)"
314
+ utf8Buf
315
+ utf8Lit
316
+ , benchB'_ " UTF-8 String (64B, half nulls)" $ utf8Lit halfNullBuf
317
+ , benchB'_ " UTF-8 String (64B, all nulls)" $ utf8Lit allNullBuf
318
+ , benchB'
319
+ " UTF-8 String (64B, all nulls, no shared work)"
320
+ allNullBuf
321
+ utf8Lit
322
+ , benchB'
323
+ " UTF-8 String (1 byte, no shared work)"
324
+ (Ptr " \xc0\x80 " # )
325
+ utf8Lit
326
+ , benchB' " ASCII String (12B, naive)" " hello world!" fromString
327
+ , benchB'_ " ASCII String (12B)" $ asciiLit (Ptr " hello wurld!" # )
328
+ , benchB' " ASCII String (64B, naive)" asciiStr fromString
329
+ , benchB'_ " ASCII String (64B)" $ asciiLit asciiBuf
262
330
]
263
331
264
332
, bgroup " Encoding wrappers"
@@ -275,11 +343,11 @@ main = do
275
343
]
276
344
, bgroup " ByteString insertion" $
277
345
[ benchB " foldMap byteStringInsert" byteStringChunksData
278
- (foldMap byteStringInsert)
346
+ (foldMap Extra. byteStringInsert)
279
347
, benchB " foldMap byteString" byteStringChunksData
280
348
(foldMap byteString)
281
349
, benchB " foldMap byteStringCopy" byteStringChunksData
282
- (foldMap byteStringCopy)
350
+ (foldMap Extra. byteStringCopy)
283
351
]
284
352
285
353
, bgroup " Non-bounded encodings"
0 commit comments