Comments (6)
Updated benchmark code shows somewhat similar memory usage by all 3 libraries
Case Allocated GCs
pqueue 1000000 5,278,798,384 5,037
psqueues 1000000 5,045,176,008 4,844
heaps 1000000 4,815,937,992 4,630
Benchmark code
{- cabal:
build-depends: base, chronos-bench, heaps, pqueue, psqueues, random, random-shuffle, weigh
ghc-options: -O
-}
{-# LANGUAGE BlockArguments #-}
import qualified Chronos.Bench as Chronos
import Control.Monad
import Data.Foldable
import Data.Function
import qualified Data.Heap as Heaps
import qualified Data.IntPSQ as Psqueues
import qualified Data.PQueue.Prio.Min as Pqueue
import System.Random (randomIO, randomRIO)
import System.Random.Shuffle (shuffleM)
import qualified Weigh
type Level = Int
type Node = Int
main :: IO ()
main = do
benchmarkMemory
benchmarkTime
benchmarkMemory :: IO ()
benchmarkMemory = do
let n = 1000000
nodes <- randomNodes n
Weigh.mainWith do
Weigh.func ("pqueue " ++ show n) (pqueueDrain . pqueueFromList) nodes
Weigh.func ("psqueues " ++ show n) (psqueuesDrain . psqueuesFromList) nodes
Weigh.func ("heaps " ++ show n) (heapsDrain . heapsFromList) nodes
benchmarkTime :: IO ()
benchmarkTime = do
let n = 100
nodes <- randomNodes n
Chronos.defaultMain
[ Chronos.bench ("pqueue insert/pop x" ++ show n) (pqueueDrain . pqueueFromList) nodes,
Chronos.bench ("psqueues insert/pop x" ++ show n) (psqueuesDrain . psqueuesFromList) nodes,
Chronos.bench ("heaps insert/pop x" ++ show n) (heapsDrain . heapsFromList) nodes
]
randomNodes :: Int -> IO [(Node, Level)]
randomNodes n =
zip
<$> (shuffleM =<< replicateM n randomIO)
<*> replicateM n (randomRIO (1, 10))
pqueueDrain :: Pqueue.MinPQueue Level Node -> ()
pqueueDrain q =
case Pqueue.minView q of
Nothing -> ()
Just (_, q') -> pqueueDrain q'
psqueuesDrain :: Psqueues.IntPSQ Level Node -> ()
psqueuesDrain q =
case Psqueues.minView q of
Nothing -> ()
Just (_, _, _, q') -> psqueuesDrain q'
heapsDrain :: Heaps.Heap (Heaps.Entry Level Node) -> ()
heapsDrain q =
case Heaps.viewMin q of
Nothing -> ()
Just (_, q') -> heapsDrain q'
pqueueFromList :: [(Node, Level)] -> Pqueue.MinPQueue Level Node
pqueueFromList =
foldl' (\acc (node, level) -> Pqueue.insert level node acc) Pqueue.empty
psqueuesFromList :: [(Node, Level)] -> Psqueues.IntPSQ Level Node
psqueuesFromList =
fst . foldl' step (Psqueues.empty, 1)
where
step (acc, counter) (node, level) =
let counter' = counter + 1
in counter' `seq` (Psqueues.insert counter level node acc, counter')
heapsFromList :: [(Node, Level)] -> Heaps.Heap (Heaps.Entry Level Node)
heapsFromList =
foldl'
(\acc (node, level) -> Heaps.insert (Heaps.Entry level node) acc)
Heaps.empty
from reactive-banana.
Fyi, pqueue
now supports GHC 9 (lspitzner/pqueue#43) and is actively maintained again (lspitzner/pqueue#41).
from reactive-banana.
Hm. I would also prefer to switch to a more maintained library, but the problem is that these libraries offer slightly different APIs that are subtly incompatible.
pqueue
supports distinct elements with the same priority. That's what we want.- In
psqueues
, queue elements are distinguished not by themselves, but by unique keys. This would force us to equip theSomeNode
type with a unique identifier, so that it can be used "as its own key". - In
heaps
, theEntry p a
type makes it possible to attach elements (typea
) to priorities. However, twoEntry
are considered identical if their priorities are equal — this is not what we want.
In the worst case, that is if pqueue
will never be compatible with new versions of GHC 9, I would rather include parts of its code in reactive-banana
, provided that the license allows this.
from reactive-banana.
Ah, interesting! Yes, I ran into that pecularity in psqueues
when writing the benchmark. I ended up identifying each node by a counter that just gets incremented by one each insert, but I can see how threading that state through in the reactive-banana implementation would be a little bit clumsy.
Also, perhaps relevant, the last psqueues
upload to hackage was actually in June 2019, whereas pqueue
had a release in 2020. I'm not sure if psqueues
is being actively maintained - if I had to guess, it is - but it's still interesting that it's gone untouched for over 2 years.
And finally, about heaps
, could you clarify why the Entry
Eq
instance is problematic? Presumably it was done to make Eq
/Ord
agree with each other, but the data structure does permit duplicates, so (as long as we don't get tripped up comparing Entry
to each other when we should be comparing the SomeNode
inside) it does seem to me like it'd work. Unfortunately, it's a little bit slower than the others, at least in my benchmark.
from reactive-banana.
Yes, I ran into that pecularity in psqueues when writing the benchmark. I ended up identifying each node by a counter that just gets incremented by one each insert, but I can see how threading that state through in the reactive-banana implementation would be a little bit clumsy.
Yes, that would be clumsy. The very existence of the pqueue
package demonstrates that it would also be unnecessary, which is a good argument not to do it.
Also, perhaps relevant, the last psqueues upload to hackage was actually in June 2019, whereas pqueue had a release in 2020.
It appears that pqueue
has a co-maintainer as well. In any case, if it's just about relaxing version bounds, I think there are Hackage trustees who can publish package revisions. I'd say we wait for something like that? Again, in the worst case, I'd prefer including source code directly in reactive-banana.
Presumably it was done to make Eq/Ord agree with each other, but the data structure does permit duplicates.
Ah, I missed that the structures permits duplicates. Still, in this case, I think that the Eq
instance is a bad idea, because I think that it's fair to expect that any Eq
instance should satisfy the following property: If x == y
, then this implies x = y
for all intents and purposes. The Entry
type violates that, and I feel very uneasy about using types where basic assumptions about equality are not satisfied.
(PS: With "for all intents and purposes", I mean that "as far as the public API is concerned, no distinction can be made". For example, if two maps from Data.Map
are equal according to ==
, then the balanced trees used internally need not be equal, but this difference cannot be detected with the public API.)
from reactive-banana.
As pqeue
is maintained again, I'd like to keep the status quo and close this issue. Please feel free to reopen.
from reactive-banana.
Related Issues (20)
- Add `Monoid a => Monoid (MomentIO a)` (and `Moment`)
- Add `@>` HOT 3
- Successful evaluation for an undefined network HOT 5
- `reactive-banana` can't be compiled on GHC 9.2 HOT 2
- I created a Matrix channel HOT 8
- An interesting `Applicative`-ish way of combining `Event`s HOT 4
- Add `MonadMoment` instances for all monad transformers in `transformers` HOT 1
- Release 1.3.0.0 HOT 3
- Join Stackage HOT 4
- `Behavior` constantly re-evaluating when caching is expected HOT 4
- Unexpected memory growth HOT 6
- Event handler not seeing early events. HOT 12
- `Behavior`s can hold onto more memory than they need to HOT 2
- Memory leak with dynamic behavior switching HOT 16
- Gloss GUI events not responding HOT 5
- Idea: add a combinator to track when a behavior is a different value HOT 14
- Call `unregister` for garbage collected events made by `AddHandler`. HOT 9
- Discrepancy in model and implementation HOT 15
- Optimize representation of Latch HOT 1
- Not compatible with these-1.2 (disallowed) HOT 2
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 reactive-banana.