-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Add a min heap option to PriorityQueue #15947
Comments
I would tend to favor composability over duplication such as this, so something along the lines of a "invert ord" wrapper may work well. |
I agree, but how would we realize it? Using a newtype with derived traits?
Or have a generic struct which implements the
|
@treeman here's a rough impl for the struct: http://is.gd/UnblpD Unfortunately, this means the user has to actively wrap/unwrap the value, and be exposed to the internal comparison details. Wouldn't something like this make more sense?
Then a priority queue can be instantiated with whatever comparison logic is desirable, without the need to wrap values. |
It would be really slow if it used a function pointer. |
You're probably right. I'm not familiar with how Rust can optimize/inline some of these things. Would a closure or proc be better? Is there any way to convince Rust "hey, create a concrete instance of this type, but with this one method's behavior changed", that isn't slow? It just seems to me like ordering the elements should be the responsibility of the structure, not the data itself. I guess we could template the priority queue on a comparator object? Would that be fast enough? |
Adding a comparator type parameter would work. It would need to use the closure traits. |
@thestinger I hacked together a very quick proof-of-concept, and threw it and std at my priorityqueue bench suite:
(Note Ord and Unord are actually reversed since I wrote this for minheaps) Edit: this is using their natural orderings, in case it wasn't clear. |
It would be more than 8% after fixing the existing high performance cost from making the code safe during unwinding. The cost of indirect calls is also lower in a micro-benchmark than the real world where the entire program doesn't fit in the cache and there are a lot of branches. It's unclear which optimization level you're using and what you're using the measure the performance anyway. |
My benchmarks are here. Tested them with uints. They basically just build an iterator of uints of size 100 or 10000 (anything more just takes forever to bench) with different ordering properties. The individual bench names are fairly self explanatory. I used the default optimization provided by cargo (and presumably rustc). Is there any access pattern and/or compiler flags you'd like to see? Or do I even need to justify using comparators to gain flexibility? Edit: Oh I just noticed your note about closure traits. My proof-of-concept just made actual (empty) structs implementing a trait with a cmp(&self, &T, &T) method. Would closure traits provide more efficient options here? |
Rust doesn't optimize by default, without passing
It would allowing passing a function/closure without defining a type every time. |
A stored function pointer will have high overhead and a good implementation with a type parameter would have zero overhead. |
Oh wow, I didn't realize how massive the performance gap was from default and -O:
Percentage-wise, perf difference is about the same, if not smaller. I'll start making a less hacked-together impl that uses closure traits. I'm guessing one source of overhead might be checking cmp(a, b) == Greater vs just *a > *b. |
Sorry, which traits exactly do you consider the "closure" ones? |
Hmm, how do want to combine the notion of specifying comparators with all the other different constructors? Should they all require a comparator to be passed to them? Or should we provide _with_comparator variants, and have the normal ones requires |
@treeman @thestinger I've got a rough design of PriorityQueue with Comparators. I'd like to discuss design before I work on tests and cleaning up docs. Should I just make a gist or something? Or submit a WIP pull request? |
I think a WIP pull request would be fine. |
Pull request up at #16047 |
@gankro what's the state of this these days? |
collect-rs has an experimental design for Comparators. We're baking it out-of-tree for now. Should be back-compat to add later with default generics. |
Interested in this as well. |
There is now a extern crate revord;
let mut pq = BinaryHeap::new();
pq.push((RevOrd(1), "foo"));
pq.push((RevOrd(0), "bar"));
pq.push((RevOrd(2), "baz"));
assert_eq!(pq.pop(), (RevOrd(0), "bar")); |
@steveklabnik Considering #40720, maybe this can be closed? |
We discussed this during triage today and while we'd like to see this feature it'd likely be done with comparators nowadays, which would require an RFC, so we decided to close this. |
Apologies for poking an old issue, but I just ran into a situation where a min-heap would be useful. I'm currently working around it by wrapping all the elements I put on the heap in std::cmp::Reverse, but the (wrap->push), (pop->unwrap) operations feel sort of clumsy. Additionally, if I didn't have to wrap, I could construct the heap with @alexcrichton's last message mentioned doing this with comparators, which I assume meant redoing the BinaryHeap to use comparators for item comparison, and allowing the user to specify the comparator. I'm not sure where this is though, as I believe comparators are still in a separate crate? (https://github.com/contain-rs/compare) Is there anything I could do to help work on this? |
I have a binary-heap-plus crate that is backward compatible with std’s BinaryHeap. You might find it useful: |
Is there some reason that we can't just use |
For min heap only support, Reverse is OK. But there are use cases where you need to compare by arbitrary order. Of course C++ has this. |
Sure, I mean, it's easy enough to implement an arbitrary |
Implementing Ord and friends includes a logic duplication (PartialOrd etc) and not simple enough, in my opinion. |
why was this closed ? was this implemented ? |
Fix panic with closure inside array len I was working on rust-lang#15947 and found out that we panic on this test: ``` fn main() { let x = [(); &(&'static: loop { |x| {}; }) as *const _ as usize] } ``` This PR fixes the panic. Closures in array len are still broken, but closure in const eval is not stable anyway.
Currently
PriorityQueue
defaults to a max-heap and if you want to use it as a min-heap you need to overloadOrd
and change the comparison.But you should be able to use the default ordering and simply specify that you want a min-heap, so we can use it for types like
(uint, &str)
for example.The simple idea would be to simply make a
new_min_heap
and amin_heap_with_capacity
constructors.Thoughts?
The text was updated successfully, but these errors were encountered: