Does anyone have any thoughts on whether it would be feasible/desirable to 
be able to report allocs/op as a decimal in benchmarks? There must be many 
small functions out there which allocate at uneven, sub b.N, rates. 
Currently they are hidden/hard to reason about.

On Sunday, 4 September 2016 21:31:04 UTC+2, Francis wrote:
>
> 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> 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.
>>> 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