-
Notifications
You must be signed in to change notification settings - Fork 18k
runtime: reduce duplicate work by merging map operations #70837
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
Comments
The paper Representing Data Collections in an SSA Form describes adding high-level collection operations to compiler SSA to aid in optimizations, which I find a fascinating direction. |
I would be very happy if this were implemented! Previously, see #5147, and in particular, #5147 (comment) which describes how this would speed up declaring flags. |
It does not have to be https://ziglang.org/documentation/master/std/#std.hash_map.HashMap.getOrPut
It returns a struct https://ziglang.org/documentation/master/std/#std.hash_map.HashMapUnmanaged.GetOrPutResult In Go land it would look something like: type GetOrPutResult[K comparable, V any] struct {
Key *K
Val *V
Found bool
}
func GetOrPut[K comparable, V any](m map[K]V, key K) GetOrPutResult[K, V] {
return GetOrPutResult[K, V]{ /*....*/ }
}
func main() {
m := make(map[string]string)
r := GetOrPut(m, "somekey")
if !r.Found {
*r.Val = "val"
}
} With This works because while scanning for a given key in a swisstable, we also know the place where we would have inserted the element with the same key (at least in Zig, not sure whether this would be possible in Go), thus we can return a pointer to it and there is no need to do that work again in |
Related: #5147 |
FYI I have a WIP (seems to work) implementation of GetOrPut operation in CL 662995 currently only for faststr and fast64. Shows ~15% speedup for get or put operations (when key does not exist in the map). v, ok := m[key]
if !ok {
m[key] = value
} With a 4.5% slowdown of only get operations (key is already inserted). |
Hi @mateusz834, could you expand slightly on what you mean by that? (I would have thought an optimization like this could be implemented without slowing down a normal get operation, but maybe you mean "GetOrPut" is only ~4.5% slower than a normal get, but that a normal get is unaffected by this change?) I have a couple of comments on the implementation, but I will comment on the CL. |
In cases where the
Yes. |
Change https://go.dev/cl/664918 mentions this issue: |
This is a
mapaccess2
followed (conditionally) by amapassign
. The beginning ofmapassign
is identical tomapaccess2
: hashing the key and looking for an existing entry (which won't be found).In theory, the compiler could detect this pattern and use a special call for the map assignment that avoids the duplicate work (this would effectively be
internal/runtime/maps.(*table).uncheckedPutSlot
in today's implementation).Another pattern is iteration plus delete:
It could be optimized in a similar way. This is also
maps.DeleteFunc
which could be directly specialized more easily than adding new compiler optimizations.The text was updated successfully, but these errors were encountered: