Skip to content

Attempting some stack safety strategies #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: nub-ord-bench
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions bench/Bench/Data/List.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@ module Bench.Data.List where
import Prelude
import Data.Foldable (maximum)
import Data.Int (pow)
import Data.List (List(..), take, range, foldr, length, nub)
import Data.List (List(..), take, range, foldr, length, sortBy, addIndexReverse, mapReverse, nub, nubBy, nubBySafe, nubByAdjacentReverse)
import Data.Maybe (fromMaybe)
import Data.Traversable (traverse_)
import Effect (Effect)
import Effect.Console (log)
import Performance.Minibench (bench)
import Performance.Minibench (bench, benchWith)

benchList :: Effect Unit
benchList = do
--benchLists "map" $ map (_ + 1)
--benchLists "foldr" $ foldr add 0
benchLists "nub" nub

--benchLists "nub" nub -- not stack safe
benchLists "nubByAdjacentReverse" $ nubByAdjacentReverse eq -- safe
benchLists "nubBySafe" $ nubBySafe compare -- safe
benchLists "addIndexReverse" addIndexReverse -- safe
benchLists "mapReverse" $ mapReverse $ add 1 -- safe
benchLists "sortBy" $ sortBy compare -- NOT SAFE!!!!
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem

--benchLists "nubBy" $ nubBy compare -- not stack safe

where

listSizes = Cons 0 $ map (pow 10) $ range 0 5
listSizes = Cons 0 $ map (pow 10) $ range 0 6
nats = range 0 $ (fromMaybe 0 $ maximum listSizes) - 1
lists = map (\n -> take n nats) listSizes

Expand All @@ -30,4 +37,4 @@ benchList = do
benchAList label func list = do
log "---"
log $ label <> ": list (" <> show (length list) <> " elems)"
bench \_ -> func list
benchWith 1 \_ -> func list
32 changes: 30 additions & 2 deletions src/Data/List.purs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ module Data.List
, groupAllBy
, partition

, nubByAdjacentReverse
, addIndexReverse
, mapReverse
, nubBySafe

, nub
, nubBy
, nubEq
Expand Down Expand Up @@ -698,6 +703,23 @@ nubBy p =
-- Add indices so we can recover original order after deduplicating.
<<< addIndexReverse

-- Same as nubBy, but eliminates nubByAdjacentReverse.
-- This is just for exploring stack safety.
-- It doesn't actually remove duplicates.
nubBySafe :: forall a. (a -> a -> Ordering) -> List a -> List a
nubBySafe p =
-- Discard indices, just keep original values.
mapReverse snd
-- Sort by index to recover original order.
-- Use `flip` to sort in reverse order in anticipation of final `mapReverse`.
<<< sortBy (flip compare `on` fst)
-- Removing neighboring duplicates.
-- <<< nubByAdjacentReverse (\a b -> (p `on` snd) a b == EQ)
-- Sort by original values to cluster duplicates.
<<< sortBy (p `on` snd)
-- Add indices so we can recover original order after deduplicating.
<<< addIndexReverse

-- | Remove duplicate elements from a list.
-- | Keeps the first occurrence of each element in the input list,
-- | in the same order they appear in the input list.
Expand Down Expand Up @@ -860,7 +882,10 @@ foldM f b (a : as) = f b a >>= \b' -> foldM f b' as
-- |
-- | Running time: `O(n)`
mapReverse :: forall a b. (a -> b) -> List a -> List b
mapReverse f = go Nil
mapReverse f l = reverse $ map f l
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using this slower, but definitely stack safe alternative (not saying the other version is stack unsafe, but just trying to eliminate unknowns).


mapReverse2 :: forall a b. (a -> b) -> List a -> List b
mapReverse2 f = go Nil
where
go :: List b -> List a -> List b
go acc Nil = acc
Expand All @@ -876,7 +901,10 @@ mapReverse f = go Nil
-- |
-- | Running time: `O(n)`
addIndexReverse :: forall a. List a -> List (Tuple Int a)
addIndexReverse = go 0 Nil
addIndexReverse = reverse <<< mapWithIndex Tuple
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be stack safe too, right?


addIndexReverse2 :: forall a. List a -> List (Tuple Int a)
addIndexReverse2 = go 0 Nil
where
go :: Int -> List (Tuple Int a) -> List a -> List (Tuple Int a)
go i acc Nil = acc
Expand Down