Skip to content

Commit 41b7cb4

Browse files
int-etreeowl
authored andcommitted
Exploit some invariants
Consequently, get rid of ApState. This speeds up the immediate-indexing test substantially: Old: benchmarking <*>/ix1000/500000 time 2.688 μs (2.607 μs .. 2.798 μs) 0.994 R² (0.988 R² .. 1.000 R²) mean 2.632 μs (2.607 μs .. 2.715 μs) std dev 129.9 ns (65.93 ns .. 242.8 ns) variance introduced by outliers: 64% (severely inflated) New: benchmarking <*>/ix1000/500000 time 1.410 μs (1.402 μs .. 1.417 μs) 1.000 R² (1.000 R² .. 1.000 R²) mean 1.417 μs (1.411 μs .. 1.425 μs) std dev 21.45 ns (16.80 ns .. 31.73 ns) variance introduced by outliers: 14% (moderately inflated)
1 parent 8b47db3 commit 41b7cb4

File tree

1 file changed

+47
-73
lines changed

1 file changed

+47
-73
lines changed

Data/Sequence.hs

Lines changed: 47 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ apShort :: Seq (a -> b) -> Seq a -> Seq b
277277
apShort (Seq fs) xs = Seq $ case toList xs of
278278
[a,b] -> ap2FT fs (a,b)
279279
[a,b,c] -> ap3FT fs (a,b,c)
280-
_ -> error "apShort: not 2-6"
280+
_ -> error "apShort: not 2-3"
281281

282282
ap2FT :: FingerTree (Elem (a->b)) -> (a,a) -> FingerTree (Elem b)
283283
ap2FT fs (x,y) = Deep (size fs * 2)
@@ -298,104 +298,85 @@ ap3FT fs (x,y,z) = Deep (size fs * 3)
298298
-- <*> when the length of each argument is at least four.
299299
apty :: Seq (a -> b) -> Seq a -> Seq b
300300
apty (Seq fs) (Seq xs@Deep{}) = Seq $
301-
runApState (fmap firstf) (fmap lastf) fmap fs' (ApState xs' xs' xs')
301+
Deep (s' * size fs)
302+
(fmap (fmap firstf) pr')
303+
(aptyMiddle (fmap firstf) (fmap lastf) fmap fs' xs')
304+
(fmap (fmap lastf) sf')
302305
where
303306
(Elem firstf, fs', Elem lastf) = trimTree fs
304-
xs' = rigidify xs
307+
xs'@(Deep s' pr' _m' sf') = rigidify xs
305308
apty _ _ = error "apty: expects a Deep constructor"
306309

307-
data ApState a = ApState (FingerTree a) (FingerTree a) (FingerTree a)
308-
309-
-- | 'runApState' uses three copies of the @xs@ tree to produce the @fs<*>xs@
310-
-- tree. It pulls left digits off the left tree, right digits off the right tree,
311-
-- and squashes down the other four digits. Once it gets to the bottom, it turns
312-
-- the middle tree into a 2-3 tree, applies 'mapMulFT' to produce the main body,
313-
-- and glues all the pieces together.
314-
runApState
310+
-- | 'aptyMiddle' does most of the hard work of computing @fs<*>xs@.
311+
-- It produces the center part of a finger tree, with a prefix corresponding
312+
-- to the prefix of @xs@ and a suffix corresponding to the suffix of @xs@
313+
-- omitted; the missing suffix and prefix are added by the caller.
314+
-- For the recursive call, it squashes the prefix and the suffix into
315+
-- the center tree. Once it gets to the bottom, it turns the tree into
316+
-- a 2-3 tree, applies 'mapMulFT' to produce the main body, and glues all
317+
-- the pieces together.
318+
aptyMiddle
315319
:: Sized c =>
316320
(c -> d)
317321
-> (c -> d)
318322
-> ((a -> b) -> c -> d)
319323
-> FingerTree (Elem (a -> b))
320-
-> ApState c
321-
-> FingerTree d
324+
-> FingerTree c
325+
-> FingerTree (Node d)
322326
-- Not at the bottom yet
323-
runApState firstf
327+
aptyMiddle firstf
324328
lastf
325329
map23
326330
fs
327-
(ApState
328-
(Deep sl
329-
prl
330-
(Deep sml prml mml sfml)
331-
sfl)
332-
(Deep sm
333-
prm
334-
(Deep _smm prmm mmm sfmm)
335-
sfm)
336-
(Deep sr
337-
prr
338-
(Deep smr prmr mmr sfmr)
339-
sfr))
340-
= Deep (sl + sr + sm * size fs)
341-
(fmap firstf prl)
342-
(runApState (fmap firstf)
331+
(Deep s pr (Deep sm prm mm sfm) sf)
332+
= Deep (sm + s * (size fs + 1)) -- note: sm = s - size pr - size sf
333+
(fmap (fmap firstf) prm)
334+
(aptyMiddle (fmap firstf)
343335
(fmap lastf)
344336
(\f -> fmap (map23 f))
345337
fs
346-
nextState)
347-
(fmap lastf sfr)
348-
where nextState =
349-
ApState
350-
(Deep (sml + size sfl) prml mml (squashR sfml sfl))
351-
(Deep sm (squashL prm prmm) mmm (squashR sfmm sfm))
352-
(Deep (smr + size prr) (squashL prr prmr) mmr sfmr)
338+
(Deep s (squashL pr prm) mm (squashR sfm sf)))
339+
(fmap (fmap lastf) sfm)
353340

354341
-- At the bottom
355-
runApState firstf
342+
aptyMiddle firstf
356343
lastf
357344
map23
358345
fs
359-
(ApState
360-
(Deep sl prl ml sfl)
361-
(Deep sm prm mm sfm)
362-
(Deep sr prr mr sfr))
363-
= Deep (sl + sr + sm * size fs)
364-
(fmap firstf prl)
365-
((fmap (fmap firstf) ml `snocTree` fmap firstf (digitToNode sfl))
366-
`appendTree0` middle `appendTree0`
367-
(fmap lastf (digitToNode prr) `consTree` fmap (fmap lastf) mr))
368-
(fmap lastf sfr)
369-
where middle = case trimTree $ mapMulFT sm (\(Elem f) -> fmap (fmap (map23 f)) converted) fs of
346+
(Deep s pr m sf)
347+
= (fmap (fmap firstf) m `snocTree` fmap firstf (digitToNode sf))
348+
`appendTree0` middle `appendTree0`
349+
(fmap lastf (digitToNode pr) `consTree` fmap (fmap lastf) m)
350+
where middle = case trimTree $ mapMulFT s (\(Elem f) -> fmap (fmap (map23 f)) converted) fs of
370351
(firstMapped, restMapped, lastMapped) ->
371352
Deep (size firstMapped + size restMapped + size lastMapped)
372353
(nodeToDigit firstMapped) restMapped (nodeToDigit lastMapped)
373-
converted = case mm of
374-
Empty -> Node2 sm lconv rconv
375-
Single q -> Node3 sm lconv q rconv
376-
Deep{} -> error "runApState: a tree is shallower than the middle tree"
377-
lconv = digitToNode prm
378-
rconv = digitToNode sfm
354+
converted = case m of
355+
Empty -> Node2 s lconv rconv
356+
Single q -> Node3 s lconv q rconv
357+
Deep{} -> error "aptyMiddle: impossible"
358+
lconv = digitToNode pr
359+
rconv = digitToNode sf
379360

380-
runApState _ _ _ _ _ = error "runApState: ApState must hold Deep finger trees of the same depth"
361+
aptyMiddle _ _ _ _ _ = error "aptyMiddle: expected Deep finger tree"
381362

382363
{-# SPECIALIZE
383-
runApState
364+
aptyMiddle
384365
:: (Node c -> d)
385366
-> (Node c -> d)
386367
-> ((a -> b) -> Node c -> d)
387368
-> FingerTree (Elem (a -> b))
388-
-> ApState (Node c)
389-
-> FingerTree d
369+
-> FingerTree (Node c)
370+
-> FingerTree (Node d)
390371
#-}
391372
{-# SPECIALIZE
392-
runApState
373+
aptyMiddle
393374
:: (Elem c -> d)
394375
-> (Elem c -> d)
395376
-> ((a -> b) -> Elem c -> d)
396377
-> FingerTree (Elem (a -> b))
397-
-> ApState (Elem c)
398-
-> FingerTree d
378+
-> FingerTree (Elem c)
379+
-> FingerTree (Node d)
399380
#-}
400381

401382
digitToNode :: Sized a => Digit a -> Node a
@@ -2096,16 +2077,9 @@ reverseNode f (Node3 s a b c) = Node3 s (f c) (f b) (f a)
20962077
-- Mapping with a splittable value
20972078
------------------------------------------------------------------------
20982079

2099-
-- For zipping, and probably also for (<*>), it is useful to build a result by
2080+
-- For zipping, it is useful to build a result by
21002081
-- traversing a sequence while splitting up something else. For zipping, we
2101-
-- traverse the first sequence while splitting up the second [and third [and
2102-
-- fourth]]. For fs <*> xs, we hope to traverse
2103-
--
2104-
-- > replicate (length fs * length xs) ()
2105-
--
2106-
-- while splitting something essentially equivalent to
2107-
--
2108-
-- > fmap (\f -> fmap f xs) fs
2082+
-- traverse the first sequence while splitting up the second.
21092083
--
21102084
-- What makes all this crazy code a good idea:
21112085
--
@@ -2129,8 +2103,8 @@ reverseNode f (Node3 s a b c) = Node3 s (f c) (f b) (f a)
21292103
-- they're actually needed. We do the same thing for Digits (splitting into
21302104
-- between one and four pieces) and Nodes (splitting into two or three). The
21312105
-- ultimate result is that we can index into, or split at, any location in zs
2132-
-- in O((log(min{i,n-i}))^2) time *immediately*, while still being able to
2133-
-- force all the thunks in O(n) time.
2106+
-- in polylogarithmic time *immediately*, while still being able to force all
2107+
-- the thunks in O(n) time.
21342108
--
21352109
-- Benchmark info, and alternatives:
21362110
--

0 commit comments

Comments
 (0)