{-# LANGUAGE OverloadedStrings, BangPatterns, RecordWildCards, MagicHash #-}
import Data.ByteString (ByteString)
import Criterion.Main
import Data.BufferBuilder
import Control.Monad
scheme :: ByteString
scheme = "http"
host :: ByteString
host = "example.com"
path :: ByteString
path = "the/path/goes/here"
buildURL :: Int -> ByteString
buildURL times = runBufferBuilder $ do
replicateM_ times $ do
-- Sadly, if you look at the generated code, there are many
-- continuations (thus indirect jumps, thus inefficient stack
-- traffic) here. Even though the test strings are constant,
-- they become ByteString CAFs, and are tag-checked on every
-- use. However, appendBS followed by appendChar8 are folded
-- into the same generated function.
appendBS scheme
appendBS "://"
appendBS host
appendChar8 '/'
appendBS path
appendChar8 '?'
appendBS "key"
appendChar8 '='
appendBS "value"
appendChar8 '?'
appendBS "otherkey"
appendChar8 '='
appendBS "othervalue"
appendChar8 '#'
appendBS "hashyhashyhashy"
buildURLLiterals :: Int -> ByteString
buildURLLiterals times = runBufferBuilder $ do
replicateM_ times $ do
-- literals avoid the CAFs for ByteString constants
unsafeAppendLiteralN 4 "http"#
unsafeAppendLiteralN 3 "://"#
unsafeAppendLiteralN 11 "example.com"#
appendChar8 '/'
unsafeAppendLiteralN 18 "the/path/goes/here"#
appendChar8 '?'
unsafeAppendLiteralN 3 "key"#
appendChar8 '='
unsafeAppendLiteralN 5 "value"#
appendChar8 '?'
unsafeAppendLiteralN 8 "otherkey"#
appendChar8 '='
unsafeAppendLiteralN 10 "othervalue"#
appendChar8 '#'
unsafeAppendLiteralN 15 "hashyhashyhashy"#
data Record = Record
{ f1 :: !ByteString
, f2 :: !ByteString
, f3 :: !ByteString
, f4 :: !ByteString
, f5 :: !ByteString
, f6 :: !ByteString
}
encodeType :: Record -> ByteString
encodeType !(Record{..}) = runBufferBuilder $ do
-- Because the record elements are strict, all of these appendBS
-- calls are emitted in one long Cmm/x86 function. Can't do much
-- better than that. :) However, if you look closely, the
-- BufferWriter handle (accessed through ReaderT) is reloaded from
-- the stack after all appendBS. In the future, GHC could realize
-- it's always the same value and only load it once.
appendBS f1
appendBS f2
appendBS f3
appendBS f4
appendBS f5
appendBS f6
recordValue :: Record
recordValue = Record { f1 = "the wheels on the bus go round and round"
, f2 = "round and round, round and round"
, f3 = "the seats on the bus go up and down"
, f4 = "up and down, up and down"
, f5 = "the doors on the bus go open and shut"
, f6 = "open and shut, open and shut"
}
main :: IO ()
main = defaultMain [ bench "buildURL" $ nf buildURL 10
, bench "buildURLLiterals" $ nf buildURLLiterals 10
, bench "encodeRecord" $ nf encodeType recordValue ]