-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Use grailsort for sort_unstable #81842
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Can you explain the difference between Grailsort and the current |
The current
Block merge sorts, such as Grailsort (and certain WIP derivative projects), in contrast, Balance the conditions of the prior 2 sorts, producing a sort that maintains fair performance in O(n log n) time, maintains stability between values, and can be executed in-place, with zero additional allocations for buffers or extra space. tl;dr: |
@666666t By “certain WIP derivative projects,” you mean Holy Grailsort? (there’s also Kota, Ecta, and Helium) |
Correct, though given the original focus of this issue cites Grailsort in particular, It seemed preferable to keep the focus on that, while Holy Grailsort and similar WIP projects seek to improve adaptability and certain other aspects but generally don't appear as complete as the current Grailsort rewrites. |
Regardless, as for a generic run-down of the algorithm itself; Grailsort works by maintaining an in-place buffer of unique values which are collected from the slice at the beginning, generally of some power of 2 size which scales with sqrt(n). It uses this in-place buffer (which is guaranteed to be stable by only collecting unique values) to traverse the array, performing in-place merges with the provided space. For fragments which are small enough to be merged, it does this outright, in one piece. For fragments which are larger than the available in-place buffer, the fragments are merged in "blocks", using a method of dividing the merge operation which involves "tagging" of blocks and fragments to ensure stability. After all fragments are merged, the in-place buffer is re-inserted to the slice, using a method of block rotations for quick insertion. I'm certainly not the most versed in explaining the algorithm as a whole, but that should give a short idea of how the parts of the algorithm function in theory. |
Ah, sorry, I should make it clear for noting; Grailsort is a stable sort, in-line with |
Right, definitely want to make clear Grailsort is a stable sort. It's not unstable. |
How does its speed compare to |
@scottmcm While Grailsort has a solid design to it, there are many flaws and opportunities to micro-optimize which generally make a "vanilla" implementation slower than Timsort. Generally speaking, I would characterize Grailsort as decently cache-friendly due to relatively simple linear memory accesses while also scaling pretty favorably. According to the original author, comparisons are ~1.6 * n log n and writes are ~2.1 * n log n in the worst-case. Grailsort is nowhere near as adaptive as Timsort, and in fact we're pretty sure its best case is O(n log n). For what it is, I think it's pretty clever and impressive but it definitely has some kinks that need to be ironed out. Our plans for an optimized Grailsort yield an algorithm that is at least 15-30% faster on average. |
Is the issue title correct? Would you intend to replace the stable or the unstable sort implementation? |
Its the other way around. The That said I'd welcome improvements to the stable |
@the8472 My bad for not being clear enough, I wasn't aiming to necessarily replace any functions in Rust. I was asking if people still were interested in a non-allocating sort. |
(Or at least, a non-allocating stable sort, in this case.) |
I wrote an in-depth explanation of the Huang and Langston-style block sort (grailsort) as well as a Rust implementation. Perhaps this will help potential reviewers to understand the block sort paradigm. I looked at some of the implementations in the Rewritten Grailsort project, but it didn't seem to close the documentation gap between grailsort and "wikisort". In the process, I was able to implement the block tagging scheme described by Huang and Langston, although I'm not sure if halving the size of the internal buffer is actually worth the extra bookkeeping. Personally, I would not recommend we switch |
Triage: Thank you for reporting. Always interesting to learn about new sorting algorithms.
Then let's close this issue for now, but feel free to reopen when the Grailsort implementation outperforms what Rust currently has! |
Hello, Rust contributors!
I've been managing a bit of a passion project centered around Grailsort, a stable, in-place, O(n log n) worst-case sorting algorithm, for a while now. It was partially inspired by previous issues brought up in this repo, including #19221 and
rust-lang/rfcs#956 (comment).
Our project is currently focusing on rewriting and documenting Grailsort, with the hopes of making the algorithm much more readable and intuitive.
I was curious if there's still interest in implementing an in-place sort like this for Rust, especially because one of our contributors is maintaining a WIP Rust version of said rewrite in our repo.
https://github.com/MusicTheorist/Rewritten-Grailsort
Feel free to let us know what your thoughts are. Thanks for reading!
The text was updated successfully, but these errors were encountered: