Thanks Peter, Your observation cracked the mystery for me. There were two things that were causing me confusion.
1: That we were not observing any allocations at all, even though the map/s must be allocating. 2: That one of these tests would allocate anything at all, while the other allocates nothing. However, given that a hashmap won't necessarily allocate on every set operation some function calls may allocate while others will not. Clearly a function setting in two hashmaps will allocate twice as often as a function setting in one. If the allocs/op is then rounded, or simply stored as an integer, then you have to allocate at a certain rate to get above 0 allocs/op. We can test this by doubling the number of elements we add inside to the setOne(...) benchmark. func BenchmarkSetOne(b *testing.B) { keyVals := make([]string, b.N*20) for i := range keyVals { keyVals[i] = strconv.Itoa(i) } s := &server{ first: make(map[string]string), second: make(map[string]string), } b.ResetTimer() b.ReportAllocs() for _, key := range keyVals { setOne(s, key) } } Gives us BenchmarkSetOne-4 200000 14677 ns/op 3192 B/op 1 allocs/op we can push it further by (naughtily) setting keyVals := make([]string, b.N*200) to get BenchmarkSetOne-4 10000 159321 ns/op 31921 B/op 11 allocs/op Your point about observing the 'once per b.N' rule is duly noted :) On Friday, 2 September 2016 22:51:01 UTC+2, peterGo wrote: > > Francis, > > And, of course, > > keyVals := make([]string, b.N*10) > // ... > for _, key := range keyVals { > setTwo(s, key) > } > > should be > > keyVals := make([]string, b.N) > // ... > for _, key := range keyVals { > setTwo(s, key) > } > > or > > s /b.N*10/b.N/g > > Peter > > On Fri, Sep 2, 2016 at 4:38 PM, peterGo <go.pe...@gmail.com <javascript:>> > wrote: > >> Francis, >> >> First, fix any bugs. >> >> For example, "The benchmark function must run the target code b.N times." >> https://golang.org/pkg/testing/ >> >> Therefore, >> >> keyVals := make([]string, b.N*10) >> // ... >> for _, key := range keyVals { >> setOne(s, key) >> } >> >> should be >> >> keyVals := make([]string, b.N) >> // ... >> for _, key := range keyVals { >> setOne(s, key) >> } >> >> or >> >> s /b.N*10/b.N/ >> >> Then, as expected, >> >> BenchmarkSetOne-4 1000000 2058 ns/op 159 B/op >> 0 allocs/op >> BenchmarkSetTwo-4 1000000 4648 ns/op 319 B/op >> 0 allocs/op >> >> Peter >> >> On Friday, September 2, 2016 at 9:07:09 AM UTC-4, Francis wrote: >>> >>> I have been working to reduce allocations in a local cache and found >>> some confusing behaviour around the allocs/op output when benchmarking. >>> >>> A simplified reproducing version is pasted at bottom. >>> >>> The behaviour is that setting a value in a single map yields 0 >>> allocations, setting a value in two maps inside the same function yields 1 >>> allocation. Increasing the number of times the Set*() method is called in >>> the benchmark (below I am using b.N*10) doesn't change the number of >>> allocations. >>> >>> Trying to track down this I have used GOSSAFUNC to inspect both >>> setOne(...) and setTwo(...). I couldn't identify any allocations, although >>> I am not confident in my reading of the SSA output. >>> >>> I also ran 'go test -bench=.* -memprofile mem.out' which showed 0 >>> allocations. >>> >>> So I am confused and wanted to ask for clarification on these >>> measurements. It is interesting to me that the setTwo(...) function >>> allocates where the setOne(...) does not, but also that the allocations to >>> the underlying maps don't appear to be recorded by any of the tools I have >>> used here. >>> >>> >>> package test >>> >>> >>> type server struct { >>> first map[string]string >>> second map[string]string >>> } >>> >>> >>> func new() *server { >>> return &server{ >>> first: make(map[string]string), >>> second: make(map[string]string), >>> } >>> } >>> >>> >>> func setOne(s *server, key string) { >>> s.first[key] = key >>> } >>> >>> >>> func setTwo(s *server, key string) { >>> s.first[key] = key >>> s.second[key] = key >>> } >>> >>> with corresponding benchmark >>> >>> >>> >>> package test >>> >>> >>> import ( >>> "strconv" >>> "testing" >>> ) >>> >>> >>> func BenchmarkSetOne(b *testing.B) { >>> keyVals := make([]string, b.N*10) >>> for i := range keyVals { >>> keyVals[i] = strconv.Itoa(i) >>> } >>> s := &server{ >>> first: make(map[string]string), >>> second: make(map[string]string), >>> } >>> b.ResetTimer() >>> b.ReportAllocs() >>> for _, key := range keyVals { >>> setOne(s, key) >>> } >>> } >>> >>> >>> func BenchmarkSetTwo(b *testing.B) { >>> keyVals := make([]string, b.N*10) >>> for i := range keyVals { >>> keyVals[i] = strconv.Itoa(i) >>> } >>> s := &server{ >>> first: make(map[string]string), >>> second: make(map[string]string), >>> } >>> b.ResetTimer() >>> b.ReportAllocs() >>> for _, key := range keyVals { >>> setTwo(s, key) >>> } >>> } >>> >>> -- >> You received this message because you are subscribed to a topic in the >> Google Groups "golang-nuts" group. >> To unsubscribe from this topic, visit >> https://groups.google.com/d/topic/golang-nuts/dasvpqes7EU/unsubscribe. >> To unsubscribe from this group and all its topics, send an email to >> golang-nuts...@googlegroups.com <javascript:>. >> For more options, visit https://groups.google.com/d/optout. >> > > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.