Comments (12)
While I'm on the topic, we may want to add fusion rules for enumerations, so that fromList [1,3..n]
, say, gets rewritten to a fromFunction
form automatically. If we decide to leave fromList
alone, that means adding rules to (try to) fuse with build forms involving eftIntFB
, efdtIntFB
, and maybe a couple others, which will only actually fire on GHC >= 7.9. If we decide to change fromList
as I suggest above, we'd instead fuse with eftInt
, efdtInt
, etc., which would likely be easier.
from containers.
This would avoid the shuffling:
fromList' :: [a] -> Seq a
fromList' xs = Seq $ mkTree $ map Elem xs
mkTree :: (Sized a) => [a] -> FingerTree a
mkTree [] = Empty
mkTree [x1] = Single x1
mkTree [x1, x2] = deep (One x1) Empty (One x2)
mkTree [x1, x2, x3] = deep (One x1) Empty (Two x2 x3)
mkTree (x1:x2:x3:xs) = deep (Three x1 x2 x3) (mkTree ns) sf
where (ns, sf) = getNodes xs
getNodes :: (Sized a) => [a] -> ([Node a], Digit a)
getNodes [x1] = ([], One x1)
getNodes [x1, x2] = ([], Two x1 x2)
getNodes [x1, x2, x3] = ([], Three x1 x2 x3)
getNodes (x1:x2:x3:xs) = (node3 x1 x2 x3:ns, d)
where (ns, d) = getNodes xs
A further refinement would be to calculate the sizes of the nodes top-down, since they're all powers of 3.
from containers.
@RossPaterson Nice. I'll try to refine it as you suggest, and we'll see what the benchmarks say.
from containers.
With computed sizes:
fromList' :: [a] -> Seq a
fromList' xs = Seq $ mkTree 1 $ map Elem xs
mkTree :: (Sized a) => Int -> [a] -> FingerTree a
mkTree s [] = Empty
mkTree s [x1] = Single x1
mkTree s [x1, x2] = Deep (2*s) (One x1) Empty (One x2)
mkTree s [x1, x2, x3] = Deep (3*s) (One x1) Empty (Two x2 x3)
mkTree s (x1:x2:x3:xs) = deep (Three x1 x2 x3) (mkTree s' ns) sf
where
s' = 3*s
(ns, sf) = getNodes s' xs
getNodes :: Int -> [a] -> ([Node a], Digit a)
getNodes s [x1] = ([], One x1)
getNodes s [x1, x2] = ([], Two x1 x2)
getNodes s [x1, x2, x3] = ([], Three x1 x2 x3)
getNodes s (x1:x2:x3:xs) = (Node3 s x1 x2 x3:ns, d)
where (ns, d) = getNodes s xs
from containers.
Nicer! We may or may not be able to do even slightly better by returning the size along with ns
and ds
from getNodes
, so we won't need those deep
smart constructors hitting the now-out-of-cache left-side digits and nodes on the way back up the recursion.
from containers.
Oh, that probably makes no difference. I tend to forget just how short these spines are.
from containers.
Argh. I'm struggling to make this perform well. After some strictification, it reduces allocation marginally compared to the current version when the current version fuses with a list producer, and reduces allocation a little more when the current version does not fuse. But for some reason it seems the garbage collector ends up doing more work, and the total times don't look so great. Maybe someone else can give it a go, or I'll try some other possible modifications (e.g., see what happens if this right-fold-like arrangement is changed to a strict-left-fold-like one), or maybe this is just one of those ideas that look good but don't actually work out.
from containers.
I think the tricky thing is that we have to (in some fashion) pile up a stack of known prefixes (and perhaps their known lengths) while waiting for the middle lengths and the suffixes to become available. The exact manner in which we do this probably matters a lot from a performance standpoint, and I'm sure I don't know the right way.
from containers.
All right. I did a little more work on this and brought allocation down quite a bit more, with a 31% allocation reduction compared to the original without list fusion and 15% reduction compared to the original with list fusion. Unfortunately, it's still coming out slower, so I guess I have more work to do.
from containers.
OK, sorry for the noise. I think I fixed most of it. At one point I made one thing too strict. I got a version that allocates way less now. It's still making the garbage collector work harder for some reason, but the mutator time drops to compensate. I'll put together a pull request and see if anyone else can improve it further.
from containers.
I have a new idea that should allocate things in a more typical way, and
therefore might make the RTS happier. Instead of building a FingerTree
directly, build a FingerTreeLS, just like a FingerTree but with a lazier
spine (lazy in the sizes and the digits). Once that's built, converting it
to a FingerTree is just O(log n) time and space.
I also had an idea for reducing allocation further, that may or may not
work. At the top level (at least) try to do things in chunks of 9 instead
of 3. This avoids allocating a list of nodes a third the length of the
original list. Whether we do this or not, we probably want to special-case
the top level, because the map Elem
only comes free in GHC >= 7.9βin
earlier versions it would cost O(n) unless it fused with its argument.
from containers.
Merged the pull request.
from containers.
Related Issues (20)
- Symmetric difference for sets
- Incorrect order of Applicative actions in mergeA when using filterAMissing HOT 4
- `IntMap` `isProperSubmapOfBy` test failure HOT 4
- NonEmpty for CyclicSCC HOT 11
- better instance Hashable IntSet? HOT 8
- Unusual definition of foldrBits and foldlBits HOT 3
- Unnecessary CPP and C header in `Data.Map.Internal.Debug.html`?
- Release for GHC 9.8.1 HOT 17
- feat request: Add `popLeftWithValue` and `popWithValue` in `Data.Sequence` HOT 5
- Data.Graph: detect cycles utility functions HOT 2
- Data.Map.mergeWithKey doesn't match documentation
- Flag to introduce pedantic invariant checks? HOT 2
- Map.unionWith is over specialized and not consistent with intersectionWith HOT 10
- Add `flattenSCC1 : SCC vertex -> Data.List.NonEmpty.NonEmpty vertex` HOT 2
- Data.Map.Internal does not export insertMin
- Repo: remove merged branches?
- Contribution guide outdated?
- Potential memory optimization for IntMap and IntSet HOT 8
- Errors when trying to generate a test coverage report HOT 10
- Full IntSets? HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from containers.