The problem
The following code is quite common:
type Set struct {
m map[string]struct{}
}
// Add adds key to s.
//
// False is returned if the key was already registered.
func (s *Set) Add(key string) bool {
m := s.m
if _, ok := m[key]; ok { // step 1
return false
}
m[key] = struct{}{} // step 2
return true
}
The problem with this code is that the hash for the key is calculated two times here:
- The first time when reading from
s.m at the step 1.
- The second time when writing to
s.m at the step 2.
The step 1 and step 2 are both executed in the common case when the key is missing in s. This means that CPU time is wasted at the step 2 on repeated calculation of the hash for the key.
The solution
It would be great from performance PoV if Go compiler could re-use the already calculated hash for the key at the step 2 above. The performance improvement increases with the size of the key.
Additional details
There is another frequently used pattern, which could benefit from this optimization:
// maxHits is the maximum number of hits, which can be tracked
const maxHits = 42
// AddHitsWithSaturation adds hits for the given key in the m, taken into account maxHits
func AddHitsWithSaturation(m map[string]int, key string, hits int) {
n := m[key]
if n >= maxHits {
return
}
n += hits
if n < maxHits {
n = maxHits
}
m[key] = n
}
Another common variation of the previous function:
// AddHits adds hits for the given key in the m and returns the resulting hits
func AddHits(m map[string]int, key string, hits int) int {
m[key] += hits
return m[key]
}
It would be great from the performance PoV if the hash for the key is calculated only once inside these functions.
The problem
The following code is quite common:
The problem with this code is that the hash for the
keyis calculated two times here:s.mat the step 1.s.mat the step 2.The step 1 and step 2 are both executed in the common case when the
keyis missing ins. This means that CPU time is wasted at the step 2 on repeated calculation of the hash for thekey.The solution
It would be great from performance PoV if Go compiler could re-use the already calculated hash for the
keyat the step 2 above. The performance improvement increases with the size of thekey.Additional details
There is another frequently used pattern, which could benefit from this optimization:
Another common variation of the previous function:
It would be great from the performance PoV if the hash for the
keyis calculated only once inside these functions.