-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[stdlib] Implement the new _modify accessor in Dictionary.subscript #18974
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
[stdlib] Implement the new _modify accessor in Dictionary.subscript #18974
Conversation
This enables in-place mutations of the key-based subscript. These get rid of one of two full hash table lookup operations (including hashing the key) relative to the getter+setter approach. They can also eliminate COW copies in code like this: dictionary[foo]?.append(bar) (Admittedly this isn’t a very useful example.)
@swift-ci smoke test |
Tests, motivating examples are not yet done. Implementation details will improve with #18790. |
General question for @rjmccall - are we going to use this with arrays too, or are we going to keep addressors around as a language feature and synthesize a ‘modify’ out of them? |
Addressors can't completely disappear because we have to bootstrap unsafe pointers somehow. But yes, #18840 can synthesize a modify from a mutable addressor. |
@swift-ci please smoke benchmark staging |
Build comment file:Performance: -O
Performance: -Osize
How to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the regressions before you merge the PR. Noise: Sometimes the performance results (not code size!) contain false alarms. Unexpected regressions which are marked with '(?)' are probably noise. If you see regressions which you cannot explain you can try to run the benchmarks again. If regressions still show up, please consult with the performance team (@eeckstein). Hardware Overview
|
Huh. If we really were avoiding double-hashing, I'd have expected more signal from the benchmarks. Surely we must be doing lots of dictionary subscript assigns in them. |
We have benchmarks setting values, but simple subscript assigns ( |
stdlib/public/core/Dictionary.swift
Outdated
// up inserting it. Otherwise this can only be a removal or an in-place | ||
// mutation. (If we guess wrong, we might needlessly rehash; that's fine.) | ||
let (_, rehashed) = _variant.ensureUniqueNative( | ||
withCapacity: self.capacity + (found ? 0 : 1), |
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.
Good thing benchmarks don’t exercise this yet — this should use self.count, not capacity. (As it stands, this causes a full 2x storage reallocation & rehash on every insertion.) 😳
I guess the trouble is that nearly all the useful in-place mutations happen via the |
You should be able to use basically the same logic for |
It can switch to using an unsafe addressor, though. |
@swift-ci please test |
Build failed |
Build failed |
@swift-ci please smoke test |
@swift-ci please smoke benchmark |
Bout to merge this, just testing it one more time since it's been a couple of weeks |
@swift-ci smoke test and merge |
Build comment file:Performance: -O
How to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the regressions before you merge the PR. Noise: Sometimes the performance results (not code size!) contain false alarms. Unexpected regressions which are marked with '(?)' are probably noise. If you see regressions which you cannot explain you can try to run the benchmarks again. If regressions still show up, please consult with the performance team (@eeckstein). Hardware Overview
|
This enables in-place mutations of the key-based subscript.
These get rid of one of two full hash table lookup operations (including hashing the key) relative to the getter+setter approach. They can also eliminate COW copies in code like this:
(Admittedly this isn’t a very useful example.)