packages feed

apply-merge-0.1.1.0: bench/Bench/ApplyMerge.hs

-- SPDX-FileCopyrightText: Copyright Preetham Gujjula
-- SPDX-License-Identifier: BSD-3-Clause
{-# LANGUAGE ImpredicativeTypes #-}

module Bench.ApplyMerge (benchmarks) where

import ApplyMerge.DoublyLinkedList qualified
import ApplyMerge.IntMap qualified
import ApplyMerge.IntSet qualified
import ApplyMerge.MergeAll qualified
import Data.Function ((&))
import Data.List.Ordered (minus)
import Test.Tasty.Bench (Benchmark, bench, bgroup, nf)

benchmarks :: Benchmark
benchmarks =
  bgroup
    "ApplyMerge"
    [ funcToBenchmark
        "linear shape: applyMerge const [1..] [1..]"
        const,
      funcToBenchmark
        "double linear shape: applyMerge min [1..] [1..]"
        min,
      funcToBenchmark
        "box shape: applyMerge max [1..] [1..]"
        max,
      funcToBenchmark
        "triangular shape: applyMerge (+) [1..] [1..]"
        (+),
      funcToBenchmark
        "skewed triangular shape: applyMerge (\\x y -> 4 * x + y) [1..] [1..]"
        (\x y -> 4 * x + y),
      funcToBenchmark
        "hyperbolic shape: applyMerge (*) [1..] [1..]"
        (*),
      funcToBenchmark
        "skewed hyperbolic shape: applyMerge (\\x y -> x^3 * y) [1..] [1..]"
        ( \x y ->
            floor @Double $
              100 * (3 * log (fromIntegral x + fromIntegral y))
        ),
      funcToBenchmark
        "circular shape: applyMerge (\\x y -> x*x + y*y) [1..] [1..]"
        (\x y -> x * x + y * y),
      funcToBenchmark
        "elliptical shape: applyMerge (\\x y -> 4*x*x + y*y) [1..] [1..]"
        (\x y -> 4 * x * x + y * y),
      collapseToBenchmark "primes" $ \applyMerge n ->
        let zero :: Int
            zero = (n `quot` maxBound)

            primes :: [Int]
            primes = 2 : 3 : 5 : ([7 ..] `minus` composites)

            composites :: [Int]
            composites = applyMerge (\p j -> p * (p + j)) primes [zero ..]
         in sum (takeWhile (<= n) primes)
    ]

type ApplyMerge = forall a b c. (Ord c) => (a -> b -> c) -> [a] -> [b] -> [c]

funcToBenchmark :: String -> (Int -> Int -> Int) -> Benchmark
funcToBenchmark name f = collapseToBenchmark name (funcToCollapse f)

funcToCollapse :: (Int -> Int -> Int) -> ApplyMerge -> Int -> Int
funcToCollapse f applyMerge n =
  let one = (n `quot` maxBound) + 1
   in applyMerge f [one ..] [one ..]
        & take n
        & sum

collapseToBenchmark :: String -> (ApplyMerge -> Int -> Int) -> Benchmark
collapseToBenchmark name collapse = bgroup name (map mkBench [1 .. 6])
  where
    mkBench :: Int -> Benchmark
    mkBench i =
      let limit :: Int
          limit = 10 ^ i

          applyMerges :: [(String, ApplyMerge)]
          applyMerges =
            [ ("DoublyLinkedList", ApplyMerge.DoublyLinkedList.applyMerge),
              ("IntMap", ApplyMerge.IntMap.applyMerge),
              ("IntSet", ApplyMerge.IntSet.applyMerge),
              ("MergeAll", ApplyMerge.MergeAll.applyMerge),
              ( "MergeAll (flipped)",
                flip . ApplyMerge.MergeAll.applyMerge . flip
              )
            ]
       in bgroup (show limit) $
            flip map applyMerges $ \(applyMergeName, applyMerge) ->
              bench applyMergeName (nf (collapse applyMerge) limit)