-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Reintroduces the 'mangle' abstraction that was removed in the switch to robinhood hashing. #13197
Conversation
Could you please amend the commit message with a better description? (and perhaps squash these commits) Thanks |
Done. |
When fixing commit messages you should also fix PR title/description. I went ahead and updated them for you. |
Thanks a lot! |
IIRC |
Uhh you CAN do it that way. But unless the compiler makes some pretty crazy inlining decisions there will be unnecessary closure creation. |
I, too, would rather avoid closures if possible. |
Can we measure the cost in this situation before making assumptions? :) |
It seems a little odd to change already-clear code to use a confusing function (I find Also, without labeled arguments, using mangle looks like crap at the call site. |
I also took the liberty of removing some instances of useless double hashing in this patch. |
@cgaebel Because it's less code, and the implementations of pub fn find_or_insert<'a>(&'a mut self, k: K, v: V) -> &'a mut V {
self.mangle(k, v, |_, v| v, |_, _, _| ())
}
pub fn find_or_insert_with<'a>(&'a mut self, k: K, f: |&K| -> V) -> &'a mut V {
self.mangle(k, (), |k, _| f(k), |_, _, _| ())
}
pub fn insert_or_update_with<'a>(&'a mut self, k: K, v: V, f: |&K, &mut V|) -> &'a mut V {
self.mangle(k, v, |_, v| v, |k, v, _| f(k, v))
} |
I agree that they are one line implementations. My (possibly flawed) intuition says that these three functions will get inlined into their caller, and I also find code like |
@cgaebel I never trust intuition when thinking about inlining. If LLVM thinks that inlining those functions into their caller and creating the closures is better than inlining mangle into those functions and skipping the closures, well, that's entirely up to it. As for code golf, it's a bit messy, but luckily the callers of those functions don't have to ever look at their implementation. As for people who do read the source, it's pretty easy to verify that those functions are correct (and in fact it's difficult to implement them incorrectly and still typecheck). Additionally, once written, these functions never have to change, even if the implementation of the hashmap changes again. With these versions, only |
Well, no they don't need to be rewritten if the implementation changes. They don't do anything hashtable-implementation specific. I can, however, more easily reason about their performance. And I don't have to "trust LLVM" to not create closures. |
Also, @kballard, do you have any thoughts on what I said in the bug report (replicated below)? I feel like mangle is the wrong abstraction to make: I feel like there's room for a more usable and powerful abstraction here. Something like: pub struct HashWithKey<K, 'a> {
priv hash: SafeHash;
priv key: &'a K;
}
impl<K, 'a> HashWithKey<K, 'a> {
pub fn key(&self) -> &'a K;
fn hash(&self) -> &'a SafeHash;
}
pub fn hash<'a>(&self, k: &'a K) -> HashWithKey<K, 'a> And expose all my methods that take a prehashed key. |
@cgaebel Just looking at that right now, I have no idea what I'm supposed to do with The point of |
Oops I may have edited that code into meaninglessness. The intent is that you can hash a key once and then safely use it for multiple operations on the hashtable without having to rehash in between. That's the only thing mangle gives you, and is artificially limiting. I already have a lot of operations that take a pre-hashed key, and it'd be nice to expose this infrastructure to the user. Thinking a bit more about this, it might be a little trickier than I originally thought. Maybe you can see a nicer way of doing it? |
Hmm if I punt on making it safe I could provide an 'unsafe_hash' that hashes a key, and just force people to wrap their custom HashMap primitives in unsafe blocks. But that's kinda sad. |
There's only one use of mangle in both rustc and servo, and this is to create an |
}, | ||
Some(idx) => { | ||
let (_, v_ref) = self.table.read_mut(&idx); | ||
*v_ref = v; | ||
} | ||
} | ||
} |
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.
Isn't this method the same as insert
?
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.
Huh. Good point! That means servo's only use of mangle
:
self.idmap.mangle(new_id, abstract_self,
|_, new_node: AbstractNode| -> AbstractNode {
new_node
},
|_, old_node: &mut AbstractNode, new_node: AbstractNode| {
*old_node = new_node;
});
should have been:
self.idmap.insert(new_id, abstract_node);
Is that measured after |
@huonw When I landed the HashMap patch, hashmap.rs was the only file that was changed. Therefore mangle wasn't used by rustc. Since I found one usage of mangle in servo (which was better written |
…to robinhood hashing.
Closing due to inactivity, but feel free to reopen with a rebase! |
Fixes #13195.