diff options
author | Johannes Gerer <oss@johannesgerer.com> | 2016-10-26 02:07:02 +0200 |
---|---|---|
committer | Johannes Gerer <oss@johannesgerer.com> | 2016-10-26 02:07:02 +0200 |
commit | 675085c2e0b0b851378da08b7d73024766107c87 (patch) | |
tree | 5a927de4a9576aef7e6129b96e74aa5c96f9ffb6 /src/Benchmarks | |
download | blazeT-675085c2e0b0b851378da08b7d73024766107c87.tar.gz blazeT-675085c2e0b0b851378da08b7d73024766107c87.tar.zst blazeT-675085c2e0b0b851378da08b7d73024766107c87.zip |
Initial
Diffstat (limited to 'src/Benchmarks')
-rw-r--r-- | src/Benchmarks/BenchmarkUtils.hs | 107 | ||||
-rw-r--r-- | src/Benchmarks/BlazeTBenchmarks.hs | 133 | ||||
-rw-r--r-- | src/Benchmarks/HtmlBenchmarks.hs | 132 | ||||
-rw-r--r-- | src/Benchmarks/LICENSE | 30 | ||||
-rw-r--r-- | src/Benchmarks/RunHtmlBenchmarks.hs | 36 | ||||
-rw-r--r-- | src/Benchmarks/ServerChunkSize.hs | 52 | ||||
-rw-r--r-- | src/Benchmarks/bigtable/erb.rb | 32 | ||||
-rw-r--r-- | src/Benchmarks/bigtable/erubis.rb | 31 | ||||
-rw-r--r-- | src/Benchmarks/bigtable/hamlet.hs | 33 | ||||
-rw-r--r-- | src/Benchmarks/bigtable/html-minimalist.hs | 20 | ||||
-rw-r--r-- | src/Benchmarks/bigtable/html.hs | 19 | ||||
-rw-r--r-- | src/Benchmarks/bigtable/php.php | 30 | ||||
-rw-r--r-- | src/Benchmarks/bigtable/xhtml.hs | 19 |
13 files changed, 674 insertions, 0 deletions
diff --git a/src/Benchmarks/BenchmarkUtils.hs b/src/Benchmarks/BenchmarkUtils.hs new file mode 100644 index 0000000..4b9546c --- /dev/null +++ b/src/Benchmarks/BenchmarkUtils.hs | |||
@@ -0,0 +1,107 @@ | |||
1 | -- | This is a module which contains some ad-hoc HTML combinators for use when | ||
2 | -- benchmarking | ||
3 | -- | ||
4 | {-# LANGUAGE OverloadedStrings, NoMonomorphismRestriction #-} | ||
5 | module Benchmarks.BenchmarkUtils | ||
6 | ( Html | ||
7 | , toHtml | ||
8 | |||
9 | , tr | ||
10 | , td | ||
11 | , html | ||
12 | , head | ||
13 | , title | ||
14 | , body | ||
15 | , div | ||
16 | , h1 | ||
17 | , h2 | ||
18 | , p | ||
19 | , ol | ||
20 | , li | ||
21 | , table | ||
22 | , img | ||
23 | , id | ||
24 | ) where | ||
25 | |||
26 | import Prelude hiding (div, head, id) | ||
27 | import Text.Blaze | ||
28 | import Text.Blaze.Internal | ||
29 | |||
30 | type Html = Markup | ||
31 | |||
32 | toHtml :: ToMarkup a => a -> Html | ||
33 | toHtml = toMarkup | ||
34 | |||
35 | tr :: Html -- ^ Inner HTML. | ||
36 | -> Html -- ^ Resulting HTML. | ||
37 | tr = Parent "tr" "<tr" "</tr>" | ||
38 | {-# INLINE tr #-} | ||
39 | |||
40 | td :: Html -- ^ Inner HTML. | ||
41 | -> Html -- ^ Resulting HTML. | ||
42 | td = Parent "td" "<td" "</td>" | ||
43 | {-# INLINE td #-} | ||
44 | |||
45 | html :: Html -- ^ Inner HTML. | ||
46 | -> Html -- ^ Resulting HTML. | ||
47 | html = Parent "html" "<html" "</html>" | ||
48 | {-# INLINE html #-} | ||
49 | |||
50 | head :: Html -- ^ Inner HTML. | ||
51 | -> Html -- ^ Resulting HTML. | ||
52 | head = Parent "head" "<head" "</head>" | ||
53 | {-# INLINE head #-} | ||
54 | |||
55 | title :: Html -- ^ Inner HTML. | ||
56 | -> Html -- ^ Resulting HTML. | ||
57 | title = Parent "title" "<title" "</title>" | ||
58 | {-# INLINE title #-} | ||
59 | |||
60 | body :: Html -- ^ Inner HTML. | ||
61 | -> Html -- ^ Resulting HTML. | ||
62 | body = Parent "body" "<body" "</body>" | ||
63 | {-# INLINE body #-} | ||
64 | |||
65 | div :: Html -- ^ Inner HTML. | ||
66 | -> Html -- ^ Resulting HTML. | ||
67 | div = Parent "div" "<div" "</div>" | ||
68 | {-# INLINE div #-} | ||
69 | |||
70 | h1 :: Html -- ^ Inner HTML. | ||
71 | -> Html -- ^ Resulting HTML. | ||
72 | h1 = Parent "h1" "<h1" "</h1>" | ||
73 | {-# INLINE h1 #-} | ||
74 | |||
75 | h2 :: Html -- ^ Inner HTML. | ||
76 | -> Html -- ^ Resulting HTML. | ||
77 | h2 = Parent "h2" "<h2" "</h2>" | ||
78 | {-# INLINE h2 #-} | ||
79 | |||
80 | p :: Html -- ^ Inner HTML. | ||
81 | -> Html -- ^ Resulting HTML. | ||
82 | p = Parent "p" "<p" "</p>" | ||
83 | {-# INLINE p #-} | ||
84 | |||
85 | ol :: Html -- ^ Inner HTML. | ||
86 | -> Html -- ^ Resulting HTML. | ||
87 | ol = Parent "ol" "<ol" "</ol>" | ||
88 | {-# INLINE ol #-} | ||
89 | |||
90 | li :: Html -- ^ Inner HTML. | ||
91 | -> Html -- ^ Resulting HTML. | ||
92 | li = Parent "li" "<li" "</li>" | ||
93 | {-# INLINE li #-} | ||
94 | |||
95 | table :: Html -- ^ Inner HTML. | ||
96 | -> Html -- ^ Resulting HTML. | ||
97 | table = Parent "table" "<table" "</table>" | ||
98 | {-# INLINE table #-} | ||
99 | |||
100 | img :: Html -- ^ Resulting HTML. | ||
101 | img = Leaf "img" "<img" ">" | ||
102 | {-# INLINE img #-} | ||
103 | |||
104 | id :: AttributeValue -- ^ Attribute value. | ||
105 | -> Attribute -- ^ Resulting attribute. | ||
106 | id = attribute "id" " id=\"" | ||
107 | {-# INLINE id #-} | ||
diff --git a/src/Benchmarks/BlazeTBenchmarks.hs b/src/Benchmarks/BlazeTBenchmarks.hs new file mode 100644 index 0000000..83a38ad --- /dev/null +++ b/src/Benchmarks/BlazeTBenchmarks.hs | |||
@@ -0,0 +1,133 @@ | |||
1 | -- | This is a collection of HTML benchmarks for BlazeMarkup. | ||
2 | -- | ||
3 | {-# LANGUAGE OverloadedStrings, ExistentialQuantification, RankNTypes #-} | ||
4 | {-# OPTIONS_GHC -fno-warn-unused-do-bind #-} | ||
5 | module Benchmarks.BlazeTBenchmarks where | ||
6 | |||
7 | import Data.Monoid (Monoid, mempty, mconcat, mappend) | ||
8 | import Prelude hiding (div, id,map) | ||
9 | import qualified Prelude as P | ||
10 | |||
11 | import Text.BlazeT | ||
12 | import Text.BlazeT.Html5 | ||
13 | import qualified Text.BlazeT.Html5 as H | ||
14 | import qualified Text.BlazeT.Html5.Attributes as A | ||
15 | |||
16 | -- | Description of an HTML benchmark | ||
17 | -- | ||
18 | data HtmlBenchmark = forall a. HtmlBenchmark | ||
19 | String -- ^ Name. | ||
20 | (a -> Html) -- ^ Rendering function. | ||
21 | a -- ^ Data. | ||
22 | Html -- ^ Longer description. | ||
23 | |||
24 | -- | List containing all benchmarks. | ||
25 | -- | ||
26 | benchmarks :: [HtmlBenchmark] | ||
27 | benchmarks = | ||
28 | [ HtmlBenchmark "bigTable" bigTable bigTableData $ | ||
29 | let h = toHtml $ length bigTableData | ||
30 | w = toHtml $ length $ P.head bigTableData | ||
31 | in (string "Rendering of a big (") >> h >> (string "x") >> w >> ") HTML table" | ||
32 | , HtmlBenchmark "basic" basic basicData | ||
33 | "A simple, small basic template with a few holes to fill in" | ||
34 | , HtmlBenchmark "wideTree" wideTree wideTreeData $ | ||
35 | (string "A very wide tree (") >> toHtml (length wideTreeData) >> " elements)" | ||
36 | , HtmlBenchmark "wideTreeEscaping" wideTree wideTreeEscapingData $ do | ||
37 | (string "A very wide tree (") >> toHtml (length wideTreeData) >> (string " elements)") | ||
38 | " with lots of escaping" | ||
39 | , HtmlBenchmark "deepTree" deepTree deepTreeData $ do | ||
40 | (string "A really deep tree (") >> toHtml deepTreeData >> " nested templates)" | ||
41 | , HtmlBenchmark "manyAttributes" manyAttributes manyAttributesData $ do | ||
42 | (string "A single element with ") >> toHtml (length manyAttributesData) | ||
43 | " attributes." | ||
44 | , HtmlBenchmark "customAttribute" customAttributes customAttributesData $ | ||
45 | "Creating custom attributes" | ||
46 | ] | ||
47 | |||
48 | rows :: Int | ||
49 | rows = 1000 | ||
50 | |||
51 | bigTableData :: [[Int]] | ||
52 | bigTableData = replicate rows [1..10] | ||
53 | {-# NOINLINE bigTableData #-} | ||
54 | |||
55 | basicData :: (String, String, [String]) | ||
56 | basicData = ("Just a test", "joe", items) | ||
57 | {-# NOINLINE basicData #-} | ||
58 | |||
59 | items :: [String] | ||
60 | items = fmap (("Number " `mappend`) . show) [1 :: Int .. 14] | ||
61 | {-# NOINLINE items #-} | ||
62 | |||
63 | wideTreeData :: [String] | ||
64 | wideTreeData = take 5000 $ | ||
65 | cycle ["λf.(λx.fxx)(λx.fxx)", "These old days", "Foobar", "lol", "x ∈ A"] | ||
66 | {-# NOINLINE wideTreeData #-} | ||
67 | |||
68 | wideTreeEscapingData :: [String] | ||
69 | wideTreeEscapingData = take 1000 $ | ||
70 | cycle ["<><>", "\"lol\"", "<&>", "'>>'"] | ||
71 | {-# NOINLINE wideTreeEscapingData #-} | ||
72 | |||
73 | deepTreeData :: Int | ||
74 | deepTreeData = 1000 | ||
75 | {-# NOINLINE deepTreeData #-} | ||
76 | |||
77 | manyAttributesData :: [String] | ||
78 | manyAttributesData = wideTreeData | ||
79 | |||
80 | customAttributesData :: [(String, String)] | ||
81 | customAttributesData = zip wideTreeData wideTreeData | ||
82 | |||
83 | -- | Render the argument matrix as an HTML table. | ||
84 | -- | ||
85 | bigTable :: [[Int]] -- ^ Matrix. | ||
86 | -> Html -- ^ Result. | ||
87 | bigTable t = table $ mconcat $ fmap row t | ||
88 | where | ||
89 | row r = tr $ mconcat $ fmap (td . toHtml) r | ||
90 | |||
91 | -- | Render a simple HTML page with some data. | ||
92 | -- | ||
93 | basic :: (String, String, [String]) -- ^ (Title, User, Items) | ||
94 | -> Html -- ^ Result. | ||
95 | basic (title', user, items') = html $ do | ||
96 | H.head $ title $ toHtml title' | ||
97 | body $ do | ||
98 | div ! A.id "header" $ (h1 $ toHtml title') | ||
99 | p $ "Hello, " `mappend` toHtml user `mappend` "!" | ||
100 | p $ "Hello, me!" | ||
101 | p $ "Hello, world!" | ||
102 | h2 $ "loop" | ||
103 | ol $ mconcat $ fmap (li . toHtml) items' | ||
104 | div ! A.id "footer" $ mempty | ||
105 | |||
106 | -- | A benchmark producing a very wide but very shallow tree. | ||
107 | -- | ||
108 | wideTree :: [String] -- ^ Text to create a tree from. | ||
109 | -> Html -- ^ Result. | ||
110 | wideTree = div . mapM_ ((p ! A.id "foo") . toHtml) | ||
111 | |||
112 | -- | Create a very deep tree. | ||
113 | -- | ||
114 | deepTree :: Int -- ^ Depth of the tree. | ||
115 | -> Html -- ^ Result. | ||
116 | deepTree 0 = "foo" | ||
117 | deepTree n = p $ table $ tr $ td $ div $ deepTree (n - 1) | ||
118 | |||
119 | -- | Create an element with many attributes. | ||
120 | -- | ||
121 | manyAttributes :: [String] -- ^ List of attribute values. | ||
122 | -> Html -- ^ Result. | ||
123 | manyAttributes = foldl setAttribute img | ||
124 | where | ||
125 | setAttribute html' value' = html' ! A.id (toValue value') | ||
126 | {-# INLINE setAttribute #-} | ||
127 | |||
128 | customAttributes :: [(String, String)] -- ^ List of attribute name, value pairs | ||
129 | -> Html -- ^ Result | ||
130 | customAttributes = foldl setAttribute img | ||
131 | where | ||
132 | setAttribute html' (name, value') = | ||
133 | html' ! customAttribute (stringTag name) (toValue value') | ||
diff --git a/src/Benchmarks/HtmlBenchmarks.hs b/src/Benchmarks/HtmlBenchmarks.hs new file mode 100644 index 0000000..3070d52 --- /dev/null +++ b/src/Benchmarks/HtmlBenchmarks.hs | |||
@@ -0,0 +1,132 @@ | |||
1 | -- | This is a collection of HTML benchmarks for BlazeMarkup. | ||
2 | -- | ||
3 | {-# LANGUAGE OverloadedStrings, ExistentialQuantification #-} | ||
4 | {-# OPTIONS_GHC -fno-warn-unused-do-bind #-} | ||
5 | module Benchmarks.HtmlBenchmarks where | ||
6 | |||
7 | import Data.Monoid (Monoid, mempty, mconcat, mappend) | ||
8 | import Prelude hiding (div, id) | ||
9 | import qualified Prelude as P | ||
10 | |||
11 | import Benchmarks.BenchmarkUtils | ||
12 | import Text.Blaze | ||
13 | import qualified Benchmarks.BenchmarkUtils as H | ||
14 | |||
15 | -- | Description of an HTML benchmark | ||
16 | -- | ||
17 | data HtmlBenchmark = forall a. HtmlBenchmark | ||
18 | { hName :: String -- ^ Name. | ||
19 | , hR :: (a -> Html) -- ^ Rendering function. | ||
20 | , hD :: a -- ^ Data. | ||
21 | , hH :: Html } -- ^ Longer description. | ||
22 | |||
23 | -- | List containing all benchmarks. | ||
24 | -- | ||
25 | benchmarks :: [HtmlBenchmark] | ||
26 | benchmarks = | ||
27 | [ HtmlBenchmark "bigTable" bigTable bigTableData $ | ||
28 | let h = toHtml $ length bigTableData | ||
29 | w = toHtml $ length $ P.head bigTableData | ||
30 | in "Rendering of a big (" >> h >> "x" >> w >> ") HTML table" | ||
31 | , HtmlBenchmark "basic" basic basicData | ||
32 | "A simple, small basic template with a few holes to fill in" | ||
33 | , HtmlBenchmark "wideTree" wideTree wideTreeData $ | ||
34 | "A very wide tree (" >> toHtml (length wideTreeData) >> " elements)" | ||
35 | , HtmlBenchmark "wideTreeEscaping" wideTree wideTreeEscapingData $ do | ||
36 | "A very wide tree (" >> toHtml (length wideTreeData) >> " elements)" | ||
37 | " with lots of escaping" | ||
38 | , HtmlBenchmark "deepTree" deepTree deepTreeData $ do | ||
39 | "A really deep tree (" >> toHtml deepTreeData >> " nested templates)" | ||
40 | , HtmlBenchmark "manyAttributes" manyAttributes manyAttributesData $ do | ||
41 | "A single element with " >> toHtml (length manyAttributesData) | ||
42 | " attributes." | ||
43 | , HtmlBenchmark "customAttribute" customAttributes customAttributesData $ | ||
44 | "Creating custom attributes" | ||
45 | ] | ||
46 | |||
47 | rows :: Int | ||
48 | rows = 1000 | ||
49 | |||
50 | bigTableData :: [[Int]] | ||
51 | bigTableData = replicate rows [1..10] | ||
52 | {-# NOINLINE bigTableData #-} | ||
53 | |||
54 | basicData :: (String, String, [String]) | ||
55 | basicData = ("Just a test", "joe", items) | ||
56 | {-# NOINLINE basicData #-} | ||
57 | |||
58 | items :: [String] | ||
59 | items = map (("Number " `mappend`) . show) [1 :: Int .. 14] | ||
60 | {-# NOINLINE items #-} | ||
61 | |||
62 | wideTreeData :: [String] | ||
63 | wideTreeData = take 5000 $ | ||
64 | cycle ["λf.(λx.fxx)(λx.fxx)", "These old days", "Foobar", "lol", "x ∈ A"] | ||
65 | {-# NOINLINE wideTreeData #-} | ||
66 | |||
67 | wideTreeEscapingData :: [String] | ||
68 | wideTreeEscapingData = take 1000 $ | ||
69 | cycle ["<><>", "\"lol\"", "<&>", "'>>'"] | ||
70 | {-# NOINLINE wideTreeEscapingData #-} | ||
71 | |||
72 | deepTreeData :: Int | ||
73 | deepTreeData = 1000 | ||
74 | {-# NOINLINE deepTreeData #-} | ||
75 | |||
76 | manyAttributesData :: [String] | ||
77 | manyAttributesData = wideTreeData | ||
78 | |||
79 | customAttributesData :: [(String, String)] | ||
80 | customAttributesData = zip wideTreeData wideTreeData | ||
81 | |||
82 | -- | Render the argument matrix as an HTML table. | ||
83 | -- | ||
84 | bigTable :: [[Int]] -- ^ Matrix. | ||
85 | -> Html -- ^ Result. | ||
86 | bigTable t = table $ mconcat $ map row t | ||
87 | where | ||
88 | row r = tr $ mconcat $ map (td . toHtml) r | ||
89 | |||
90 | -- | Render a simple HTML page with some data. | ||
91 | -- | ||
92 | basic :: (String, String, [String]) -- ^ (Title, User, Items) | ||
93 | -> Html -- ^ Result. | ||
94 | basic (title', user, items') = html $ do | ||
95 | H.head $ title $ toHtml title' | ||
96 | body $ do | ||
97 | div ! id "header" $ (h1 $ toHtml title') | ||
98 | p $ "Hello, " `mappend` toHtml user `mappend` "!" | ||
99 | p $ "Hello, me!" | ||
100 | p $ "Hello, world!" | ||
101 | h2 $ "loop" | ||
102 | ol $ mconcat $ map (li . toHtml) items' | ||
103 | div ! id "footer" $ mempty | ||
104 | |||
105 | -- | A benchmark producing a very wide but very shallow tree. | ||
106 | -- | ||
107 | wideTree :: [String] -- ^ Text to create a tree from. | ||
108 | -> Html -- ^ Result. | ||
109 | wideTree = div . mapM_ ((p ! id "foo") . toHtml) | ||
110 | |||
111 | -- | Create a very deep tree. | ||
112 | -- | ||
113 | deepTree :: Int -- ^ Depth of the tree. | ||
114 | -> Html -- ^ Result. | ||
115 | deepTree 0 = "foo" | ||
116 | deepTree n = p $ table $ tr $ td $ div $ deepTree (n - 1) | ||
117 | |||
118 | -- | Create an element with many attributes. | ||
119 | -- | ||
120 | manyAttributes :: [String] -- ^ List of attribute values. | ||
121 | -> Html -- ^ Result. | ||
122 | manyAttributes = foldl setAttribute img | ||
123 | where | ||
124 | setAttribute html' value' = html' ! id (toValue value') | ||
125 | {-# INLINE setAttribute #-} | ||
126 | |||
127 | customAttributes :: [(String, String)] -- ^ List of attribute name, value pairs | ||
128 | -> Html -- ^ Result | ||
129 | customAttributes = foldl setAttribute img | ||
130 | where | ||
131 | setAttribute html' (name, value') = | ||
132 | html' ! customAttribute (stringTag name) (toValue value') | ||
diff --git a/src/Benchmarks/LICENSE b/src/Benchmarks/LICENSE new file mode 100644 index 0000000..8122505 --- /dev/null +++ b/src/Benchmarks/LICENSE | |||
@@ -0,0 +1,30 @@ | |||
1 | Copyright Jasper Van der Jeugt 2010 | ||
2 | |||
3 | All rights reserved. | ||
4 | |||
5 | Redistribution and use in source and binary forms, with or without | ||
6 | modification, are permitted provided that the following conditions are met: | ||
7 | |||
8 | * Redistributions of source code must retain the above copyright | ||
9 | notice, this list of conditions and the following disclaimer. | ||
10 | |||
11 | * Redistributions in binary form must reproduce the above | ||
12 | copyright notice, this list of conditions and the following | ||
13 | disclaimer in the documentation and/or other materials provided | ||
14 | with the distribution. | ||
15 | |||
16 | * Neither the name of Jasper Van der Jeugt nor the names of other | ||
17 | contributors may be used to endorse or promote products derived | ||
18 | from this software without specific prior written permission. | ||
19 | |||
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
diff --git a/src/Benchmarks/RunHtmlBenchmarks.hs b/src/Benchmarks/RunHtmlBenchmarks.hs new file mode 100644 index 0000000..04c28ff --- /dev/null +++ b/src/Benchmarks/RunHtmlBenchmarks.hs | |||
@@ -0,0 +1,36 @@ | |||
1 | -- | This is a module which runs the 'HtmlBenchmarks' module using the different | ||
2 | -- renderers available. | ||
3 | -- | ||
4 | module Benchmarks.RunHtmlBenchmarks where | ||
5 | |||
6 | import Criterion.Main | ||
7 | import qualified Data.Text.Lazy as LT | ||
8 | import Data.List | ||
9 | import qualified Data.ByteString.Lazy as LB | ||
10 | |||
11 | import qualified Text.Blaze.Renderer.Utf8 as Utf8 | ||
12 | import qualified Text.Blaze.Renderer.String as String | ||
13 | import qualified Text.Blaze.Renderer.Text as Text | ||
14 | |||
15 | import Benchmarks.HtmlBenchmarks (HtmlBenchmark (..), benchmarks) | ||
16 | import qualified Benchmarks.BlazeTBenchmarks as BT (HtmlBenchmark (..), benchmarks) | ||
17 | import qualified Text.BlazeT.Renderer.Utf8 as TUtf8 | ||
18 | import qualified Text.BlazeT.Renderer.String as TString | ||
19 | import qualified Text.BlazeT.Renderer.Text as TText | ||
20 | |||
21 | -- | Function to run the benchmarks using criterion | ||
22 | -- | ||
23 | main :: IO () | ||
24 | main = defaultMain $ zipWith g benchmarks BT.benchmarks | ||
25 | where | ||
26 | g x y = bgroup (hName x) $ benchHtml x ++ benchHtml2 y | ||
27 | benchHtml (HtmlBenchmark _ f x _) = | ||
28 | [ bench "Utf8" $ nf (LB.length . Utf8.renderMarkup . f) x | ||
29 | , bench "String" $ nf (String.renderMarkup . f) x | ||
30 | , bench "Text" $ nf (LT.length . Text.renderMarkup . f) x | ||
31 | ] | ||
32 | benchHtml2 (BT.HtmlBenchmark _ f x _) = | ||
33 | [ bench "BlazeT.Utf8" $ nf (LB.length . TUtf8.renderMarkup . f) x | ||
34 | , bench "BlazeT.String" $ nf (TString.renderMarkup . f) x | ||
35 | , bench "BlazeT.Text" $ nf (LT.length . TText.renderMarkup . f) x | ||
36 | ] | ||
diff --git a/src/Benchmarks/ServerChunkSize.hs b/src/Benchmarks/ServerChunkSize.hs new file mode 100644 index 0000000..7b01c68 --- /dev/null +++ b/src/Benchmarks/ServerChunkSize.hs | |||
@@ -0,0 +1,52 @@ | |||
1 | -- | A benchmark for measuring the impact of lazy bytestring chunk size on | ||
2 | -- server performance. | ||
3 | -- | ||
4 | {-# LANGUAGE OverloadedStrings #-} | ||
5 | module Main where | ||
6 | |||
7 | import Control.Concurrent (forkIO) | ||
8 | import Control.Monad (forever) | ||
9 | import Data.Monoid (mappend) | ||
10 | import Network (listenOn, PortID (PortNumber)) | ||
11 | import Network.Socket (accept, sClose) | ||
12 | import Prelude hiding (putStrLn) | ||
13 | import System.Environment (getArgs) | ||
14 | |||
15 | import Network.Socket.ByteString (recv, send) | ||
16 | import Network.Socket.ByteString.Lazy (sendAll) | ||
17 | import qualified Data.ByteString.Char8 as SBC | ||
18 | import qualified Data.ByteString.Lazy as LB | ||
19 | |||
20 | -- | Generate a 128k response, with a given chunk size. | ||
21 | -- | ||
22 | makeResponse :: Int -- ^ Chunk size. | ||
23 | -> LB.ByteString -- ^ Result. | ||
24 | makeResponse chunkSize = | ||
25 | let chunks = createChunks chunkSize totalSize | ||
26 | in LB.fromChunks chunks | ||
27 | where | ||
28 | -- A 64 kilobyte response. | ||
29 | totalSize = 128 * 1024 | ||
30 | |||
31 | createChunks c s | ||
32 | | c < s = SBC.replicate c 'a' : createChunks c (s - c) | ||
33 | | otherwise = SBC.replicate s 'a' : [] | ||
34 | |||
35 | main :: IO () | ||
36 | main = do | ||
37 | args <- getArgs | ||
38 | let port = PortNumber $ fromIntegral $ (read $ head args :: Int) | ||
39 | chunkSize = read $ args !! 1 | ||
40 | |||
41 | socket <- listenOn port | ||
42 | forever $ do | ||
43 | (s, _) <- accept socket | ||
44 | forkIO (respond chunkSize s) | ||
45 | where | ||
46 | respond chunkSize s = do | ||
47 | _ <- recv s 1024 | ||
48 | _ <- send s $ "HTTP/1.1 200 OK\r\n" | ||
49 | `mappend` "Content-Type: text/html; charset=UTF-8\r\n" | ||
50 | `mappend` "\r\n" | ||
51 | sendAll s $ makeResponse chunkSize | ||
52 | sClose s | ||
diff --git a/src/Benchmarks/bigtable/erb.rb b/src/Benchmarks/bigtable/erb.rb new file mode 100644 index 0000000..c3a675a --- /dev/null +++ b/src/Benchmarks/bigtable/erb.rb | |||
@@ -0,0 +1,32 @@ | |||
1 | # BigTable benchmark implemented in ERB. | ||
2 | # | ||
3 | require 'erb' | ||
4 | require 'benchmark' | ||
5 | include ERB::Util | ||
6 | |||
7 | table = (1 .. 1000).map do |_| (1 .. 10) end | ||
8 | |||
9 | template = ERB.new <<-EOF | ||
10 | <table> | ||
11 | <% table.each do |row| %> | ||
12 | <tr> | ||
13 | <% row.each do |value| %> | ||
14 | <td> | ||
15 | <%= value %> | ||
16 | </td> | ||
17 | <% end %> | ||
18 | </tr> | ||
19 | <% end %> | ||
20 | </table> | ||
21 | EOF | ||
22 | |||
23 | number_runs = 100 | ||
24 | start_time = Time.now.to_f | ||
25 | number_runs.times do | ||
26 | template.result(binding) | ||
27 | end | ||
28 | end_time = Time.now.to_f | ||
29 | |||
30 | # start_time and end_time are both in seconds now | ||
31 | ms = (end_time - start_time) * 1000 / number_runs | ||
32 | puts "\"ERB\", #{ms}" | ||
diff --git a/src/Benchmarks/bigtable/erubis.rb b/src/Benchmarks/bigtable/erubis.rb new file mode 100644 index 0000000..9a1edf9 --- /dev/null +++ b/src/Benchmarks/bigtable/erubis.rb | |||
@@ -0,0 +1,31 @@ | |||
1 | # BigTable benchmark implemented in erubis | ||
2 | # | ||
3 | require 'erubis' | ||
4 | require 'benchmark' | ||
5 | |||
6 | table = (1 .. 1000).map do |_| (1 .. 10) end | ||
7 | |||
8 | template = Erubis::Eruby.new <<-EOF | ||
9 | <table> | ||
10 | <% table.each do |row| %> | ||
11 | <tr> | ||
12 | <% row.each do |value| %> | ||
13 | <td> | ||
14 | <%= value %> | ||
15 | </td> | ||
16 | <% end %> | ||
17 | </tr> | ||
18 | <% end %> | ||
19 | </table> | ||
20 | EOF | ||
21 | |||
22 | number_runs = 100 | ||
23 | start_time = Time.now.to_f | ||
24 | number_runs.times do | ||
25 | template.result(binding) | ||
26 | end | ||
27 | end_time = Time.now.to_f | ||
28 | |||
29 | # start_time and end_time are both in seconds now | ||
30 | ms = (end_time - start_time) * 1000 / number_runs | ||
31 | puts "\"Erubis\", #{ms}" | ||
diff --git a/src/Benchmarks/bigtable/hamlet.hs b/src/Benchmarks/bigtable/hamlet.hs new file mode 100644 index 0000000..2778f2d --- /dev/null +++ b/src/Benchmarks/bigtable/hamlet.hs | |||
@@ -0,0 +1,33 @@ | |||
1 | -- | BigTable benchmark implemented using Hamlet. | ||
2 | -- | ||
3 | {-# LANGUAGE QuasiQuotes #-} | ||
4 | module Main where | ||
5 | |||
6 | import Criterion.Main | ||
7 | import Text.Hamlet | ||
8 | import Text.Hamlet.Monad | ||
9 | import Numeric (showInt) | ||
10 | import Data.Text (Text) | ||
11 | import qualified Data.Text as T | ||
12 | import Data.Maybe (fromJust) | ||
13 | |||
14 | main = defaultMain | ||
15 | [ bench "bigTable" $ nf bigTable bigTableData | ||
16 | ] | ||
17 | where | ||
18 | rows :: Int | ||
19 | rows = 1000 | ||
20 | |||
21 | bigTableData :: [[Int]] | ||
22 | bigTableData = replicate rows [1..10] | ||
23 | {-# NOINLINE bigTableData #-} | ||
24 | |||
25 | bigTable rows = fromJust $ hamletToText undefined [$hamlet| | ||
26 | %table | ||
27 | $forall rows row | ||
28 | %tr | ||
29 | $forall row cell | ||
30 | %td $showInt'.cell$ | ||
31 | |] | ||
32 | where | ||
33 | showInt' i = Encoded $ T.pack $ showInt i "" | ||
diff --git a/src/Benchmarks/bigtable/html-minimalist.hs b/src/Benchmarks/bigtable/html-minimalist.hs new file mode 100644 index 0000000..2a52751 --- /dev/null +++ b/src/Benchmarks/bigtable/html-minimalist.hs | |||
@@ -0,0 +1,20 @@ | |||
1 | -- | BigTable benchmark using the html-minimalist package from hackage. | ||
2 | -- | ||
3 | import Text.HTML.Light hiding (map) | ||
4 | import Criterion.Main | ||
5 | |||
6 | bigTable :: [[Int]] -> String | ||
7 | bigTable t = | ||
8 | renderXHTML xhtml_1_0_strict $ html [] $ return $ table [] $ map row t | ||
9 | where | ||
10 | row r = tr [] $ map (td [] . return . cdata . show) r | ||
11 | |||
12 | main = defaultMain | ||
13 | [ bench "bigTable" $ nf bigTable myTable ] | ||
14 | where | ||
15 | rows :: Int | ||
16 | rows = 1000 | ||
17 | |||
18 | myTable :: [[Int]] | ||
19 | myTable = replicate rows [1..10] | ||
20 | {-# NOINLINE myTable #-} | ||
diff --git a/src/Benchmarks/bigtable/html.hs b/src/Benchmarks/bigtable/html.hs new file mode 100644 index 0000000..57a62b3 --- /dev/null +++ b/src/Benchmarks/bigtable/html.hs | |||
@@ -0,0 +1,19 @@ | |||
1 | -- | BigTable benchmark using the HTML package from hackage. | ||
2 | -- | ||
3 | import Text.Html | ||
4 | import Criterion.Main | ||
5 | |||
6 | bigTable :: [[Int]] -> String | ||
7 | bigTable t = renderHtml $ table $ concatHtml $ map row t | ||
8 | where | ||
9 | row r = tr $ concatHtml $ map (td . stringToHtml . show) r | ||
10 | |||
11 | main = defaultMain | ||
12 | [ bench "bigTable" $ nf bigTable myTable ] | ||
13 | where | ||
14 | rows :: Int | ||
15 | rows = 1000 | ||
16 | |||
17 | myTable :: [[Int]] | ||
18 | myTable = replicate rows [1..10] | ||
19 | {-# NOINLINE myTable #-} | ||
diff --git a/src/Benchmarks/bigtable/php.php b/src/Benchmarks/bigtable/php.php new file mode 100644 index 0000000..f2e51a4 --- /dev/null +++ b/src/Benchmarks/bigtable/php.php | |||
@@ -0,0 +1,30 @@ | |||
1 | <?php | ||
2 | $table = array_fill(0, 1000, array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); | ||
3 | |||
4 | function test_bigtable($table) { | ||
5 | ob_start(); | ||
6 | ?> | ||
7 | <table> | ||
8 | <?php foreach($table as $row) { ?> | ||
9 | <tr> | ||
10 | <?php foreach($row as $value) { ?> | ||
11 | <td><?php echo $value; ?></td> | ||
12 | <?php } ?> | ||
13 | </tr> | ||
14 | <?php } ?> | ||
15 | </table> | ||
16 | <?php | ||
17 | return ob_get_clean(); | ||
18 | } | ||
19 | |||
20 | $request_count = 1000; | ||
21 | |||
22 | $start = microtime(true); | ||
23 | for ($i = 0; $i < $request_count; $i++) | ||
24 | { | ||
25 | test_bigtable($table); | ||
26 | } | ||
27 | $elapsed = microtime(true) - $start; | ||
28 | $time_per_request = ($elapsed / $request_count) * 1000; | ||
29 | echo "\"PHP\", $time_per_request\n"; | ||
30 | ?> | ||
diff --git a/src/Benchmarks/bigtable/xhtml.hs b/src/Benchmarks/bigtable/xhtml.hs new file mode 100644 index 0000000..993994c --- /dev/null +++ b/src/Benchmarks/bigtable/xhtml.hs | |||
@@ -0,0 +1,19 @@ | |||
1 | -- | BigTable benchmark using the XHTML package from hackage. | ||
2 | -- | ||
3 | import Text.XHtml.Strict | ||
4 | import Criterion.Main | ||
5 | |||
6 | bigTable :: [[Int]] -> String | ||
7 | bigTable t = renderHtml $ table $ concatHtml $ map row t | ||
8 | where | ||
9 | row r = tr $ concatHtml $ map (td . stringToHtml . show) r | ||
10 | |||
11 | main = defaultMain | ||
12 | [ bench "bigTable" $ nf bigTable myTable ] | ||
13 | where | ||
14 | rows :: Int | ||
15 | rows = 1000 | ||
16 | |||
17 | myTable :: [[Int]] | ||
18 | myTable = replicate rows [1..10] | ||
19 | {-# NOINLINE myTable #-} | ||