packages feed

base64-0.3.1.1: benchmarks/Base64Bench.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Main
( main
) where


import Control.DeepSeq

import Criterion
import Criterion.Main

import "memory" Data.ByteArray.Encoding as Mem
import Data.ByteString
import "base64-bytestring" Data.ByteString.Base64 as Bos
import "base64" Data.ByteString.Base64 as B64
import Data.ByteString.Random (random)
import Data.Kind
import Data.Text (Text)
import qualified Data.Text.Encoding.Base64 as B64T


main :: IO ()
main = defaultMain $ bench' stepwise random encode_
    ++ bench' stepwise encoded decode_
    ++ bench' stepwise encoded lenient_
    ++ bench' megabytes random mbPerSec

  where
    encoded = fmap B64.encodeBase64' . random
    bench' sz f b = fmap (benchN f b) sz
    stepwise = [25,100,1000,10000,100000]
    megabytes = fmap (* 1000000) [1..5]
    benchN f bs n = env (f n) $ bgroup (show n) . bs

    encode_ e =
      [ bgroup "encode"
        [ encodeBench @'Mem e
        , encodeBench @'Bos e
        , encodeBench @'B64 e
        ]
      ]

    decode_ e =
      [ bgroup "decode"
        [ decodeBench @'Mem e
        , decodeBench @'Bos e
        , decodeBench @'B64 e
        ]
      ]

    lenient_ e =
      [ bgroup "decode-lenient"
        [ lenientBench @'Bos e
        , lenientBench @'B64 e
        ]
      ]

    mbPerSec e =
      [ bgroup "MB per second"
        [ encodeBench @'Mem e
        , encodeBench @'Bos e
        , encodeBench @'B64 e
        ]
      ]

encodeBench :: forall a. Harness a => Base64 a -> Benchmark
encodeBench = bench (label @a) . nf (encoder @a)

decodeBench :: forall a. Harness a => Base64 a -> Benchmark
decodeBench = bench (label @a) . nf (decoder @a)

lenientBench :: forall a. Harness a => Base64 a -> Benchmark
lenientBench = bench (label @a) . nf (lenient @a)

data Bench where
  Mem :: Bench
  Bos :: Bench
  B64 :: Bench
  T64 :: Bench


class (NFData (Base64 a), NFData (Err a)) => Harness (a :: Bench) where
    type Base64 a :: Type
    type Err a :: Type
    label :: String
    encoder :: Base64 a -> Base64 a
    decoder :: Base64 a -> Either (Err a) (Base64 a)
    lenient :: Base64 a -> Base64 a

instance Harness 'Mem where
    type Base64 'Mem = ByteString
    type Err 'Mem = String
    label = "memory"
    encoder = Mem.convertToBase Mem.Base64
    decoder = Mem.convertFromBase Mem.Base64
    lenient = id

instance Harness 'Bos where
    type Base64 'Bos = ByteString
    type Err 'Bos = String
    label = "base64-bytestring"
    encoder = Bos.encode
    decoder = Bos.decode
    lenient = Bos.decodeLenient

instance Harness 'B64 where
    type Base64 'B64 = ByteString
    type Err 'B64 = Text
    label = "base64"
    encoder = B64.encodeBase64'
    decoder = B64.decodeBase64
    lenient = B64.decodeBase64Lenient

instance Harness 'T64 where
    type Base64 'T64 = Text
    type Err 'T64 = Text
    label = "base64-text"
    encoder = B64T.encodeBase64
    decoder = B64T.decodeBase64
    lenient = B64T.decodeBase64Lenient