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.

Reply via email to