1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
-- |
-- Copyright: (c) 2022 Andrew Lelechenko
-- Licence: BSD3
-- Maintainer: Andrew Lelechenko <andrew.lelechenko@gmail.com>
module BenchDecimal (benchDecimal) where
import Data.ByteString qualified as B
import Data.ByteString.Builder qualified as B
import Data.Text qualified as T
import Data.Text.Builder.Linear.Buffer (Buffer, runBuffer, ($$<|), ($<|), (|>$), (|>$$))
import Data.Text.Lazy (toStrict)
import Data.Text.Lazy.Builder (toLazyText)
import Data.Text.Lazy.Builder.Int (decimal)
import Test.Tasty.Bench (Benchmark, bench, bgroup, nf)
#ifdef MIN_VERSION_text_builder
import qualified Text.Builder
#endif
#ifdef MIN_VERSION_bytestring_strict_builder
import qualified ByteString.StrictBuilder
#endif
benchDecimal ∷ Benchmark
benchDecimal = bgroup "Decimal" [benchBoundedDecimal, benchUnboundedDecimal]
--------------------------------------------------------------------------------
-- Bounded
--------------------------------------------------------------------------------
int ∷ Int
int = 123456789123456789
benchLazyBuilder ∷ Integral a ⇒ a → Int → T.Text
benchLazyBuilder k = toStrict . toLazyText . go mempty
where
go !acc 0 = acc
go !acc n = let i = fromIntegral n * k in go (decimal i <> (acc <> decimal i)) (n - 1)
{-# SPECIALIZE benchLazyBuilder ∷ Int → Int → T.Text #-}
{-# SPECIALIZE benchLazyBuilder ∷ Integer → Int → T.Text #-}
benchLazyBuilderBS ∷ Int → Int → B.ByteString
benchLazyBuilderBS k = B.toStrict . B.toLazyByteString . go mempty
where
go !acc 0 = acc
go !acc n = let i = n * k in go (B.intDec i <> (acc <> B.intDec i)) (n - 1)
#ifdef MIN_VERSION_text_builder
benchStrictBuilder ∷ (Integral a) ⇒ a → Int → T.Text
benchStrictBuilder k = Text.Builder.run . go mempty
where
go !acc 0 = acc
go !acc n = let i = fromIntegral n * k in go (Text.Builder.decimal i <> (acc <> Text.Builder.decimal i)) (n - 1)
{-# SPECIALIZE benchStrictBuilder ∷ Int → Int → T.Text #-}
{-# SPECIALIZE benchStrictBuilder ∷ Integer → Int → T.Text #-}
#endif
#ifdef MIN_VERSION_bytestring_strict_builder
benchStrictBuilderBS ∷ (Integral a) ⇒ a → Int → B.ByteString
benchStrictBuilderBS k = ByteString.StrictBuilder.builderBytes . go mempty
where
go !acc 0 = acc
go !acc n = let i = fromIntegral n * k in go (ByteString.StrictBuilder.asciiIntegral i <> (acc <> ByteString.StrictBuilder.asciiIntegral i)) (n - 1)
{-# SPECIALIZE benchStrictBuilderBS ∷ Int → Int → B.ByteString #-}
{-# SPECIALIZE benchStrictBuilderBS ∷ Integer → Int → B.ByteString #-}
#endif
benchBoundedLinearBuilder ∷ Int → Int → T.Text
benchBoundedLinearBuilder k m = runBuffer (\b → go b m)
where
go ∷ Buffer ⊸ Int → Buffer
go !acc 0 = acc
go !acc n = let i = n * k in go (i $<| (acc |>$ i)) (n - 1)
benchBoundedDecimal ∷ Benchmark
benchBoundedDecimal = bgroup "Bounded" $ map mkBoundedGroup [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6]
mkBoundedGroup ∷ Int → Benchmark
mkBoundedGroup n =
bgroup
(show n)
[ bench "Data.Text.Lazy.Builder" $ nf (benchLazyBuilder int) n
, bench "Data.ByteString.Builder" $ nf (benchLazyBuilderBS int) n
#ifdef MIN_VERSION_text_builder
, bench "Text.Builder" $ nf (benchStrictBuilder int) n
#endif
#ifdef MIN_VERSION_bytestring_strict_builder
, bench "ByteString.StrictBuilder" $ nf (benchStrictBuilderBS int) n
#endif
, bench "Data.Text.Builder.Linear" $ nf (benchBoundedLinearBuilder int) n
]
--------------------------------------------------------------------------------
-- Unbounded
--------------------------------------------------------------------------------
integerSmall ∷ Integer
integerSmall = toInteger (div @Word maxBound 20)
integerBig ∷ Integer
integerBig = toInteger (maxBound @Word - 1) ^ (10 ∷ Word)
integerHuge ∷ Integer
integerHuge = toInteger (maxBound @Word - 1) ^ (100 ∷ Word)
benchUnboundedDecimal ∷ Benchmark
benchUnboundedDecimal =
bgroup
"Unbounded"
[ bgroup "Small" $ map (mkUnboundedGroup integerSmall) [1e0, 1e1, 1e2, 1e3, 1e4, 1e5]
, bgroup "Big" $ map (mkUnboundedGroup integerBig) [1e0, 1e1, 1e2, 1e3, 1e4]
, bgroup "Huge" $ map (mkUnboundedGroup integerHuge) [1e0, 1e1, 1e2, 1e3]
]
-- NOTE: In the following benchmarks, the ByteString builder would share work
-- if the prepender and the appender are identical, while our linear buffer does
-- not. So we increment the appender to get a fair benchmark.
benchUnboundedLazyBuilderBS ∷ Integer → Int → B.ByteString
benchUnboundedLazyBuilderBS k = B.toStrict . B.toLazyByteString . go mempty
where
go !acc 0 = acc
go !acc n = let i = fromIntegral n * k in go (B.integerDec i <> (acc <> B.integerDec (i + 1))) (n - 1)
benchUnboundedLinearBuilder ∷ Integer → Int → T.Text
benchUnboundedLinearBuilder k m = runBuffer (\b → go b m)
where
go ∷ Buffer ⊸ Int → Buffer
go !acc 0 = acc
go !acc n = let i = fromIntegral n * k in go (i $$<| (acc |>$$ (i + 1))) (n - 1)
mkUnboundedGroup ∷ Integer → Int → Benchmark
mkUnboundedGroup integer n =
bgroup
(show n)
[ bench "Data.Text.Lazy.Builder" $ nf (benchLazyBuilder integer) n
, bench "Data.ByteString.Builder" $ nf (benchUnboundedLazyBuilderBS integer) n
#ifdef MIN_VERSION_text_builder
, bench "Text.Builder" $ nf (benchStrictBuilder integer) n
#endif
#ifdef MIN_VERSION_bytestring_strict_builder
, bench "ByteString.StrictBuilder" $ nf (benchStrictBuilderBS integer) n
#endif
, bench "Data.Text.Builder.Linear" $ nf (benchUnboundedLinearBuilder integer) n
]
|