Skip to content

Commit 9498edf

Browse files
authored
Add link{L,R} for Set and Map (#1141)
linkL restores balance when the left tree is too heavy for the right tree, but unlike link it skips checking the other way around. linkR handles the opposite. linkL and linkR are used where we know the direction in which we need to rebalance. linkL and linkR delegate to linkL_ and linkR_, which do the actual work. link also delegates to linkL_ and linkR_, since the direction does not change when linking two trees. This single-direction linking algorithm can be seen in Figure 4 of "Parallel Ordered Sets Using Join". Similarly, link2/merge delegates to link2L_,link2_R/mergeL_,mergeR_. Benchmarks show a decrease in time and allocations, especially in set-set operations like union. Inspection of the Core of the previous link implementation revealed that it reboxes trees in some cases, which explains the decrease in allocations.
1 parent 8aa8647 commit 9498edf

File tree

2 files changed

+130
-48
lines changed

2 files changed

+130
-48
lines changed

containers/src/Data/Map/Internal.hs

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,7 @@ take i0 m0 = go i0 m0
15581558
go i (Bin _ kx x l r) =
15591559
case compare i sizeL of
15601560
LT -> go i l
1561-
GT -> link kx x l (go (i - sizeL - 1) r)
1561+
GT -> linkL kx x l (go (i - sizeL - 1) r)
15621562
EQ -> l
15631563
where sizeL = size l
15641564

@@ -1578,7 +1578,7 @@ drop i0 m0 = go i0 m0
15781578
go !_ Tip = Tip
15791579
go i (Bin _ kx x l r) =
15801580
case compare i sizeL of
1581-
LT -> link kx x (go i l) r
1581+
LT -> linkR kx x (go i l) r
15821582
GT -> go (i - sizeL - 1) r
15831583
EQ -> insertMin kx x r
15841584
where sizeL = size l
@@ -1600,9 +1600,9 @@ splitAt i0 m0
16001600
go i (Bin _ kx x l r)
16011601
= case compare i sizeL of
16021602
LT -> case go i l of
1603-
ll :*: lr -> ll :*: link kx x lr r
1603+
ll :*: lr -> ll :*: linkR kx x lr r
16041604
GT -> case go (i - sizeL - 1) r of
1605-
rl :*: rr -> link kx x l rl :*: rr
1605+
rl :*: rr -> linkL kx x l rl :*: rr
16061606
EQ -> l :*: insertMin kx x r
16071607
where sizeL = size l
16081608

@@ -3034,7 +3034,7 @@ filterWithKeyA p t@(Bin _ kx x l r) =
30343034
takeWhileAntitone :: (k -> Bool) -> Map k a -> Map k a
30353035
takeWhileAntitone _ Tip = Tip
30363036
takeWhileAntitone p (Bin _ kx x l r)
3037-
| p kx = link kx x l (takeWhileAntitone p r)
3037+
| p kx = linkL kx x l (takeWhileAntitone p r)
30383038
| otherwise = takeWhileAntitone p l
30393039

30403040
-- | \(O(\log n)\). Drop while a predicate on the keys holds.
@@ -3052,7 +3052,7 @@ dropWhileAntitone :: (k -> Bool) -> Map k a -> Map k a
30523052
dropWhileAntitone _ Tip = Tip
30533053
dropWhileAntitone p (Bin _ kx x l r)
30543054
| p kx = dropWhileAntitone p r
3055-
| otherwise = link kx x (dropWhileAntitone p l) r
3055+
| otherwise = linkR kx x (dropWhileAntitone p l) r
30563056

30573057
-- | \(O(\log n)\). Divide a map at the point where a predicate on the keys stops holding.
30583058
-- The user is responsible for ensuring that for all keys @j@ and @k@ in the map,
@@ -3075,8 +3075,8 @@ spanAntitone p0 m = toPair (go p0 m)
30753075
where
30763076
go _ Tip = Tip :*: Tip
30773077
go p (Bin _ kx x l r)
3078-
| p kx = let u :*: v = go p r in link kx x l u :*: v
3079-
| otherwise = let u :*: v = go p l in u :*: link kx x v r
3078+
| p kx = let u :*: v = go p r in linkL kx x l u :*: v
3079+
| otherwise = let u :*: v = go p l in u :*: linkR kx x v r
30803080

30813081
-- | \(O(n)\). Partition the map according to a predicate. The first
30823082
-- map contains all elements that satisfy the predicate, the second all
@@ -3842,7 +3842,7 @@ ascLinkTop (Push kx x l@(Bin lsz _ _ _ _) stk) !rsz r ky y
38423842
ascLinkTop stk !_ l kx x = Push kx x l stk
38433843

38443844
ascLinkAll :: Stack k a -> Map k a
3845-
ascLinkAll stk = foldl'Stack (\r kx x l -> link kx x l r) Tip stk
3845+
ascLinkAll stk = foldl'Stack (\r kx x l -> linkL kx x l r) Tip stk
38463846
{-# INLINABLE ascLinkAll #-}
38473847

38483848
-- | \(O(n)\). Build a map from a descending list of distinct elements in linear time.
@@ -3875,7 +3875,7 @@ descLinkTop ky y !_ r stk = Push ky y r stk
38753875
{-# INLINABLE descLinkTop #-}
38763876

38773877
descLinkAll :: Stack k a -> Map k a
3878-
descLinkAll stk = foldl'Stack (\l kx x r -> link kx x l r) Tip stk
3878+
descLinkAll stk = foldl'Stack (\l kx x r -> linkR kx x l r) Tip stk
38793879
{-# INLINABLE descLinkAll #-}
38803880

38813881
data Stack k a = Push !k a !(Map k a) !(Stack k a) | Nada
@@ -3939,8 +3939,8 @@ split !k0 t0 = toPair $ go k0 t0
39393939
case t of
39403940
Tip -> Tip :*: Tip
39413941
Bin _ kx x l r -> case compare k kx of
3942-
LT -> let (lt :*: gt) = go k l in lt :*: link kx x gt r
3943-
GT -> let (lt :*: gt) = go k r in link kx x l lt :*: gt
3942+
LT -> let (lt :*: gt) = go k l in lt :*: linkR kx x gt r
3943+
GT -> let (lt :*: gt) = go k r in linkL kx x l lt :*: gt
39443944
EQ -> (l :*: r)
39453945
#if __GLASGOW_HASKELL__
39463946
{-# INLINABLE split #-}
@@ -3964,10 +3964,10 @@ splitLookup k0 m = case go k0 m of
39643964
Tip -> StrictTriple Tip Nothing Tip
39653965
Bin _ kx x l r -> case compare k kx of
39663966
LT -> let StrictTriple lt z gt = go k l
3967-
!gt' = link kx x gt r
3967+
!gt' = linkR kx x gt r
39683968
in StrictTriple lt z gt'
39693969
GT -> let StrictTriple lt z gt = go k r
3970-
!lt' = link kx x l lt
3970+
!lt' = linkL kx x l lt
39713971
in StrictTriple lt' z gt
39723972
EQ -> StrictTriple l (Just x) r
39733973
#if __GLASGOW_HASKELL__
@@ -3988,10 +3988,10 @@ splitMember k0 m = case go k0 m of
39883988
Tip -> StrictTriple Tip False Tip
39893989
Bin _ kx x l r -> case compare k kx of
39903990
LT -> let StrictTriple lt z gt = go k l
3991-
!gt' = link kx x gt r
3991+
!gt' = linkR kx x gt r
39923992
in StrictTriple lt z gt'
39933993
GT -> let StrictTriple lt z gt = go k r
3994-
!lt' = link kx x l lt
3994+
!lt' = linkL kx x l lt
39953995
in StrictTriple lt' z gt
39963996
EQ -> StrictTriple l True r
39973997
#if __GLASGOW_HASKELL__
@@ -4079,11 +4079,38 @@ finishB (BMap m) = m
40794079
link :: k -> a -> Map k a -> Map k a -> Map k a
40804080
link kx x Tip r = insertMin kx x r
40814081
link kx x l Tip = insertMax kx x l
4082-
link kx x l@(Bin sizeL ky y ly ry) r@(Bin sizeR kz z lz rz)
4083-
| delta*sizeL < sizeR = balanceL kz z (link kx x l lz) rz
4084-
| delta*sizeR < sizeL = balanceR ky y ly (link kx x ry r)
4085-
| otherwise = bin kx x l r
4086-
4082+
link kx x l@(Bin lsz lkx lx ll lr) r@(Bin rsz rkx rx rl rr)
4083+
| delta*lsz < rsz = balanceL rkx rx (linkR_ kx x lsz l rl) rr
4084+
| delta*rsz < lsz = balanceR lkx lx ll (linkL_ kx x lr rsz r)
4085+
| otherwise = Bin (1+lsz+rsz) kx x l r
4086+
4087+
-- Variant of link. Restores balance when the left tree may be too large for the
4088+
-- right tree, but not the other way around.
4089+
linkL :: k -> a -> Map k a -> Map k a -> Map k a
4090+
linkL kx x l r = case r of
4091+
Tip -> insertMax kx x l
4092+
Bin rsz _ _ _ _ -> linkL_ kx x l rsz r
4093+
4094+
linkL_ :: k -> a -> Map k a -> Int -> Map k a -> Map k a
4095+
linkL_ kx x l !rsz r = case l of
4096+
Bin lsz lkx lx ll lr
4097+
| delta*rsz < lsz -> balanceR lkx lx ll (linkL_ kx x lr rsz r)
4098+
| otherwise -> Bin (1+lsz+rsz) kx x l r
4099+
Tip -> Bin (1+rsz) kx x Tip r
4100+
4101+
-- Variant of link. Restores balance when the right tree may be too large for
4102+
-- the left tree, but not the other way around.
4103+
linkR :: k -> a -> Map k a -> Map k a -> Map k a
4104+
linkR kx x l r = case l of
4105+
Tip -> insertMin kx x r
4106+
Bin lsz _ _ _ _ -> linkR_ kx x lsz l r
4107+
4108+
linkR_ :: k -> a -> Int -> Map k a -> Map k a -> Map k a
4109+
linkR_ kx x !lsz l r = case r of
4110+
Bin rsz rkx rx rl rr
4111+
| delta*lsz < rsz -> balanceL rkx rx (linkR_ kx x lsz l rl) rr
4112+
| otherwise -> Bin (1+lsz+rsz) kx x l r
4113+
Tip -> Bin (1+lsz) kx x l Tip
40874114

40884115
-- insertMin and insertMax don't perform potentially expensive comparisons.
40894116
insertMax,insertMin :: k -> a -> Map k a -> Map k a
@@ -4105,10 +4132,24 @@ insertMin kx x t
41054132
link2 :: Map k a -> Map k a -> Map k a
41064133
link2 Tip r = r
41074134
link2 l Tip = l
4108-
link2 l@(Bin sizeL kx x lx rx) r@(Bin sizeR ky y ly ry)
4109-
| delta*sizeL < sizeR = balanceL ky y (link2 l ly) ry
4110-
| delta*sizeR < sizeL = balanceR kx x lx (link2 rx r)
4111-
| otherwise = glue l r
4135+
link2 l@(Bin lsz lkx lx ll lr) r@(Bin rsz rkx rx rl rr)
4136+
| delta*lsz < rsz = balanceL rkx rx (link2R_ lsz l rl) rr
4137+
| delta*rsz < lsz = balanceR lkx lx ll (link2L_ lr rsz r)
4138+
| otherwise = glue l r
4139+
4140+
link2L_ :: Map k a -> Int -> Map k a -> Map k a
4141+
link2L_ l !rsz r = case l of
4142+
Bin lsz lkx lx ll lr
4143+
| delta*rsz < lsz -> balanceR lkx lx ll (link2L_ lr rsz r)
4144+
| otherwise -> glue l r
4145+
Tip -> r
4146+
4147+
link2R_ :: Int -> Map k a -> Map k a -> Map k a
4148+
link2R_ !lsz l r = case r of
4149+
Bin rsz rkx rx rl rr
4150+
| delta*lsz < rsz -> balanceL rkx rx (link2R_ lsz l rl) rr
4151+
| otherwise -> glue l r
4152+
Tip -> l
41124153

41134154
{--------------------------------------------------------------------
41144155
[glue l r]: glues two trees together.

containers/src/Data/Set/Internal.hs

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ ascLinkTop (Push x l@(Bin lsz _ _ _) stk) !rsz r y
12311231
ascLinkTop stk !_ r y = Push y r stk
12321232

12331233
ascLinkAll :: Stack a -> Set a
1234-
ascLinkAll stk = foldl'Stack (\r x l -> link x l r) Tip stk
1234+
ascLinkAll stk = foldl'Stack (\r x l -> linkL x l r) Tip stk
12351235
{-# INLINABLE ascLinkAll #-}
12361236

12371237
-- | \(O(n)\). Build a set from a descending list of distinct elements in linear time.
@@ -1259,7 +1259,7 @@ descLinkTop x !lsz l (Push y r@(Bin rsz _ _ _) stk)
12591259
descLinkTop y !_ r stk = Push y r stk
12601260

12611261
descLinkAll :: Stack a -> Set a
1262-
descLinkAll stk = foldl'Stack (\l x r -> link x l r) Tip stk
1262+
descLinkAll stk = foldl'Stack (\l x r -> linkR x l r) Tip stk
12631263
{-# INLINABLE descLinkAll #-}
12641264

12651265
data Stack a = Push !a !(Set a) !(Stack a) | Nada
@@ -1416,8 +1416,8 @@ splitS :: Ord a => a -> Set a -> StrictPair (Set a) (Set a)
14161416
splitS _ Tip = (Tip :*: Tip)
14171417
splitS x (Bin _ y l r)
14181418
= case compare x y of
1419-
LT -> let (lt :*: gt) = splitS x l in (lt :*: link y gt r)
1420-
GT -> let (lt :*: gt) = splitS x r in (link y l lt :*: gt)
1419+
LT -> let (lt :*: gt) = splitS x l in (lt :*: linkR y gt r)
1420+
GT -> let (lt :*: gt) = splitS x r in (linkL y l lt :*: gt)
14211421
EQ -> (l :*: r)
14221422
{-# INLINABLE splitS #-}
14231423

@@ -1428,10 +1428,10 @@ splitMember _ Tip = (Tip, False, Tip)
14281428
splitMember x (Bin _ y l r)
14291429
= case compare x y of
14301430
LT -> let (lt, found, gt) = splitMember x l
1431-
!gt' = link y gt r
1431+
!gt' = linkR y gt r
14321432
in (lt, found, gt')
14331433
GT -> let (lt, found, gt) = splitMember x r
1434-
!lt' = link y l lt
1434+
!lt' = linkL y l lt
14351435
in (lt', found, gt)
14361436
EQ -> (l, True, r)
14371437
#if __GLASGOW_HASKELL__
@@ -1558,7 +1558,7 @@ take i0 m0 = go i0 m0
15581558
go i (Bin _ x l r) =
15591559
case compare i sizeL of
15601560
LT -> go i l
1561-
GT -> link x l (go (i - sizeL - 1) r)
1561+
GT -> linkL x l (go (i - sizeL - 1) r)
15621562
EQ -> l
15631563
where sizeL = size l
15641564

@@ -1578,7 +1578,7 @@ drop i0 m0 = go i0 m0
15781578
go !_ Tip = Tip
15791579
go i (Bin _ x l r) =
15801580
case compare i sizeL of
1581-
LT -> link x (go i l) r
1581+
LT -> linkR x (go i l) r
15821582
GT -> go (i - sizeL - 1) r
15831583
EQ -> insertMin x r
15841584
where sizeL = size l
@@ -1598,9 +1598,9 @@ splitAt i0 m0
15981598
go i (Bin _ x l r)
15991599
= case compare i sizeL of
16001600
LT -> case go i l of
1601-
ll :*: lr -> ll :*: link x lr r
1601+
ll :*: lr -> ll :*: linkR x lr r
16021602
GT -> case go (i - sizeL - 1) r of
1603-
rl :*: rr -> link x l rl :*: rr
1603+
rl :*: rr -> linkL x l rl :*: rr
16041604
EQ -> l :*: insertMin x r
16051605
where sizeL = size l
16061606

@@ -1618,7 +1618,7 @@ splitAt i0 m0
16181618
takeWhileAntitone :: (a -> Bool) -> Set a -> Set a
16191619
takeWhileAntitone _ Tip = Tip
16201620
takeWhileAntitone p (Bin _ x l r)
1621-
| p x = link x l (takeWhileAntitone p r)
1621+
| p x = linkL x l (takeWhileAntitone p r)
16221622
| otherwise = takeWhileAntitone p l
16231623

16241624
-- | \(O(\log n)\). Drop while a predicate on the elements holds.
@@ -1636,7 +1636,7 @@ dropWhileAntitone :: (a -> Bool) -> Set a -> Set a
16361636
dropWhileAntitone _ Tip = Tip
16371637
dropWhileAntitone p (Bin _ x l r)
16381638
| p x = dropWhileAntitone p r
1639-
| otherwise = link x (dropWhileAntitone p l) r
1639+
| otherwise = linkR x (dropWhileAntitone p l) r
16401640

16411641
-- | \(O(\log n)\). Divide a set at the point where a predicate on the elements stops holding.
16421642
-- The user is responsible for ensuring that for all elements @j@ and @k@ in the set,
@@ -1659,8 +1659,8 @@ spanAntitone p0 m = toPair (go p0 m)
16591659
where
16601660
go _ Tip = Tip :*: Tip
16611661
go p (Bin _ x l r)
1662-
| p x = let u :*: v = go p r in link x l u :*: v
1663-
| otherwise = let u :*: v = go p l in u :*: link x v r
1662+
| p x = let u :*: v = go p r in linkL x l u :*: v
1663+
| otherwise = let u :*: v = go p l in u :*: linkR x v r
16641664

16651665
{--------------------------------------------------------------------
16661666
SetBuilder
@@ -1740,11 +1740,38 @@ finishB (BSet s) = s
17401740
link :: a -> Set a -> Set a -> Set a
17411741
link x Tip r = insertMin x r
17421742
link x l Tip = insertMax x l
1743-
link x l@(Bin sizeL y ly ry) r@(Bin sizeR z lz rz)
1744-
| delta*sizeL < sizeR = balanceL z (link x l lz) rz
1745-
| delta*sizeR < sizeL = balanceR y ly (link x ry r)
1746-
| otherwise = bin x l r
1747-
1743+
link x l@(Bin lsz lx ll lr) r@(Bin rsz rx rl rr)
1744+
| delta*lsz < rsz = balanceL rx (linkR_ x lsz l rl) rr
1745+
| delta*rsz < lsz = balanceR lx ll (linkL_ x lr rsz r)
1746+
| otherwise = Bin (1+lsz+rsz) x l r
1747+
1748+
-- Variant of link. Restores balance when the left tree may be too large for the
1749+
-- right tree, but not the other way around.
1750+
linkL :: a -> Set a -> Set a -> Set a
1751+
linkL x l r = case r of
1752+
Tip -> insertMax x l
1753+
Bin rsz _ _ _ -> linkL_ x l rsz r
1754+
1755+
linkL_ :: a -> Set a -> Int -> Set a -> Set a
1756+
linkL_ x l !rsz r = case l of
1757+
Bin lsz lx ll lr
1758+
| delta*rsz < lsz -> balanceR lx ll (linkL_ x lr rsz r)
1759+
| otherwise -> Bin (1+lsz+rsz) x l r
1760+
Tip -> Bin (1+rsz) x Tip r
1761+
1762+
-- Variant of link. Restores balance when the right tree may be too large for
1763+
-- the left tree, but not the other way around.
1764+
linkR :: a -> Set a -> Set a -> Set a
1765+
linkR x l r = case l of
1766+
Tip -> insertMin x r
1767+
Bin lsz _ _ _ -> linkR_ x lsz l r
1768+
1769+
linkR_ :: a -> Int -> Set a -> Set a -> Set a
1770+
linkR_ x !lsz l r = case r of
1771+
Bin rsz rx rl rr
1772+
| delta*lsz < rsz -> balanceL rx (linkR_ x lsz l rl) rr
1773+
| otherwise -> Bin (1+lsz+rsz) x l r
1774+
Tip -> Bin (1+lsz) x l Tip
17481775

17491776
-- insertMin and insertMax don't perform potentially expensive comparisons.
17501777
insertMax,insertMin :: a -> Set a -> Set a
@@ -1766,10 +1793,24 @@ insertMin x t
17661793
merge :: Set a -> Set a -> Set a
17671794
merge Tip r = r
17681795
merge l Tip = l
1769-
merge l@(Bin sizeL x lx rx) r@(Bin sizeR y ly ry)
1770-
| delta*sizeL < sizeR = balanceL y (merge l ly) ry
1771-
| delta*sizeR < sizeL = balanceR x lx (merge rx r)
1772-
| otherwise = glue l r
1796+
merge l@(Bin lsz lx ll lr) r@(Bin rsz rx rl rr)
1797+
| delta*lsz < rsz = balanceL rx (mergeR_ lsz l rl) rr
1798+
| delta*rsz < lsz = balanceR lx ll (mergeL_ lr rsz r)
1799+
| otherwise = glue l r
1800+
1801+
mergeL_ :: Set a -> Int -> Set a -> Set a
1802+
mergeL_ l !rsz r = case l of
1803+
Bin lsz lx ll lr
1804+
| delta*rsz < lsz -> balanceR lx ll (mergeL_ lr rsz r)
1805+
| otherwise -> glue l r
1806+
Tip -> r
1807+
1808+
mergeR_ :: Int -> Set a -> Set a -> Set a
1809+
mergeR_ !lsz l r = case r of
1810+
Bin rsz rx rl rr
1811+
| delta*lsz < rsz -> balanceL rx (mergeR_ lsz l rl) rr
1812+
| otherwise -> glue l r
1813+
Tip -> l
17731814

17741815
{--------------------------------------------------------------------
17751816
[glue l r]: glues two trees together.

0 commit comments

Comments
 (0)