packages feed

apply-merge-0.1.0.0: bench/Bench/Data/DoublyLinkedList/STRef.hs

-- SPDX-FileCopyrightText: Copyright Preetham Gujjula
-- SPDX-License-Identifier: BSD-3-Clause

module Bench.Data.DoublyLinkedList.STRef (benchmarks) where

import Control.Monad (forM_, void)
import Control.Monad.ST (runST)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Maybe (MaybeT (..), runMaybeT)
import Data.DoublyLinkedList.STRef
  ( cons,
    delete,
    empty,
    head,
    insertAfter,
    prev,
    snoc,
    value,
  )
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Maybe (fromMaybe)
import Test.Tasty.Bench (Benchmark, bench, bgroup, nf)
import Prelude hiding (head, last, null)

superscript :: Char -> Char
superscript c = fromMaybe c (Map.lookup c superscriptMap)

superscriptMap :: Map Char Char
superscriptMap =
  Map.fromList
    [ ('0', '⁰'),
      ('1', '¹'),
      ('2', '²'),
      ('3', '³'),
      ('4', '⁴'),
      ('5', '⁵'),
      ('6', '⁶'),
      ('7', '⁷'),
      ('8', '⁸'),
      ('9', '⁹')
    ]

benchmarks :: Benchmark
benchmarks =
  bgroup
    "Data.DoublyLinkedList.STRef"
    [ constructionBenchmarks,
      traversalBenchmarks,
      queryBenchmarks,
      insertionBenchmarks,
      deletionBenchmarks,
      listConversionBenchmarks
    ]

-- * Construction

constructionBenchmarks :: Benchmark
constructionBenchmarks =
  bgroup
    "Construction"
    [ emptyBenchmarks,
      fromListBenchmarks
    ]

emptyBenchmarks :: Benchmark
emptyBenchmarks = bgroup "empty" []

fromListBenchmarks :: Benchmark
fromListBenchmarks = bgroup "fromList" []

-- * Traversal

traversalBenchmarks :: Benchmark
traversalBenchmarks =
  bgroup
    "Traversal"
    [ headBenchmarks,
      lastBenchmarks,
      nextBenchmarks,
      prevBenchmarks
    ]

headBenchmarks :: Benchmark
headBenchmarks = bgroup "head" []

lastBenchmarks :: Benchmark
lastBenchmarks = bgroup "last" []

nextBenchmarks :: Benchmark
nextBenchmarks = bgroup "next" []

prevBenchmarks :: Benchmark
prevBenchmarks = bgroup "prev" []

-- * Query

queryBenchmarks :: Benchmark
queryBenchmarks = bgroup "Query" [nullBenchmarks, valueBenchmarks]

nullBenchmarks :: Benchmark
nullBenchmarks = bgroup "null" []

valueBenchmarks :: Benchmark
valueBenchmarks = bgroup "value" []

-- * Insertion

insertionBenchmarks :: Benchmark
insertionBenchmarks =
  bgroup
    "Insertion"
    [ consBenchmarks,
      snocBenchmarks,
      insertAfterBenchmarks,
      insertBeforeBenchmarks
    ]

consBenchmarks :: Benchmark
consBenchmarks = bgroup "cons" $
  flip map [(1 :: Int) .. 6] $ \i ->
    bench ("cons 10" ++ map superscript (show i) ++ " times") $
      flip nf ((10 :: Int) ^ i) $ \n -> runST $ do
        list <- empty
        forM_ [1 .. n] (cons list)
        firstNode <- head list
        let firstValue = value <$> firstNode
        pure firstValue

snocBenchmarks :: Benchmark
snocBenchmarks = bgroup "snoc" $
  flip map [(1 :: Int) .. 6] $ \i ->
    bench ("snoc 10" ++ map superscript (show i) ++ " times") $
      flip nf ((10 :: Int) ^ i) $ \n -> runST $ do
        list <- empty
        forM_ [1 .. n] (snoc list)
        firstNode <- head list
        let firstValue = value <$> firstNode
        pure firstValue

insertBeforeBenchmarks :: Benchmark
insertBeforeBenchmarks = bgroup "insertBefore" []

insertAfterBenchmarks :: Benchmark
insertAfterBenchmarks = bgroup "insertAfter" []

-- * Deletion

deletionBenchmarks :: Benchmark
deletionBenchmarks = bgroup "Deletion" [deleteBenchmarks]

deleteBenchmarks :: Benchmark
deleteBenchmarks = bgroup "delete (uses insertAfter and prev)" $
  flip map [(1 :: Int) .. 6] $ \i ->
    bench ("delete 10" ++ map superscript (show i) ++ " times") $
      flip nf ((10 :: Int) ^ i) $ \n -> runST $ runMaybeT $ do
        list <- lift empty
        node1 <- lift (cons list (1 :: Int))
        node2 <- lift (insertAfter node1 2)
        node3 <- lift (insertAfter node2 3)
        forM_ [1 .. n] $ \j -> do
          lift (void (insertAfter node1 j))
          prevNode <- MaybeT (prev node3)
          lift (delete prevNode)

-- * List conversion

listConversionBenchmarks :: Benchmark
listConversionBenchmarks = bgroup "List conversion" [toListBenchmarks]

toListBenchmarks :: Benchmark
toListBenchmarks = bgroup "toList" []