-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Implement paged SparseArray #3813
Conversation
I could add unsafe unchecked gets while indexing into individual pages, but I think that's outside of the scope of this initial PR, adds a lot more |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the make_page
function is sound now. The tradeoffs of whether this is worth it I can't comment on.
Out of curiosity I compared the assembly to a simple
fn make_page() -> Box<[Option<V>; PAGE_SIZE>] {
Box::new([None; PAGE_SIZE])
}
and with opt-level = 2
and V=usize
it results in the exact same assembly:
https://godbolt.org/z/3T47bMrre
With a page size of 1024
however or type V = [u8; 12]
(if I'm reading the assembly correct) the array is written to the stack and then memcpy
d to the allocated box.
|
||
#[derive(Debug)] | ||
pub struct SparseArray<I, V = I> { | ||
values: Vec<Option<V>>, | ||
values: Vec<Option<Box<[Option<V>; PAGE_SIZE]>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a comment linking back to the PR; the details are relatively arcane and stumbling across this type will be intimidating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look fine to me, but like always, I'd like to see both performance and memory benchmarks here.
Ran some basic benchmarks. The results are, as expected, rather poor. This hurts sparse iteration/fetches the most, with a 8-17% regression in these benchmarks, and since we're using SparseSet to back a lot of our other storage, it affected quite a bit more too. We may want to wait on the other ID size optimization PRs to be merged in before rerunning these benchmarks and attempting to merge this.
|
Closing as part of backlog cleanup effort. @james7132 will leave up to you if this is still relevant in 2024 and needs an issue to be created 🙏 |
Objective
Reduce the memory usage of SparseArray when very large indicies are used.
Solution
Replace the current backing Vec to hold
Option<Box<[Option<V>; PAGE_SIZE]>>
instead of justOption<V>
. Any pages that have no corresponding elements are set toNone
, and a page is allocated only when it becomes populated. In the scenarios where the vast majority of the elements areNone
, as expected ofSparseSets
, this can save a significant amount of memory.This PR contains one
unsafe
block to get around the array initialization limitations of Rust to create new pages. By default,Option
always implements Default, returning None, but the array Default impl only goes up to a size of 32.The page size is set to 64 elements per page.
Pros
Cons
This can be used alongside #3678, and/or #2104 to further reduce memory usage.