packages feed

linear-base-0.8.1: bench/Data/Mutable/Array.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneKindSignatures #-}

-- Uncomment the line below to observe the generated (optimised) Core. It will
-- land in a file named “Array.dump-simpl”
-- {-# OPTIONS_GHC -ddump-simpl -ddump-to-file -dsuppress-all -dsuppress-uniques #-}

module Data.Mutable.Array (benchmarks) where

import Control.DeepSeq (rnf)
import qualified Data.Array.Mutable.Linear as Array.Linear
import qualified Data.Array.Mutable.Linear as Array.Linear.Array
import qualified Data.Foldable
import Data.Functor.Compose
import Data.Kind
import qualified Data.Sequence
import qualified Data.Unrestricted.Linear as Linear
import qualified Data.Vector
import Prelude.Linear (($), (&))
import qualified Prelude.Linear as Linear
import Test.Tasty.Bench
import Prelude hiding (($))

arr_size :: Int
arr_size = 1_000

benchmarks :: Benchmark
benchmarks =
  bgroup
    "arrays"
    $ runImpls
      [ bAlloc,
        bToList,
        bMap,
        bReads,
        bSets
      ]

--------------------------------------------------------------------------------

data Impl where
  Impl :: String -> (forall arr. (ArrayThing arr) => arr Int %1 -> ()) -> Impl

runImpls :: [Impl] -> [Benchmark]
runImpls = map (runImpl arr_size)

runImpl :: Int -> Impl -> Benchmark
runImpl sz0 (Impl name impl) =
  bgroup
    name
    [ bench "Data.Array.Mutable.Linear" $ whnf (runLinear impl) sz0,
      bench "Data.Vector" $ whnf (runDataVector (cleanup impl)) sz0,
      bench "Data.Sequence" $ whnf (runSequence (cleanup impl)) sz0
    ]
  where
    runLinear :: (Array.Linear.Array Int %1 -> ()) -> Int -> ()
    runLinear cb sz = Linear.unur (Array.Linear.alloc sz 0 (\a -> Linear.move (cb a)))

    runDataVector :: (Data.Vector.Vector Int -> ()) -> Int -> ()
    runDataVector cb sz = cb (Data.Vector.replicate sz 0)

    runSequence :: (Data.Sequence.Seq Int -> ()) -> Int -> ()
    runSequence cb sz = cb (Data.Sequence.replicate sz 0)
{-# INLINE runImpl #-}

type ArrayThing :: (Type -> Type) -> Constraint
class ArrayThing arr where
  size :: arr a %1 -> (Linear.Ur Int, arr a)
  get :: Int -> arr a %1 -> (Linear.Ur a, arr a)
  set :: Int -> a -> arr a %1 -> arr a
  toList :: arr a %1 -> Linear.Ur [a]
  amap :: (a -> b) -> arr a %1 -> arr b

  -- | Note: I [Arnaud Spiwack] initially thought I could use
  -- 'Consumable'/'consume' for this. But it doesn't work because the natural
  -- 'consume' function for `Ur x` doesn't evaluate the `x` at all. We need to
  -- evaluate the `x` in the 'Vector' instance.
  force :: arr a %1 -> ()

type UArrayThing :: (Type -> Type) -> Constraint
class UArrayThing arr where
  usize :: arr a -> Int
  uget :: Int -> arr a -> a
  uset :: Int -> a -> arr a -> arr a
  utoList :: arr a -> [a]
  uamap :: (a -> b) -> arr a -> arr b
  uforce :: arr a -> ()

instance ArrayThing Array.Linear.Array where
  size = Array.Linear.Array.size
  get = Array.Linear.Array.unsafeGet
  set = Array.Linear.Array.unsafeSet
  toList = Array.Linear.Array.toList
  amap = Array.Linear.Array.map
  force = Linear.consume

instance (UArrayThing arr) => ArrayThing (Compose Linear.Ur arr) where
  size (Compose (Linear.Ur arr)) = (Linear.Ur (usize arr), Compose (Linear.Ur arr))
  get i (Compose (Linear.Ur arr)) = (Linear.Ur (uget i arr), Compose (Linear.Ur arr))
  set i a (Compose (Linear.Ur arr)) = Compose (Linear.Ur (uset i a arr))
  toList (Compose (Linear.Ur arr)) = Linear.Ur (utoList arr)
  amap f (Compose (Linear.Ur arr)) = Compose (Linear.Ur (uamap f arr))
  force (Compose (Linear.Ur arr)) = uforce arr

instance UArrayThing Data.Vector.Vector where
  usize = Data.Vector.length
  uget i v = v Data.Vector.! i
  uset i a v = v Data.Vector.// [(i, a)]
  utoList = Data.Vector.toList
  uamap = Data.Vector.map
  uforce = (`seq` ())

instance UArrayThing Data.Sequence.Seq where
  usize = Data.Sequence.length
  uget i s = Data.Sequence.index s i
  uset = Data.Sequence.update
  utoList = Data.Foldable.toList
  uamap = fmap

  -- I'm not sure about this one: on the one hand it forces the data structure
  -- to be allocated. On the other hand, it will do an extra traversal. Maybe
  -- there's a better comparison that can be done.
  uforce s = (foldMap (\_ -> Strict) s) `seq` ()

cleanup :: ((Compose Linear.Ur f a) %1 -> b) -> (f a -> b)
cleanup k a = k (Compose (Linear.Ur a))

data Strict = Strict

instance Semigroup Strict where
  Strict <> x = x

instance Monoid Strict where
  mempty = Strict

--------------------------------------------------------------------------------

bToList :: Impl
bToList = Impl "toList" impl
  where
    impl :: (ArrayThing arr) => arr Int %1 -> ()
    impl arr = arr & toList & Linear.lift rnf & Linear.unur

bMap :: Impl
bMap = Impl "map" impl
  where
    impl :: (ArrayThing arr) => arr Int %1 -> ()
    impl arr =
      case arr & amap (+ 1) & get 5 of
        (Linear.Ur _, arr') -> force arr'

bReads :: Impl
bReads = Impl "reads" impl
  where
    impl :: (ArrayThing arr) => arr Int %1 -> ()
    impl arr0 =
      case size arr0 of
        (Linear.Ur sz, arr) -> go 0 sz arr
      where
        go :: (ArrayThing arr) => Int -> Int -> arr Int %1 -> ()
        go start end arr
          | start < end =
              case get start arr of
                (Linear.Ur i, arr') -> i `Linear.seq` go (start + 1) end arr'
          | otherwise = force arr

bAlloc :: Impl
bAlloc = Impl "alloc" impl
  where
    impl :: (ArrayThing arr) => arr Int %1 -> ()
    impl = force

bSets :: Impl
bSets = Impl "successive writes (very unfair to vector)" impl
  where
    impl :: (ArrayThing arr) => arr Int %1 -> ()
    impl arr0 =
      case size arr0 of
        (Linear.Ur sz, arr) -> go 0 sz arr
      where
        go :: (ArrayThing arr) => Int -> Int -> arr Int %1 -> ()
        go start end arr
          | start < end =
              go (start + 1) end Linear.$ set start 42 arr
          | otherwise = force arr